Можно ли исключить определенные коммиты при выполнении git merge?
Допустим, я хочу объединить ветку релиза с веткой master, и в ветке релиза есть некоторые коммиты, которые я не хочу включать в ветку master. Есть ли способ сделать слияние, чтобы один или несколько из этих коммитов не были объединены?
Моя стратегия до сих пор заключается в следующем (в мастер):
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
Есть лучший способ сделать это?
8 ответов
Создайте новую ветку, перебазируйте ветку в интерактивном режиме и удалите коммиты, которые вам не нужны, а затем объедините их.
Вы не можете вынести изменения из середины ветви без перефразирования, но правильная вещь произойдет, когда она увидит те же изменения в последующем слиянии (например, от выбора вишни и чего-то еще).
Я нашел решение, которое работает для меня в книге Pro Git.
Допустим, вы хотите исключить файл config.php
,
На ветке А:
Создайте файл с именем
.gitattributes
в том же каталоге, с этой строкой:config.php merge=ours
, Это говорит git, какую стратегию использовать при объединении файла. При этом всегда сохраняйте свою версию, т.е. версия ветки, в которую вы сливаетесь.Добавить
.gitattributes
файл и коммит
На ветке B: повторите шаги 1-2
Попробуйте объединить сейчас. Ваш файл должен быть оставлен нетронутым.
Если у вас есть ветка поддержки, где вы исправляете ошибки и создаете новые версии. На master у вас есть следующая версия, где вы также часто создаете новые версии.
Каждый раз, когда вы создаете новую версию, вы изменяете версию в каком-то файле, фиксируете этот новый файл, создаете тег и нажимаете. Теперь слияния от поддержки к основному всегда будут конфликтовать в файле, содержащем информацию о версии.
Если файл, содержащий информацию о версии, содержит только информацию о версии, вы можете пойти с ответом fcurella. Но если он действительно может также содержать объединяемую информацию (pom.xml, gradle.properties, MANIFEST.MF, ...), вы должны выполнить некоторые дополнительные действия.
Давайте использовать следующий пример
C---D*---E---F* support
/
A---B---G---H*---I 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
вариант свн.
Выше приведут к следующему макету
-------------C---D*---E---F* support
/ \ \ \ \
A---B---G---H*---I---J---K----L---M master
Один коммит слияния с использованием cherry-pick
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'
Причина, по которой это не может быть сделано напрямую, заключается в том, что каждый коммит содержит ссылки на родительские коммиты (обычно только один, но несколько для слияний). Таким образом, если у вас есть один коммит (по сумме SHA1), вся история также фиксируется, поскольку родители также содержат ссылки на своих родителей и так далее. Таким образом, единственный способ оставить патчи в истории - это написать новый. git rebase -i во вновь созданной ветви, вероятно, самый простой способ добиться этого.
Главный вопрос: как вы хотите представлять коммиты, которые вы хотите пропустить?
- хитро спрятать их (не мой любимый)
- явно пропустить их
- явно отменить их
К сожалению нет. 2 невозможно выразить в графе истории.
Номер 1 возможен, но я бы никогда этого не сделал: коммит слияния может содержать изменения. - Обычно коммит слияния указывает на результат слияния еще двух ветвей: все, что разрабатывается в этих ветках, должно быть в коде после слияния (т.е. в коде, указанном коммитом слияния). И больше ничего не должно быть в этом коммите.
Но удивительно, удивительно, вы можете изменить всю кодовую базу и представить ее как объединение. Эффект слияния имеет два аспекта: он объединяет дерево истории и должен объединять две базы кода. Первый он делает наверняка (в противном случае никто не называет это слиянием), для второго он может потерпеть неудачу, например, когда возник конфликт слияния и он был разрешен неправильно (тогда базы кода не были объединены должным образом).
Некоторые другие ответы предполагают это сокрытие. Я рекомендую явный способ: объединить плюс отменить коммиты.
Также возможно изменить файл.git/info/attribute и сохранить его в папке.git вместо того, чтобы добавлять файлы.gitattribute повсюду, что в конечном итоге потребует их добавления в систему контроля версий.
Если вы хотите исключить только некоторые коммиты, которые находятся в конце, вы можете просто зафиксировать конкретный номер коммита:
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
Чтобы избежать проблемы с перезаписью, возникшей с ответом , начните с определения драйвера слияния, который всегда будет поддерживать нашу текущую версию файла, используя существующую команду true. Мы назовем этот драйвер нашим, чтобы соответствовать аналогичным стратегиям слияния:
git config --global merge.ours.driver true
После этого мы можем реализовать решение @fcurella@fcurella :
Допустим, вы хотите исключить файл .env
На ветке A (например, Master):
Создайте файл с именем .gitattributes в том же каталоге с этой строкой:
.env merge=ours
Это сообщает git, какую стратегию использовать при слиянии файла. В этом случае он всегда сохраняет свою версию, т.е. версия ветки, в которую вы вливаетесь.
Добавьте файл .gitattributes и зафиксируйте
На ветке B (например, Develop): повторите шаги 1-2.
Попробуйте объединиться сейчас. Ваш файл должен остаться нетронутым.
Прочитайте это для дальнейших объяснений