与rebase相比,git merge diffs应用程序进程

时间:2015-02-14 20:19:36

标签: git

例如,我有以下内容:

A--B (master)
 \
  C--D (feature)

如果我在提交git rebase master featurediff之间执行A git需要C并将其应用于提交B之上,则需要diff提交CD之间的1}}并将其应用于新提交C'之上。 Git称之为replaying changes。现在,如果我将feature合并到master中,实际发生了什么过程? 根据我所读到的内容,我的假设是git找到共同的祖先,在这种情况下A,然后:

  1. 在分支diff上的最后一次提交之间,featureA
  2. 之间进行D
  3. 在分支diff上的最后一次提交之间,masterA
  4. 之间进行B
  5. 将步骤1和步骤2中的diff应用于共同祖先
  6. 这是对的吗?我已经决定确认这一点,因为在引用replaying changes操作时,还会提及git文档merge

2 个答案:

答案 0 :(得分:2)

Git使用的最常见算法是recursive three-way merge。合并的三向部分是指两个分支头(B和D)及其祖先(A)。

首先,它决定了共同的祖先。在你的例子中,这很容易,但是在分支之间进行大量合并可能是非常重要的。如果有几个候选祖先,它将在它们和它们的候选祖先之间执行虚拟合并,并将该合并用作虚拟祖先。如果候选人的祖先无法解决,那么他们会对他们的祖先进行另一次合并......依此类推,直到找到一个祖先为止。这是"递归"部分。使用rebase更新功能分支的部分原因是为了保持分支祖先的简单。

三向合并查找祖先和两个分支(A,B和D)中相同或不同的部分。

  • 如果三者都同意,则使用A(或B或D)。
  • 如果只有B和D同意,则两个分支进行相同的更改,输出B或D.
  • 如果只有祖先(A)和一个分支同意,则另一个分支进行更改。
    • 如果只有B和A同意,则D(特征)进行更改,输出D.
    • 如果只有D和A同意,B(主人)做出改变,B输出。
  • 如果一切都不同,就会发生冲突。

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的尖端。