git rebase 是否需要一个共同的提交祖先?

Does git rebase ever require a common commit ancestor?

在试验 git 时,我创建了两个没有共同提交祖先的分支。我们称他们为“主人”和“其他”。当前分支是“master”。

正如预期的那样,尝试通过以下方式合并“其他”:

git merge other

制作:fatal: refusing to merge unrelated histories

这正是我所期望的。令我惊讶的是,运行 通过以下方式变基:

git rebase other

成功。

这让我感到惊讶,因为我认为变基需要一个共同的提交祖先,就像 git 合并一样。 git rebase 是否需要一个共同的祖先?

我认为理解这一点的方法,就像很多关于 rebase 的内容一样,是理解两件事:

  • rebase 只是 cherry-pick:它根据连续的差异创建新的提交,将旧的留在原地,并将它们附加到目标。不同之处仅在于,之后,cherry picking 会推进目标分支名称,而 rebasing 会转移源分支名称。

  • git rebase xxx是一个shorthand。因此,结果可能会令人惊讶。

git rebase 的完整形式是 git rebase --onto x y z,意思是:“从(但不包括)y 开始,挑选每个连续的提交到 x直到您精心挑选 z."

当你使用shorthand形式时,x通常是你指定的提交,z是当前分支,y是共同祖先两人

但在某些情况下 shorthand 无法正常工作。在this的情况下,没有共同的祖先。因此,对于 y,Git 选择“根”,即虚无 — 就像您使用 --root 选项的完整形式一样。


为了说明,让我们假设分支 one 由提交 ab 组成,分支 two 由提交 c 组成,然后d:

a <-- b (one)

c <-- d (two)

那么如果你在two,你说git rebase oneone就是b,所以Git从two向后走(d) 到 c 并自言自语:我可以挑选 diff “nothingness-to-c”到 b 吗?如果是这样(因为没有冲突),它确实如此。然后它说:我可以选择差异“c-to-d”到我刚刚创建的提交上吗?如果是这样,它确实如此。这就是结束——我们已经到达当前分支提交——所以它停止,并将当前分支指针(HEAD)移动到它创建的最后一个新提交:

a <-- b <-- c' <-- d' (two)
      ^
    (one)

请注意 c'd' 是副本(即由 Git 创建的新提交)。原来的 cd 仍然存在,但现在没有分支名称指向它们,最终它们将通过垃圾收集被删除。