git 与 rebase 相比,合并 diffs 应用程序流程

git merge diffs application process compared to rebase

例如,我有以下内容:

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

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

  1. 在分支 feature 上的最后一次提交之间花费 diff,此处在 AD
  2. 之间
  3. 在分支 master 上的最后一次提交之间需要 diff,这里是在 AB
  4. 之间
  5. 将第 1 步和第 2 步中的 diff 应用于共同祖先

这是正确的吗?我决定确认这一点,因为在 git 文档中提到 merge 操作时也提到了 replaying changes

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

首先,它确定了共同的祖先。在您的示例中,这很容易,但是分支之间有很多合并,这可能很重要。如果有多个候选祖先,它将在它们和他们的 候选祖先之间执行虚拟合并,并将该合并用作虚拟祖先。如果候选人的祖先无法解决,它将对他们的祖先进行另一次合并……依此类推,直到找到一个祖先。这是 "recursive" 部分。使用 rebase 更新功能分支的部分原因是为了保持分支祖先简单。

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

  • 如果三者都同意,则使用 A(或 B 或 D)。
  • 如果只有B和D一致,两个分支做同样的改变,B或D输出。
  • 如果只有祖先(A)和一个分支同意,另一个分支进行了更改。
    • 如果只有B和A一致,D(特征)做了改变,输出D。
    • 如果只有D和A同意,B(master)做了修改,B就输出了。
  • 如果都不同,则存在冲突。

Git 具有启发式识别文件何时被重命名或复制的额外能力。因此,如果功能将 foo 重命名为 bar 并进行小的更改,Git 通常可以识别功能的 foo 是 master 的 bar 并正确合并。

How diff works is by solving the longest common subsequence problem. If you want a lot of detail, here is a formal study of how diff3 works.

但是 Git 有多种合并策略,并且会选择它认为最好的一个。如果你认为它猜错了,通常是因为有很多冲突,你可以告诉它使用哪一个并用 -s-X 配置它。您可以在 the git-merge man page.

中阅读有关这些策略的更多信息

这里有一些关于策略是什么以及何时使用它们的资源。

正确:git 从合并基础 (A) 获取每个提示的差异并合并它们。

值得注意的是,发出 git merge 命令时所在的分支始终是结果合并提交的 first 父级,而分支-你要求它合并的提示是第二个;如果合并因冲突而停止,git checkout--ours--theirs 标志分别引用当前和待合并的提交。


git rebase 操作期间 git 进入 rebase 目标上的 "detached HEAD",然后(本质上是普通 rebase,但对于交互式 rebase 确实如此)cherry - 将每个提交都选择到它形成的新分支中。如果 cherry-pick 导致合并冲突,git 将 --ours 分配给您正在进行的提交——分离的 HEAD——并将 --theirs 分配给您正在 cherry-picking 的提交,这是你要重新定位的那个;所以在这种情况下 "ours" 和 "theirs" 的感觉是相反的。

由于 rebase 所做的选择与您提交的 re-base 一样多,您可以获得合并冲突,解决它们,继续,并获得另一组不同的合并冲突 - 或者在某些情况下,相同的合并冲突(在这种情况下设置 rerere.enabled 可能是个好主意)。

一旦 rebase 完成,git 调整分支引用以指向新建(不再是 "detached")HEAD 的尖端。