中间版本是合并基础,就像git merge
.
(名称“other”可能比“remote”更合适,因为不要求合并的另一端是远程的,而且 Mercurial 始终使用名称“other”,因此 Git 不需要匹配 Mercurial ,但一定的一致性可能会更好。请注意,Git 在这里也使用名称“我们的”和“他们的”,因此我们永远不会从 Git 获得 100% 的一致性。:-) )
但是等等,怎么会有合并基础呢?
有always合并基地。
通常我们甚至不必找到它,因为每个补丁在被视为补丁时都会干净地应用(无需尝试三向合并)。但有时补丁无法完全应用,我们do必须退回到三路合并。
(顺便说一句,您可以禁用此后备。请参阅--3way
, --no-3way
, and am.threeWay
in git-am 文档,尽管此处链接的页面已经过时,因为这些控件最近发生了更改。)
$ git rebase -i
pick aaaaaaa first commit
pick bbbbbbb second commit
pick ccccccc third commit
让我们也绘制提交图,这样我们就可以看到我们要变基的内容和变基的内容:
A - B - C <-- branch
/
... - o - *
\
G - H <-- origin/branch
我们将精挑细选提交A
, B
, and C
(A
= aaaaaaa
等),最终我们得到这个结果:
A - B - C [abandoned]
/
... - o - * A' - B' - C' <-- branch
\ /
G - H <-- origin/branch
让我们仔细看看第一个选择,A
.
这比较(差异)A
针对其父级,即 commit*
,并尝试应用生成的差异来提交H
.
Commit H
然而,与 commit 有所偏离*
。事实上,我们可以找到一个合并基础A
and H
,这是...提交*
。这实际上是一个相当不错的合并基础,尽管最好是 Git 可以按原样应用补丁,而不必退回到三向合并代码。
所以,提交*
是挑选时的合并基础A
onto H
。合并完成后我们得到新的提交A'
。 (其新的 SHA-1 ID 可能是aaaaaa1
例如。可能不会;我们就这样称呼它吧A'
.)
现在我们将挑选B
。这有区别B
与其父级相比,即A
,并尝试将差异应用于A'
.
Commit A'
, however, has drifted somewhat from commit B
. In fact, we can find a merge base between B
and A'
, and that is ... commit *
again. Unfortunately, this is a wretched merge base. Fortunately, Git only falls back on it if the patch cannot be applied as-is, and usually it can. But if it can't, Git will diff *
vs B
and *
vs A'
and try to merge those two diffs. Note that *
vs B
incorporates all of the changes we made in A
, but *
vs A'
also incorporates all of those same A
changes, so if we are lucky, Git notices the already-incorporated changes and does not duplicate them. edit Git cheats. (This code has changed recently in version 2.6, although the overall strategy remains the same.)
考虑实际输出git diff
当用于仅显示提交后的更改时A
承诺B
。这包括一个index
line:
diff --git a/foo b/foo
index f0b98f8..0ea3286 100644
左侧的值是文件版本的(缩写)哈希值foo
提交中A
。右侧的值是提交中文件版本的哈希值B
.
Git 只是从左侧散列伪造了一个合并基数。换句话说,commit中的文件版本A
成为伪造的合并基础。 (Git 通过--build-fake-ancestor
to git apply
。这要求特定的文件 blob 对象位于存储库中,但它们是因为它们处于提交状态A
。对于通过电子邮件发送的补丁,Git 使用相同的代码,但 blob 可能存在也可能不存在。)
请注意,Git 实际上在挑选提交时执行此操作A
同样,但这次合并基础文件是提交的版本*
,这确实是is合并基地。
Finally, we cherry-pick C
. This diffs B
vs C
, just as we diffed A
vs B
last time. If we can apply the patch as is, good; if not, we fall back to using commit *
as the merge base again. It is once again a pretty wretched merge base. the same way as before, pretending that the version in B
was the common base.
This also explains, incidentally, why you tend to see the same merge conflicts over and over again for these rebases: we're using the same merge-base each time. (Enabling git rerere
can help.)