Описание можно найти здесь (см. Также part 2 ):
Когда требуется рекурсивное слияние?
Что если мы найдем "двух общих предков"? Представление проводника веток ниже показывает альтернативу, в которой есть два возможных «общих предка».
Пожалуйста, обратите внимание: пример немного навязчив, поскольку изначально нет веской причины для слияния разработчика из набора изменений 11 в 16 вместо объединения из набора изменений 15 (последний из основного филиала в точке слияния ).
Но давайте предположим, что это нужно сделать по какой-то причине, скажем, набор изменений 11 был стабильным, а 13 и 15, например, не был в то время.
Суть в том, что между 15 и 16 нет ни одного уникального предка, а, скорее, два предка на одном и том же «расстоянии»: 12 и 11.
Хотя это случается не часто, на самом деле это может произойти с долгоживущими ветвями или сложными топологиями ветвей. (Показанный выше случай является самым коротким, приводящим к проблеме «множественных предков», но это может произойти и с несколькими наборами изменений и ветвями между «скрещенными» слияниями).
Одно из решений состоит в том, чтобы «выбрать» одного из предков в качестве допустимого для слияния (это вариант, который берет Mercurial), но у него есть много недостатков.
Как работает рекурсивное слияние?
Когда найдено более одного действительного предка, стратегия рекурсивного слияния создаст нового уникального «виртуального предка», объединяющего первоначально найденных.
На следующем рисунке изображен алгоритм:
Новый предок 2 будет использоваться как "предок" для объединения "src" и "dst".
«Рекурсивная стратегия слияния» способна найти лучшее решение, чем «выбор одного из двух», как я опишу ниже.
Примечание: рекурсивная стратегия слияния изначально была стратегией слияния "fredrik" (см. commit e4cf17c , сентябрь 2005, Git v0.99.7a) после Fredrik Kuivinen .
Это был скрипт Python , инициированный в commit 720d150 , и он иллюстрирует оригинальный алгоритм.
Для получения более подробной информации рассмотрите « Текущие концепции в системах контроля версий от Петра Баудиуса 2009-09-11 », стр. 17.
|B| = 1 : b(B) = B0
|B| = 2 : b(B) = M(LCA(B0, B1), B0, B1)
M(B, x, y) = ∆−1
(b(B), x ∪ y)
m(x, y) = M(LCA(x, y), x, y)
(Да, я тоже не знаю, как это читать)
В случае конфликта основная идея алгоритма состоит в том, чтобы просто оставить маркеры конфликта на месте при использовании результата в качестве основы для дальнейших слияний.
Это означает, что более ранние конфликты правильно распространяются, а также конфликтующие изменения в более новых редакциях.
Это относится к revctrl.org/CrissCrossMerge
, который описывает контекст рекурсивного слияния в крест-слияние .
Слияние крест-накрест - это граф предков, в котором минимальные общие предки не являются уникальными.
Простейший пример со скалярами выглядит примерно так:
a
/ \
b1 c1
|\ /|
| X |
|/ \|
b2 c2
История, которую можно здесь рассказать, заключается в том, что Боб и Клэр внесли некоторые изменения независимо, затем каждый из них объединил изменения.
Они вступили в конфликт, и Боб (конечно) решил, что его изменение было лучше, в то время как Клэр (как правило) выбрала свою версию.
Теперь нам нужно снова объединиться. Это должен быть конфликт.
Обратите внимание, что это может произойти одинаково хорошо с текстовым слиянием - каждый из них отредактировал одно и то же место в файле, и при разрешении конфликта каждый из них выбирает, чтобы результирующий текст был идентичен их исходной версии (т.е. как-то не совместить два изменения, они просто выбирают одно, чтобы выиграть).
Итак:
Другое возможное решение - сначала объединить 'b1
' и 'c1
' с временным узлом (в принципе, представьте, что 'X
' на диаграмме на самом деле является ревизией, а не просто пересечением ребер), а затемиспользуйте это как основу для объединения 'b2
' и 'c2
'.
Интересно, когда слияние 'b1
' и 'c1
' приводит к конфликтам - хитрость в том, что в этом случае 'X
' включается в конфликты, записанные внутри (например, с использованиемклассические маркеры конфликта).
Так как 'b2
' и 'c2
' должны были разрешать один и тот же конфликт, в случае, если они разрешали его одинаково, они оба удаляли конфликты из 'X
' одинаковои результаты чистого слияния;если они разрешают его по-разному, конфликты от 'X
' распространяются на конечный результат слияния.
То есть torek , описанное в «git merge: как я получил конфликт в файле BASE?» как «асимметричный результат»:
"Эти асимметричные результаты были безвредны, за исключением самой бомбы замедленного действия и того факта, что вы позже запустили рекурсивное слияние.
Вы видите конфликт. Вам решать, как его решить - снова - но на этот раз не наш простой / их трюк, если это сработало для людей C
и D
. "
Возобновление с revctrl.org/CrissCrossMerge
:
Если объединение приведет к более чем двум базам ('b1
', 'c1
,' d1
'), они будут объединены последовательно - сначала' b1
'с' c1
'и затем результат с' d1
'.
Это то, что делает стратегия "рекурсивного слияния" Git.
Обратите внимание, что Git 2.22(Q2 2019) улучшит эту стратегию рекурсивного слияния, так как в последнее время бэкэнд git merge-recursive (Git 2.18) изучил новую эвристику для вывода движения файла на основе того, какr файлы в том же каталоге перемещены.
Поскольку это по своей природе менее надежная эвристика, чем та, которая основана на сходстве содержимого самого файла (а не на том, что делают его соседи), иногда он дает конечным пользователям неожиданный результат.Это было смягчено, чтобы переименованные пути оставались на более высоких / конфликтующих стадиях в индексе, чтобы пользователь мог просматривать и подтверждать результат.
См. commit 8c8e5bd , commit e62d112, commit 6d169fd , commit e0612a1 , commit 8daec1d , commit e2d563d , commit c336ab8 , коммит 3f9c92e , коммит e9cd1b5 , коммит 967d6be , коммит 043622b , коммит 93a02c5 , коммит e3de888 , коммит 259ccb6 , коммит 5ec1e72 (05 апреля 2019) Элайджа Ньюрен (newren
) .
(объединено Junio C Hamano - gitster
- в коммит 96379f0 , 08 мая 2019 г.)
merge-recursive
: переключить каталог обнаружения переименования по умолчанию по умолчанию
Когда все x/a
, x/b
и x/c
переместились в z/a
, z/b
и z/c
в одной ветви, возникает вопрос о том, добавлено ли x/d
на другой ветке следуетостаются в x/d
или появляются в z/d
, когда две ветви объединены.
Здесь возможны различные точки обзора:
A) Файл был размещен в x / d;он не связан с другими файлами в x/
, поэтому не имеет значения, что все файлы из x/
перемещены в z/
в одной ветви;x/d
должен оставаться в x/d
.
B) x/d
относится к другим файлам в x/
, а x/
было переименовано в z/
;поэтому x/d
следует переместить в z/d
.
Поскольку до Git 2.18 не было возможности обнаружить переименования каталогов, пользователи испытывали (A)
независимо от контекста.
Выбор (B)
был реализован в Git 2.18, без возможности вернуться к (A)
, и использовался с тех пор.
Hoоднако один пользователь сообщил, что результаты слияния не соответствуют их ожиданиям, что делает изменение по умолчанию проблематичным, тем более что при перемещении файлов, обнаруженных при переименовании каталогов, уведомление не было напечатано.
Обратите внимание, что существует и третья возможностьздесь:
C) Существуют разные ответы в зависимости от контекста и содержимого, которые не могут быть определены Git, поэтому это конфликт.
Используйте более высокую ступень в индексе для записи конфликтаи уведомлять пользователя о потенциальной проблеме, вместо того чтобы молча выбирать для него разрешение.
Добавьте опцию для пользователей, чтобы указать свои предпочтения относительно использования обнаружения переименования каталогов, и по умолчанию (C)
.
Даже когда обнаружение переименования каталогов включено, добавляйте уведомления о файлах, перемещенных в новые каталоги.