就 Git 而言,这是正确的合并结果。
Gitgit merge
通过将两个分支提示与一个通用合并基础进行比较来工作。也就是说,如果你绘制提交图,它看起来像这样:
o--o--L <-- branch1
/
...--o--o--*
\
o--o--R <-- branch2
branch1
然后将(commit L
) 与branch2
(commit )合并的结果R
将通过比较 commit 的内容(*
两个分支派生的公共点)与 commit 的内容和 commitL
的内容来计算R
。
无论从*
到L
发生在左侧。无论从*
到R
发生在右侧。Git 将这两个“发生的事情”结合起来,将这些结合后的更改应用于 的内容*
,如果一切顺利,则从结果中进行新的提交。
但是您正在使用--allow-unrelated-histories
. 仅当没有共同提交时才需要这样做:我们绘制 和 的历史L
并R
发现它们从未偏离共同点。例如,我们可能会得到:
A--o--o--...--L <-- branch1
B--o--...--o--R <-- branch2
whereA
和B
are都是根提交。那么Git应该使用什么作为共同的起点,以找出从那时起发生了什么变化?
人们可以为自己选择的各种起点争论不休,但 Git 的答案是:使用空提交。 也就是说,使用--allow-unrelated-histories
,Git 假装有一个根本没有文件的共同提交:
A--o--o--...--L <-- branch1
/
*
\
B--o--...--o--R <-- branch2
比较*
空的 commit 和 commit 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
中选择哪个提交并不重要。任何提交都可以(包括它们自己) ,但请注意 Git 只会“看到”自您选择的提交以来的更改(由于-vs-和-vs-的差异)。因此,在链中选择一些适当的提交并运行:A--...--L
A
L
C
L
C
R
git replace --graft <hash-ID-of-chosen-commit> <hash-ID-of-C>
并且 Git 现在将使用替换(移植)提交而不是选择的提交。Agit merge
现在将使用 的内容C
作为公共源。(您可以在git merge
退出后立即删除嫁接,即使它以合并冲突退出。因此,您可以编写一个插入嫁接、运行git merge
和删除嫁接的小脚本,以与任意选择的祖先合并。只需插入它L
只要在线就作为替换,或者只要B--...--R
在线就插入作为替换。)R
A--...--L