В большинстве VCS используется базовая концепция трехстороннего слияния. Это сравнивает две ветви с общим предком каждой, поэтому, если строка кода отличается между двумя ветвями, вы знаете, какая ветвь изменила ее. Если они оба изменили его, у вас возникнет конфликт слияния, который должен разрешить человек.
Есть несколько случаев, когда трудно определить подходящего общего предка. Многие исследования были посвящены различным алгоритмам для этого, многие из которых включали отслеживание дополнительных метаданных с коммитами.
Важным нововведением Линуса было отслеживание деревьев , а не файлов . Это своего рода тонкое различие. Для иллюстрации на примере из блога Винсента рассмотрим файл foo
в ветке A
. Вы ветвитесь, чтобы сделать ветку B
. В ветке A
foo
переименовывается в bar
. В ветке B
он удаляется. Затем вы пытаетесь объединиться.
Если вы отслеживаете файлы, это выглядит так:
Перед переходом создается версия 1 файла foo
.
После следующего коммита ветвь A
указывает на версию 2 foo
, которая является удаленным файлом, и версию 1 нового файла bar
.
После следующей фиксации ветвь B
указывает на версию 2.1 foo
, которая является удаленным файлом.
При слиянии версии 2 и 2.1 из foo
сравниваются и обнаруживаются как идентичные. Там нет конфликта слияния там. Ветвь B
даже не имеет файла с именем bar
, поэтому в этом нет никакого конфликта. В результате вы получаете алгоритм слияния, который молча принимает переименование ветви A
, хотя между удалением foo
и его переименованием существовал реальный конфликт.
Если вы отслеживаете деревья, это выглядит так:
Перед ветвлением создается блоб с хешем dcb8bd7a97ab39f4c156a1a96d4b10720a39fb81. Дерево создается с записью, содержащей метку foo
, указывающую на хеш.
После следующего коммита ветвь A
указывает на дерево с записью, содержащей метку bar
, указывающую на тот же хеш.
После следующего коммита ветвь B
указывает на пустое дерево.
При слиянии деревья сравниваются: B
показывает удаление, а A
показывает переименование BLOB-объекта dcb8bd7a97ab39f4c156a1a96d4b10720a39fb81. Человека спрашивают, кого он предпочитает.
Вы можете несколько смягчить эффект с помощью VCS с отслеживанием файлов, добавив метаданные для переименований, но способ git использует свою обычную стандартную структуру данных. Кроме того, способ метаданных имеет трудности со сложными объединениями, когда существует много возможных вариантов для общего предка. Вы можете поместить миллиард возможных путей между общим предком и двумя ветвями, и git все равно увидит большой двоичный объект с таким же хешем и сможет обнаружить переименование и удаление. Также сложно сохранить метаданные, например, при принятии изменений в патче по электронной почте.
С переименованным файлом, который меняется одновременно, становится немного сложнее, но, отслеживая деревья, git получает всю необходимую информацию. Он видит BLOB-объект dcb8bd7a97ab39f4c156a1a96d4b10720a39fb81, ушедший из обеих веток, но также видит новую запись дерева, указывающую на новый BLOB-объект, и может сравнить их. Если значительная часть файла совпадает, это считается переименованием. Очевидно, это сломается, если вы внесете массу изменений в переименованный файл, но в какой-то момент никакой алгоритм слияния не сможет вам помочь.
См. это электронное письмо от Линуса, чтобы узнать больше о его философии на эту тему.