Как определить, приведет ли git-слияние к пустой фиксации при двух произвольных фиксациях? - PullRequest
0 голосов
/ 20 декабря 2018

Если я использую функцию «Diff» в LibGit2Sharp для сравнения двух коммитов ветвлений, разница будет изменяться независимо от порядка их сравнения.

var newBranch = repository.Branches[currentBranchName];
var oldBranch = repository.Branches[name];

var patch = repository.Diff.Compare<Patch>(oldBranch.Tip.Tree, newBranch.Tip.Tree);
var patchChanges = patch.Count();

Если я поменяю местами newBranch и oldBranch в приведенном выше коде, идентичный / противоположный патч возвращается.Например, если первый патч имеет что-то вроде +300 -200, то диффузия ветвей в обратном порядке приведет к патчу с -300 + 200.

С другой стороны, если я объединю одну ветку вВо-вторых, не может быть внесено никаких новых изменений (т. е. будет получен пустой коммит слияния), тогда как, если я произвожу слияние в обратном порядке, БУДУТ изменения.

Я просто пытаюсь определить, является ли одна ветвьОтсутствие исправлений от другого, что аналогично ответу на вопрос о том, приведет ли объединение одной ветви к другой пустой коммит слияния.Я могу получить счет вперед / назад, но часто, когда одна ветвь отстает от другой на один коммит, этот коммит является пустым коммитом слияния, так что на самом деле он не отстает (т.е. новый коммит слияния будет пустым), поэтомуcount фактически не говорит мне, отсутствуют ли исправленные изменения.Я подумал, что, может быть, я мог бы просто различить два коммита, но, как я обнаружил, всегда есть равные / противоположные изменения в обоих направлениях, тогда как мне нужна какая-то операция, которая обнаружит, что нет изменений в одном конкретном направлении (то есть «вперед»).коммит пуст).Я не могу найти в LibGit2Sharp ничего, что сообщило бы мне, пустой ли коммит слияния.

1 Ответ

0 голосов
/ 20 декабря 2018

Может произойти пустое слияние , но, помимо прочего, требуется определить, что мы подразумеваем под пустым слиянием .

Слияние имеет три входов, а не два.У 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. Но я исключаю это из любой обычной настройки.)

Следовательно, еслиВы хотите предварительно вычислить, что будет делать слияние, ваша работа такова:

  1. Найти базовый коммит слияния B.(Также обратите внимание на то, что происходит, если не существует общего коммита, или если существует более одного наилучшего общего коммита, между двумя историями, полученными в результате перехода назад от двух кончиков ветвей.)
  2. Обнаружив B,или совершил рекурсивное слияние, как это делает git merge -s recursive для базового случая множественного слияния, запустите две операции diff, как это делает Git.Не забудьте включить обнаружение переименования.
  3. Объедините два различия.

(Nне включается здесь: когда обе подсказки ветви изменяют файл относительно базы слияния, Git будет использовать любые драйверы слияния , выбранные в .gitattributes файлах.Если вы хотите эмулировать слияние, вы должны сделать это тоже.)

Как правило, гораздо проще просто сделать слияние и посмотреть, что произойдет.Если вы не хотите обновлять текущую ветку, отсоедините HEAD перед выполнением слияния или выполните слияние с --no-commit, а затем используйте git merge --abort, чтобы остановить слияние и выполнить сброс.

...