Может произойти пустое слияние , но, помимо прочего, требуется определить, что мы подразумеваем под пустым слиянием .
Слияние имеет три входов, а не два.У diff есть два входа, а не три.
Когда Git выполняет слияние - настоящее слияние обычного типа, а не слияние с осьминогом, не ускоренная перемотка вперед или некоторые другиедругих вещей, которые git merge
может сделать, но истинное трехстороннее слияние с двумя кончиками ветвей и базой слияния - Git должен выполнить две git diff
операции, а не одну, потому что каждая git diff
сравнивает только два дерева, и нам нужно сравнить три.
Первый из трех входных данных - ни один ответвление .Вместо этого это базовый коммит слияния между двумя подсказками:
C--D--E <-- ourbranch (HEAD)
/
...--A--B
\
F--G <-- theirbranch
Здесь каждая заглавная буква заменяет фактический хеш-идентификатор фиксации.Чтобы выполнить слияние, Git должен:
- найти коммит
B
, базу слияния - сравнить
B
с E
: что сделали we изменить? - сравнить
B
с G
: что изменилось они ? - объединить эти изменения!
Когда слияниесделано, при условии, что все идет хорошо, Git делает новый коммит с двумя родителями вместо одного:
C--D--E
/ \
...--A--B H <-- ourbranch (HEAD)
\ /
F-----G <-- theirbranch
Если после факта слияния вы сравниваете H
против E
, вы увидите изменения, которые произошли через theirbranch
: B
против G
, минус все, что уже было в B
против E
.Если вы сравните H
против G
, вы увидите изменения, которые произошли через ourbranch
: B
против E
, за исключением всего, что было продублировано в B
против G
.
Если мы определим пустое слияние как единое целое, где H
против E
не даст никакой разницы (т. Е. Снимок в E
совпадает с снимком в E
) и H
против G
не дает никакой разницы, тогда E
должно соответствовать G
.(Содержимое B
становится неактуальным в этом определении!)
Если мы определим пустое слияние как то, где H
против E
не производит никакой разницы, независимо от того, H
против G
производит некоторую разницу, тогда есть больше способов добраться сюда.Одним из них является использование -s ours
при запуске git merge
, так как это указывает Git на игнорировать второй diff полностью (и на самом деле, не надо его запускать): просто используйте G
в качестве снимка для H
, продолжая при этом привязывать историю H
назад и к E
и G
.Другой способ - убедиться, что все, что находится в дифференциале B
против G
, является просто подмножеством того, что находится в дифференциале B
против H
, так, чтобы процесс объединения двух различийв результате просто получаем B
-vs- H
diff в конце.
Если мы определим пустое слияние 1114 * как одно, в котором H
соответствует B
,ограничения еще сильнее.Единственный естественный способ получить это для E
, чтобы также соответствовать B
, и, если мы не использовали -s ours
, для G
, чтобы соответствовать B
.
(Примечаниечто мы можем запустить git merge --no-commit
, чтобы остановить Git от автоматической фиксации результата слияния. В этом случае мы можем впоследствии связываться с содержимым индекса - вещью, из которой Git сделает следующий коммит - так что мы можем построить деревос H
, чтобы выглядеть так, как нам нравится, независимо от того, что находится в B
, E
и / или G
. Но я исключаю это из любой обычной настройки.)
Следовательно, еслиВы хотите предварительно вычислить, что будет делать слияние, ваша работа такова:
- Найти базовый коммит слияния
B
.(Также обратите внимание на то, что происходит, если не существует общего коммита, или если существует более одного наилучшего общего коммита, между двумя историями, полученными в результате перехода назад от двух кончиков ветвей.) - Обнаружив
B
,или совершил рекурсивное слияние, как это делает git merge -s recursive
для базового случая множественного слияния, запустите две операции diff, как это делает Git.Не забудьте включить обнаружение переименования. - Объедините два различия.
(Nне включается здесь: когда обе подсказки ветви изменяют файл относительно базы слияния, Git будет использовать любые драйверы слияния , выбранные в .gitattributes
файлах.Если вы хотите эмулировать слияние, вы должны сделать это тоже.)
Как правило, гораздо проще просто сделать слияние и посмотреть, что произойдет.Если вы не хотите обновлять текущую ветку, отсоедините HEAD
перед выполнением слияния или выполните слияние с --no-commit
, а затем используйте git merge --abort
, чтобы остановить слияние и выполнить сброс.