例如,我有以下内容:
A--B (master)
\
C--D (feature)
如果我在提交git rebase master feature
和diff
之间执行A
git需要C
并将其应用于提交B
之上,则需要diff
提交C
和D
之间的1}}并将其应用于新提交C'
之上。 Git称之为replaying changes
。现在,如果我将feature
合并到master
中,实际发生了什么过程?
根据我所读到的内容,我的假设是git找到共同的祖先,在这种情况下A
,然后:
diff
上的最后一次提交之间,feature
和A
D
diff
上的最后一次提交之间,master
和A
B
diff
应用于共同祖先这是对的吗?我已经决定确认这一点,因为在引用replaying changes
操作时,还会提及git文档merge
。
答案 0 :(得分:2)
Git使用的最常见算法是recursive three-way merge。合并的三向部分是指两个分支头(B和D)及其祖先(A)。
首先,它决定了共同的祖先。在你的例子中,这很容易,但是在分支之间进行大量合并可能是非常重要的。如果有几个候选祖先,它将在它们和它们的候选祖先之间执行虚拟合并,并将该合并用作虚拟祖先。如果候选人的祖先无法解决,那么他们会对他们的祖先进行另一次合并......依此类推,直到找到一个祖先为止。这是"递归"部分。使用rebase更新功能分支的部分原因是为了保持分支祖先的简单。
三向合并查找祖先和两个分支(A,B和D)中相同或不同的部分。
Git具有在重命名或复制文件时启发式识别的额外功能。因此,如果功能将foo重命名为bar并进行了少量更改,Git通常会认识到该功能的foo是主要的条形并正确合并。
How diff works是通过求解longest common subsequence problem。如果您需要大量详细信息,请here is a formal study of how diff3 works。
但Git有多种合并策略,并会选择它认为最好的策略。如果您认为错误,通常是因为存在大量冲突,您可以告诉它使用哪一个并使用-s
和-X
对其进行配置。您可以在the git-merge man page中了解有关这些策略的更多信息。
这里有一些关于策略是什么以及何时使用它们的资源。
答案 1 :(得分:2)
这是正确的:git从合并基础(A
)获取到每个提示的差异并合并它们。
值得注意的是,您发出git merge
命令时所在的分支始终是生成的合并提交的第一个父级,并且您要求它的分支提示合并是第二个;如果合并因冲突而停止,则--ours
和--theirs
标志分别指向当前和即将合并的提交。
在git checkout
操作期间,git在rebase的目标上进入“分离的HEAD”,然后(实质上对于简单的rebase,但对于交互式rebase而言确实如此),每个提交到一个新的分支随着它的形成。如果cherry-pick导致合并冲突,git会将git rebase
分配给您所在的提交 - 分离的HEAD - 并--ours
分配给您正在挑选的提交,这是一个在你的内容中;所以在这种情况下,“我们的”和“他们的”感觉是相反的。
由于rebase会像你提交的那样重新设置重复基础,你可以获得合并冲突,解决它们,继续并获得另一组不同的合并冲突 - 或者在某些情况下,相同的合并冲突(在这种情况下,设置--theirs
可能是一个好主意。)
一旦rebase完成,git会调整分支引用以指向新构建的(并且不再“分离”)HEAD的尖端。