Конфликты слияния происходят во время слияний, а не после их.
Конфликтная часть действительно очень проста: Конфликт возникает в файле F когда:
- «наши» изменения - отличие от базового коммита слияния с нашим
HEAD
коммитом - имеет изменение в файле F , и - «их» изменения - отличие от того же самого базового коммита слияния на их коммитный коммит - также имеют изменения в файле F , а также
- наших изменений и их изменения частично совпадают, но не идентичны.
Чтобы понять это, вам необходимо:
- понять вывод
git diff
; и - понять, что такое база слияния .
Вывод git diff
довольно прост, на самом деле, но для этого нужно помнить, что каждый коммит содержит снимок из всех ваших файлов. Это означает, что мы должны дать git diff
два снимка: старый и новый. Это две "картинки" того, на что был похож файл в два момента времени. Git затем играет в игру Найди отличия : он говорит вам, что до go от снимка с левой стороны до снимка с правой стороны, вы должны внести некоторые изменения в некоторый набор файлы. Эти изменения могут включать переименование некоторых файлов; они могут включать добавление новых файлов; они могут включать удаление файлов; и они могут включать удаление определенных строк из некоторых файлов и добавление некоторых строк в некоторые файлы, в определенных местах.
Вывод git diff
не обязательно является чем-то, что person делал , Это просто набор изменений, которые, если применить их к левому снимку, дают вам правый снимок. «Левая сторона» здесь - это левый аргумент git diff
, а «правая сторона» - это правый аргумент, когда вы используете:
git diff <hash1> <hash2>
, где два хэша являются идентификаторами ha sh коммитов. (Это то, что git merge
делает, хотя и делает все это внутренне.) Механизм сравнения предназначен для создания наименьшего набора изменений, которые дают правильный эффект. Это, как выясняется, обычно то, что кто-то на самом деле делал делает ... но не всегда; следовательно, обычно верно, но не всегда.
Последней, но, вероятно, самой сложной частью понимания git merge
является концепция базы слияния . Технически база слияния - это (единичный) коммит, который получается из алгоритма, который находит наименьшего общего предка (LCA) узлов, выбранных из Направленного ациклического графика c (DAG). Не все пары (или наборы) узлов DAG имеют LCA: у некоторых нет ни одного, а у некоторых более одного. Тем не менее, для графика фиксации Git достаточно иметь здесь один LCA, а git merge
имеет несколько методов для работы с несколькими LCA. (Когда существует нет LCA, современный git merge
отказывается запускаться по умолчанию, сообщая вам, что две ветви имеют несвязанные истории. Старый Git все равно выполнил слияние, и вы можете сделать современный Git в любом случае выполните слияние, в этом случае Git использует синтетический c коммит без файлов в качестве базы слияния.)
Важной частью здесь является концептуальное "чувство" для база слияния. Для некоторых графиков это легко. Рассмотрим, например, случай Git графа коммитов, где ваши две ветви просто разветвляются от коммитов общего предка, чей идентификатор ha sh равен H
:
I--J <-- branch1 (HEAD)
/
...--G--H
\
K--L <-- branch2
Здесь, при объединении branch1
и branch2
- что означает фиксацию J
и L
- общая отправная точка - это фиксация H
. Таким образом, git merge
будет выполнять две команды git diff
, а база слияния в каждая будет H
:
git diff --find-renames <hash-of-H> <hash-of-J> # what we changed on branch1
git diff --find-renames <hash-of-H> <hash-of-L> # what they changed on branch2
Git теперь будет объединять набор изменений, произведенных этими двумя git diff
командами. Там, где они перекрываются, но не вносите одно и то же изменение , вы получаете конфликты слияния.
Git применит объединенные изменения к снимку в H
. Применение вашего изменения к этому снимку приводит к фиксации J
; применение их изменений приводит к коммиту L
; применение комбинированных изменений приводит, в общем, к комбинации.
Если нет конфликтов, Git сможет объединить изменения самостоятельно. Применив объединенные изменения, Git зафиксирует результат самостоятельно, как новый коммит слияния M
:
I--J
/ \
...--G--H M <-- branch1 (HEAD)
\ /
K--L <-- branch2
, и это будет ваш результат слияния.
Если объединение не удается, Git останавливается в середине слияния. Теперь ваша задача - fini sh объединить (объединить изменения самостоятельно), затем сказать Git, что вы сделали это, и выполнить коммит слияния. Если это слишком большой беспорядок, вы можете сказать Git: прервать слияние целиком , и он откажется от всех своих попыток объединить вещи и оставит вас на коммите J
, как будто вы даже никогда не запустим git merge
.
Последний хитрый момент заключается в следующем: когда вы делаете завершаете sh слияние - автоматически через Git или вручную - результирующие записи фиксации слияния два родителя. То есть, если вы посмотрите на слияние M
выше, вы увидите, что оно подключается обратно к обоим коммитам J
и L
. Во многих слияниях мы рисовали бы это немного по-другому:
o--o <-- small-feature
/ \
...--o--B--o--D--o---o--o <-- mainline
\
o--o--o--o--o--o <-- big-feature
Здесь небольшая функция была объединена с основной линией, а большая функция все еще выполняется. Основой слияния небольшой функции был коммит D
. Основой слияния большой функции будет коммит B
. (Остальные коммиты не очень интересны.) Однако в некоторых случаях мы получаем более запутанный граф:
o--o---o <-- offshoot-feature
/ / \
o--o---o---o--o <-- medium-feature
/ \ /
...--o--o--o--o--o---o----o <-- mainline
Этот граф не так уж и сложен, но сейчас это действительно сложно , чтобы увидеть , где базы слияния, из-за всего слияния различных функций в основную линию и друг с другом.
Git найдет слияние основы. Вы можете найти базы слияния самостоятельно, используя git merge-base --all
. Вы можете нарисовать график или использовать Git, используя git log --graph
, и попытаться найти базы слияния с помощью глазного яблока. Найдя базы слияния, как бы вы ни делали, вы можете запустить две команды git diff
, которые будет запускать git merge
. Это скажет вам, где ваши конфликты будут . Но обычно нет никакого смысла: просто запустите git merge
и найдите конфликты.