Как мерзавец сливается после вишневого выбора?

190

Давайте представим, что у нас есть masterфилиал.

Затем мы создаем newbranch

git checkout -b newbranch

и сделать два новых коммита newbranch: commit1 и commit2

Затем мы переключаемся на мастера и делаем cherry-pick

git checkout master
git cherry-pick hash_of_commit1

Посмотрев, gitkмы видим, что commit1 и его выбранная версия имеют разные хэши, поэтому технически это два разных коммита.

Наконец мы сливаемся newbranchв master:

git merge newbranch

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

Действительно ли git проводит интеллектуальный анализ содержимого коммита при слиянии и решает, что изменения не должны применяться дважды, или эти коммиты помечаются внутри как связанные вместе?

Павел
источник

Ответы:

131

Короткий ответ

Не волнуйтесь, Git справится с этим.

Длинный ответ

В отличие, например, от SVN 1 , Git не хранит коммиты в дельта-формате, но основан на снимках 2,3 . В то время как SVN наивно пытался применить каждый объединенный коммит как патч (и потерпел неудачу, по той причине, что вы описали), Git обычно способен справиться с этим сценарием.

При объединении Git попытается объединить снимки обоих коммитов HEAD в новый снимок. Если часть кода или файла идентична на обоих снимках (т. Е. Потому что коммит уже был выбран), Git его не трогает.

источники

1 Skip-Deltas в Subversion
2 Git Basics
3 Объектная модель Git

helmbert
источник
40
На самом деле, я бы сказал, что вам следует беспокоиться о слиянии, а «мерзавец справится» - не очень хорошее правило.
Cregox
4
Фактически, слияние МОЖЕТ привести к дублированию контента в некоторых случаях. Git иногда справляется, но иногда нет.
Donquixote
Это ужасно неправильно. Получил в значительной степени хранит файлы файлов во всех пригодных для использования формах. И если я правильно помню, SVN использовал для хранения снимков.
he_the_great
2
@he_the_great, нет. Формат хранения SVN с пропуском-дельта (! = Снимки) хорошо документирован в руководстве . И я действительно не понимаю , что вы имеете в виду condevable . Я не носитель языка, но я уверен, что это не настоящее слово.
helmbert
2
@he_the_great Но даже когда packfiles любой данный хеш для файла, в результате получается полный файл. Да, он сжимает с использованием дельт, но это не дельта для изменений в коммите, а вместо дельты между хешами для файла. Что касается объекта commit, то он ссылается на дерево, которое ссылается на хеш для полного файла. То, что сжатые данные скрыты, не влияет на работу git. Насколько я понимаю, Git хранит полные файлы для коммитов, а SVN хранит дельты для коммитов.
Питер Олсон,
44

После такого слияния у вас могут быть дважды выбранные в истории коммиты.

Решение, чтобы предотвратить это, я цитирую из статьи, которая рекомендует для веток с дублированием (вишня) выбирает rebase перед слиянием:

git merge после git cherry-pick: избегать дублирования коммитов

Представьте, что у нас есть основная ветка и ветка b:

   o---X   <-- master
    \
     b1---b2---b3---b4   <-- b

Теперь нам срочно нужны коммиты b1 и b3 в master, но не остальные коммиты в b. Итак, что мы делаем, это проверяем основную ветвь и коммиты cherry-pick b1 и b3:

$ git checkout master
$ git cherry-pick "b1's SHA"
$ git cherry-pick "b3's SHA"

Результат будет:

   o---X---b1'---b3'   <-- master
    \
     b1---b2---b3---b4   <-- b

Допустим, мы делаем еще один коммит на master и получаем:

   o---X---b1'---b3'---Y   <-- master
    \
     b1---b2---b3---b4   <-- b

Если бы мы теперь слили ветку b в master:

$ git merge b

Мы получили бы следующее:

   o---X---b1'---b3'---Y--- M  <-- master
     \                     /
      b1----b2----b3----b4   <-- b

Это означает, что изменения, внесенные b1 и b3, появятся дважды в истории. Чтобы избежать этого, мы можем сделать ребаз вместо слияния:

$ git rebase master b

Что даст:

   o---X---b1'---b3'---Y   <-- master
                        \
                         b2'---b4'   <-- b

В заключение:

$ git checkout master
$ git merge b

дает нам:

   o---X---b1'---b3'---Y---b2'---b4'   <-- master, b

РЕДАКТИРОВАТЬ Исправления, предполагаемые комментарием Дэвида Лемон

ephemerr
источник
1
отличный намек на ребаз! он автоматически пропустит все выбранные вишни коммиты.
iTake
2
Честно говоря, это звучит слишком хорошо, чтобы быть правдой, я должен видеть это своими глазами. Также ребаз изменяет коммиты, ваш последний график должен быть---Y---b2'---b4'
Дэвид Лемон
Работает отлично. Очень полезно, если вы не хотите, чтобы вишневые коммиты дважды в истории.
user2350644
1
Если не отметить, что хотя rebase прекрасен, опасность его использования заключается в том, что любые ветки или ветки, созданные из старого b, будут не синхронизированы, и пользователям, возможно, придется прибегнуть к таким вещам, как git reset --hard и git пуш-ф?
JHH
1
@JHH, вот почему мы перебазируем местный филиал здесь
ephemerr