Можно ли исключить определенные коммиты при выполнении слияния git?

Скажем, что я хочу объединиться из ветки релиза в главную ветку, и в ветке релиза есть некоторые коммиты, которые я не хочу включать в главную ветвь. Есть ли способ выполнить слияние, чтобы одно или несколько из этих коммитов не были объединены?

Моя стратегия состоит в том, чтобы сделать следующее (в главном):

git merge --no-commit release-branch
# Resolve conflicts and apply reverse patch of the commits that I don't want included
git commit # Edit commit message so that it lists the commits that have been reverse-patched

Есть ли лучший способ сделать это?

Ответ 1

Создайте новую ветвь, перетащите ветвь в интерактивном режиме и отбросьте, что вы не хотите, а затем слейте ее.

Вы не можете принимать изменения из середины ветки без повторной перемотки, но правильная вещь случится, когда она увидит те же изменения в более позднем слиянии (например, из набора вишни и чего нет).

Ответ 2

Я нашел решение, которое работает для меня в книге Pro Git.

Предположим, вы хотите исключить файл config.php.

На ветке A:

  • Создайте файл с именем .gitattributes в том же каталоге, с этой строкой: config.php merge=ours. Это сообщает Git, какую стратегию использовать при слиянии файла. В этом случае он всегда сохраняет вашу версию, т.е. версию на ветке, в которую вы сливаетесь.

  • Добавить файл .gitattributes и зафиксировать

На ветке B: повторите шаги 1-2

Попробуйте слить сейчас. Ваш файл должен быть оставлен нетронутым.

Ответ 3

Если у вас есть ветка поддержки, где вы исправляете ошибки и создаете новые версии. На мастере у вас есть следующая версия, в которой вы также часто создаете новые версии.

Каждый раз, когда вы создаете новую версию, вы меняете версию в каком-то файле, фиксируете этот новый файл, создаете тег и нажимаете. Теперь слияние с поддержкой на master всегда будет иметь конфликты в файле, содержащем информацию о версии.

Если файл, содержащий информацию о версии только, содержит информацию о версии, вы можете пойти с ответом fcurella. Но если он действительно может содержать также объединенную информацию (pom.xml, gradle.properties, MANIFEST.MF,...), вы должны выполнить некоторые дополнительные действия.

Давайте рассмотрим следующий пример

      C---D*---E---F* support
     /
A---B---G---H*---I master

где фиксации со звездами содержат только изменения из-за изменений версии, которые следует игнорировать при слиянии.

Чтобы объединить поддержку в master без конфликтов слияния из-за версий, вы можете выполнить одно из следующих действий:

Множественное слияние совершает

git checkout master
git merge C
git merge D -s ours
git merge E
git merge F -s ours

С аргументом -s ours мы говорим git только для записи слияния без изменения рабочего пространства. Это сопоставимо с опцией --record-only svn.

Вышеупомянутое приведет к следующему макету

      -------------C---D*---E---F* support
     /              \   \    \   \
A---B---G---H*---I---J---K----L---M master

Выполнение одного слияния с использованием вишневого выбора

git checkout master
git merge support -s ours --no-commit
git cherry-pick C E --no-commit
git commit -m 'merged support into master'

сначала мы начинаем слияние, но записываем только то, что мы объединяем, не изменяя рабочее пространство и не совершаем слияние. Затем мы собираем вишни, чтобы слить, опять же без фиксации. Наконец, мы совершаем слияние.

Вышеупомянутое приведет к следующему макету

      C---D*---E---F* support
     /              \
A---B---G---H*---I---J master

Можно даже автоматизировать сбор вишни.

git checkout master
git merge support -s ours --no-commit
for id in `git log support --reverse --not HEAD --format="%H [%an] %s" |
  grep -v "bump version" |
  sed "s/\(\w*\)\s.*/\1/g"`
do
  git cherry-pick --no-commit $id
done
git commit -m 'merged support into master'

Ответ 4

Причина, по которой это не может быть сделано напрямую, заключается в том, что каждая фиксация содержит ссылки на родительские коммиты (обычно один, но несколько для слияний). Таким образом, если у вас есть одна фиксация (по сумме SHA1), вся история также фиксируется, поскольку родители также содержат ссылки на своих родителей и так далее. Таким образом, единственный способ уйти от патчей в истории - написать новый. git rebase -i на вновь созданной ветке, вероятно, является самым простым способом достижения этого.

Ответ 5

Также возможно изменить файл .git/info/attributes и сохранить его в папке .git вместо добавления файлов .gitattribute по всему, что в конечном итоге потребует добавления их в исходный код.

Ответ 6

Основной вопрос: как вы хотите представить коммиты, которые хотите пропустить?

  • скрытно скрывать их (не мой любимый)
  • явно пропустить их
  • явно отменить их

К сожалению нет. 2 невозможно выразить на графике истории.

Нет. 1 возможно, но я бы никогда этого не делал: фиксация слияния может содержать изменения. - Обычно фиксация слияния указывает на результат слияния еще двух ветвей: все вещи, разработанные в этих ветвях, должны быть в коде после слияния (т.е. В коде, на который указывает слияние слиянием). И больше ничего не должно быть в этой фиксации.

Но удивите, удивите, вы можете изменить всю базу кода и представить ее как слияние. Эффект слияния двояка: он объединяет дерево истории и должен объединять две базы кода. Первое, что он делает точно (в противном случае никто не называет это слиянием), поскольку последний может потерпеть неудачу, например. когда возник конфликт слияния, и он был разрешен неправильно (тогда базы кода не были объединены должным образом).

Некоторые из других ответов говорят об этом. Я рекомендую явный способ: merge plus revert commits.

Ответ 7

Если вы хотите исключить некоторые коммиты, которые находятся в конце, вы можете просто зафиксировать определенный номер фиксации:

git checkout partlyMergedFrom
git whatchanged
--> find the commit hash up to where you want to merge
git checkout partlyMergedInto
git merge e40a0e384f58409fe3c864c655a8d252b6422bfc
git whatchanged
--> check that you really got all the changes you want to have