Как Git решить проблему слияния?

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

Достигает ли это Git и как?

(отказ от ответственности: все, что я знаю о Git, основано на лекции Линуса - всего Git noob здесь)

Ответ 1

Git не будет препятствовать конфликту в слияниях, но может примирить историю, даже если они не разделяют какого-либо родительского предка.
(через Файл трансплантатов (.git/info/grafts), который представляет собой список, по одному в строке, фиксации, за которым следуют его родители, которые вы можете изменить для этой цели "примирения".)
Там довольно мощный.

Но чтобы действительно взглянуть на "как просочились слияния", вы можете начать с обращения к самому Линусу и реализовать это Проблема не столько в "алгоритме":

Линус: Я лично, я хочу иметь что-то очень повторяющееся и не-умное. Что-то я понимаю или говорю, что он не может этого сделать.
И, честно говоря, слияние истории с одним файлом, не учитывая историю всех других файлов, заставляет меня пойти "тьфу".

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

Другими словами, важная часть - это тривиальная часть: называние родителей и отслеживание их отношений. Не столкновения.

И похоже, что 99% людей SCM, похоже, думают, что решение этого должно быть более умным в отношении контента. Который полностью упускает точку.


Итак, Винсент Колайута добавляет (внимание мое):

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

Какие файлы были переименованы? Какие из них были скопированы? Какие из них были удалены? Какие строки были добавлены? Какие из них были удалены? Какие строки были сделаны внутри? Какие плиты текста были скопированы из одного файла в другой?
Вам не нужно заботиться ни о одном из этих вопросов, и вам, безусловно, не нужно сохранять специальные данные отслеживания, чтобы помочь вам ответить: все изменения в дереве (добавления, удаления, переименования, изменения и т.д.). ) неявно закодированы в дельте между двумя состояниями дерева; вы просто отслеживаете, что такое контент.

Абсолютно все может (и должно) быть выведено.

Git ломает форму, потому что она думает о содержимом, а не о файлах.
Он не отслеживает переименования, отслеживает контент. И он делает это на уровне целых деревьев.
Это радикальный отход от большинства систем контроля версий.
Это не мешает пытаться хранить истории в каждом файле; он вместо этого сохраняет историю на уровне дерева.
Когда вы выполняете diff, вы сравниваете два дерева, а не два файла.

Другое принципиально умное решение - это то, как сливается Git.
Алгоритмы слияния являются умными, но они не пытаются быть слишком умными. Недвусмысленные решения принимаются автоматически, но когда вы сомневаетесь в этом, пользователь должен решить.
Так оно и должно быть. Вы не хотите, чтобы машина принимала эти решения за вас. Вы никогда этого не захотите. Это фундаментальное понимание подхода Git к объединению: в то время как каждая другая система управления версиями пытается стать более умной, Git счастливо описывается как "глупый менеджер контента", и это лучше для него.

Ответ 2

В настоящее время в целом согласовывается этот трехсторонний алгоритм слияния (возможно, с такими усовершенствованиями, как переименование и обработка более сложной истории), в котором учитывается версия текущей ветки ('ours'), версия на объединенной ветке ( "их" ), а версия общего предка объединенных ветвей ( "предок" ) (с практической точки зрения) - лучший способ разрешить слияния. В большинстве случаев и для большей части уровня дерева дерева слияния (какая версия файла требуется) достаточно; редко приходится иметь дело с конфликтами содержимого, а затем алгоритм diff3 достаточно хорош.

Чтобы использовать трехстороннее слияние, вам нужно знать общий предок объединенных ветвей (co-base). Для этого вам необходимо знать полную историю между этими ветвями. То, что Subversion до (текущей) версии 1.5 отсутствовало (без сторонних инструментов, таких как SVK или svnmerge), было отслеживание слияния, т.е. Помня о том, чтобы слияние зафиксировало, какие родители (что коммиты) использовались в слиянии. Без этой информации невозможно правильно подсчитать общего предка при наличии повторных слияний.

Обратите внимание на следующую диаграмму:

---.---a---.---b---d---.---1
        \        /
         \-.---c/------.---2

(который, вероятно, будет искажен... было бы неплохо иметь возможность рисовать диаграммы ASCII-art здесь).
Когда мы сливали коммиты 'b' и 'c' (создание commit 'd'), общим предком была точка ветвления, commit 'a'. Но когда мы хотим объединить коммиты '1' и '2', теперь общим предком является commit 'c'. Не сохраняя информацию о слиянии, мы должны были бы ошибочно заключить, что это commit 'a'.

Subversion (до версии 1.5) и более ранние CVS сделали сложным слияние, потому что вам приходилось самостоятельно вычислять общего предка и давать информацию о предке вручную при выполнении слияния.

Git хранит информацию обо всех родителях фиксации (более одного родителя в случае слияния) в объекте commit. Таким образом, вы можете сказать, что Git хранит DAG (прямой ациклический график) ревизий, сохраняя и запоминая отношения между коммитами.


(Я не уверен, как Subversion занимается проблемами, упомянутыми ниже)

Кроме того, слияние в Git может иметь дело с двумя дополнительными проблемами сложности: переименование файлов (когда одна сторона переименовала файл, а другая - нет, мы хотим переименовать, и мы хотим получить изменения, применяемые к правильному файлу) и criss-cross merges (более сложная история, когда есть более одного общего предка).

  • Переименования файлов во время слияния управляются с использованием оценки эвристического подобия (учитываются как сходство содержимого файла, так и сходство имени пути) определение переименования. Git определяет, какие файлы соответствуют друг другу в объединенных ветвях (и предках). На практике это работает очень хорошо для реальных случаев.
  • Criss-cross объединяется, см. определение в revctrl.org wiki (и наличие multiple слияния баз) управляются с помощью стратегии рекурсивного слияния, которая генерирует один виртуальный общий предок.

Ответ 3

Ответы выше все правильные, но я думаю, что они пропустят центральную точку git легко сливаются для меня. Слияние SVN требует, чтобы вы отслеживали и запоминали, что было объединено, и что огромный PITA. Из своих документов:

svn merge -r 23:30 file:///tmp/repos/trunk/vendors

Теперь, когда это не убийца, но если вы забудете, будет ли он 23-30 включительно или 23-30 эксклюзивным, или вы уже объединили некоторые из этих коммитов, вы были взволнованы, и вам нужно выяснить, ответы, чтобы избежать повторения или отсутствия коммитов. Бог поможет вам, если вы разветките ветку.

С git он просто объединяется git, и все это происходит без проблем, даже если вы выбрали вишневую пару или совершили какое-либо количество фантастических git -land вещей.

Ответ 4

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

Ответ 5

Git просто затрудняет испортить все остальное хранилище с плохим слиянием.

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

SVN, правильно используемый, отлично подходит для использования.