就 Git 而言,是正确的合并结果。
Git 的git merge 通过将两个分支提示与一个公共合并基础进行比较来工作。也就是说,如果你绘制提交图,它看起来像这样:
o--o--L <-- branch1
/
...--o--o--*
\
o--o--R <-- branch2
然后将branch1 (commit L) 与branch2 (commit R) 合并的结果将通过比较commit *(两个分支派生的共同点)的内容来计算提交L的内容和提交R的内容。
从* 到L 的任何变化都发生在左侧。从* 到R 的任何变化都发生在右侧。 Git 将这两个“发生的事情”结合起来,将这些结合后的更改应用于* 的内容,如果一切顺利,则从结果中进行新的提交。
但是您使用的是--allow-unrelated-histories。 仅在没有共同提交时才需要这样做:我们绘制了 L 和 R 的历史记录,发现它们从未偏离共同点。例如,我们可能会得到:
A--o--o--...--L <-- branch1
B--o--...--o--R <-- branch2
A 和 B 是 both 根提交。那么应该 Git 使用什么作为共同的起点,来弄清楚从那时起发生了什么变化?
人们可以为自己选择的各种起点争论不休,但 Git 的回答是:使用空提交。 也就是说,使用 --allow-unrelated-histories,Git 会假装有一个没有文件的公共提交完全没有:
A--o--o--...--L <-- branch1
/
*
\
B--o--...--o--R <-- branch2
比较空提交* 和提交L,Git 发现branch1 上的每个文件都是以L 中的形式新创建的。比较* 和R,Git 发现branch2 上的每个文件都是以R 中的形式新创建的。无论这些文件在哪里匹配,一切都很好:我们只需要那个文件。无论L 有一个R 没有的文件,反之亦然,一切都很好:我们只需要那个文件。只要L 和R 都有文件,但它们的内容不同,就会发生合并冲突。
由于您使用了-X theirs,Git 解决了每个冲突,方法是从空的合并基础提交中获取“他们的”更改(“他们的”是您命名的提交,而不是您作为 HEAD 进行的提交)。
如果你想让 Git 假装其他提交是合并基础,你可以使用 git replace --graft 插入一些假的父链接。例如,假设您想在任意选择的提交 C 处将两者假绑定在一起:
A--o--o--...--L <-- branch1
/
B--...--C--...--R <-- branch2
以便 Git 将 C 与 L 进行比较,并将 C 与 R 进行比较。就git merge 本身而言,您在A--...--L 链中选择哪个提交并不重要; 任何 提交都可以(包括 A 和 L 本身),但请注意 Git 只会“看到”自您选择的提交以来的更改(由于 C-vs-@987654368 的差异@ 和 C-vs-R)。因此,在链中选择一些适当的提交并运行:
git replace --graft <hash-ID-of-chosen-commit> <hash-ID-of-C>
并且 Git 现在将使用替换(移植)提交而不是选择的提交。 git merge 现在将使用 C 的内容作为公共源。 (您可以在git merge 退出后立即删除嫁接,即使它因合并冲突而退出。因此,您可以编写一个插入嫁接的小脚本,运行git merge,并删除嫁接,以任意合并-选择的祖先。只要它在B--...--R 行上,就插入它作为L 的替换,或者只要它在A--...--L 行上,就将它作为R 的替换插入。)