Во-первых, давайте отметим, что такое «перекрёстное слияние». В Git имя ветви просто идентифицирует last (или tip ) коммит, который является частью этой ветви. Оставшиеся коммиты, являющиеся частью ветви, определяются по следованию родителя этого коммита, родителя его родителя (прародителя), родителя следующего шага назад (прапрадеда) и т. Д .:
... <-F <-G <-H <--branch
Здесь имя ветви branch
идентифицирует коммит, чей настоящий идентификатор ha sh является чем-то большим и уродливым, но мы просто назовем его H
. То есть Git считывает содержимое имени branch
:
$ git rev-parse branch
<some big ugly hash ID here>
Git использует этот идентификатор ha sh для чтения коммита H
из хранилища. Коммит H
говорит, что его родительский коммит - это еще один большой уродливый случайный идентификатор га sh, но мы просто назовем его G
, поэтому Git может использовать H
, чтобы найти га G
. sh ID. Git теперь может читать родителя G
- H
- из хранилища. Коммит G
хранит идентификатор F
ha sh, так что Git может читать F
и т. Д.
Когда у нас есть две ветви с одним лучшим общим предком, это имеет тенденцию выглядеть следующим образом:
I--J <-- branch1
/
...--G--H
\
K--L <-- branch2
Начиная с J
и работая в обратном направлении, Git перечисляет коммиты J
, затем I
, затем H
, затем G
и скоро. Начиная с L
и работая в обратном направлении, Git перечисляет L
, затем K
, затем H
, затем G
и т. Д.
Подтверждает H
и более ранние на обе ветви. Commit H
является последним таким коммитом, поэтому это base merge . Поэтому и -s resolve
, и -s recursive
выберут коммит H
в качестве базы слияния. (После слияния будет сделан новый коммит M
, чьи родители будут оба J
и L
.)
Но не все графики так хороши , В частности, мы можем иметь следующую серию коммитов:
...--G--H---K--L <-- branch1
\ /
x
/ \
...--I--J---M--N <-- branch2
Чтобы определить, какой коммит использовать в качестве базы слияния, Git начинается с L
и идет назад: L
, K
, H
-и-J
, G
-и-I
. То есть Git следует оба родителя слияния совершают K
. В то же время, Git начинается с N
и работает в обратном направлении: N
, M
, затем H
-and- J
и т. Д.
Ясно H
-и- J
лучше, чем G
-и- I
, но не существует простого способа разбить t ie между H
и J
как «лучшего» общего предка. Вот где рекурсивность и разрешение отличаются.
Стратегия рекурсивный выбирает оба фиксируют в качестве базы слияния. Чтобы достичь этого, он вызывает внутренний, внутренний git merge
для коммитов H
и J
. Эта операция слияния находит базу слияния H
и J
и объединяет их для создания временного коммита, который находится прямо там, где x
. Эта новая, но временная фиксация теперь является базой слияния для внешнего слияния.
Стратегия resol - это та, о которой вы спрашиваете в строке темы. Он выбирает один из H
или J
, по-видимому, случайно. Какой из них вы получите, зависит от используемого алгоритма, который не указан и может меняться от одного Git выпуска к другому.
Какой алгоритм разрешает обработку перекрестных слияний?
Это стратегия -s recursive
, которая используется по умолчанию. Как отмечалось выше, он обрабатывает их путем объединения баз слияния.
Вы можете запустить git merge-base --all branch1 branch2
(с примером выше), и вы увидите два идентификатора ha sh двух баз слияния. В некоторых ситуациях (очень трудно нарисовать или, если на то пошло, достичь) может быть три или более баз слияния. (Здесь нет теоретического верхнего предела.) Также каждая пара слияния-базы может иметь несколько баз слияния. Рекурсивная стратегия обрабатывает все эти случаи путем многократного слияния баз слияний, пока в итоге не останется один лучший (но временный) коммит.
Когда рекурсивное слияние выполняет рекурсивное слияние, конфликты, возникающие во время внутренних слияний, незаметно передаются - вместе с маркерами конфликтов - в конечную базу слияния, используемую для слияния. Результат очень сбивает с толку .