我找不到有关git如何在内部执行rebase的解释。
最常见的答案是基于一个基础提交应用补丁,但我不明白这是如何实现的,因为没有办法正确地将补丁应用于(可能已完全更改的)文件。三方合并被提及,但没有明确说明合并了什么。
我找不到有关git如何在内部执行rebase的解释。
最常见的答案是基于一个基础提交应用补丁,但我不明白这是如何实现的,因为没有办法正确地将补丁应用于(可能已完全更改的)文件。三方合并被提及,但没有明确说明合并了什么。
git-rebase--am
的内部程序切换到git-rebase--interactive
,而Git 2.12(2017年2月)将交互式变量从使用shell脚本切换到Git称之为其“序列器”的东西。因此,对于每个git rebase
调用或每个Git版本,并不存在适用的单一正确答案。请注意,即使在非常旧的Git版本中,git rebase -i
也使用git-rebase--interactive
后端。
无论如何,可以将每个提交复制为似乎使用git cherry-pick
或git format-patch
和git am
。使用旧的基于git am
的算法时有一些细微差别-例如它根本不处理文件重命名;这就是它被淘汰的主要原因-而在现代Git的情况下,我们大多数情况下使用git cherry-pick
,但是可能更容易考虑这个问题是“获取并应用补丁”。
那么,更有趣的事情实际上是git am
和/或git cherry-pick
如何完成其工作。长答案很冗长无聊,最好缩短为“去看源代码”,但简短的答案是:git am
首先尝试将更改应用为补丁,仅在失败时才会回退到完整的三方合并。在git am
中查看-3
标志的简要说明。与此同时,git cherry-pick
只进行直接的三方合并。
在这里使用的合并基础通常并不是很有用。考虑这个初始DAGlet和一个git rebase
,它打算将A
复制到A'
,将B
复制到B'
,并将它们附加到提交D
之上:
A--B <-- branch
/
...--*------C--D <-- origin/branch
第一个 cherry-pick 操作是选择 A
,因此它会将提交 A
与两个分支的合并基础提交 *
进行比较。使用 git am
方法时,Git 尝试将其作为补丁应用到 D
上。如果失败了,或者你正在使用交互式 rebase 导致使用 git cherry-pick
,则该提交(如果是 cherry-pick)或每个失败的文件(如果使用 git am
)将通过三方合并过程运行。
实际上这非常合理:我们正在尝试在处理 *
-to-D
后重新播放从 *
到 A
的提交记录。最终结果是 A'
:
A--B <-- branch
/
...--*------C--D <-- origin/branch
\
A' [detached HEAD]
现在我们要复制 B
,这对于 git am
意味着生成从 A
到 B
的补丁。对于那些可以应用的补丁部分,我们只需将它们应用即可。对于存在冲突的文件,这一次我们针对每个无法应用补丁的文件,使用从 A
-vs-B
和 A
-vs-A'
中得到的差异进行低级别文件合并。A
在这里是一种奇怪的合并基础,但它肯定比没有更好,并且通常能够完美地工作。
对于实际的 git cherry-pick
变基操作,Git 使用整个提交 A
作为合并基础,并在整个树上执行正常的三方合并。每个文件的合并基础都是来自提交 A
的该文件版本。
(如果我们有超过 B
的更多提交,这将继续进行。)
很容易看出,这对于非交互式操作来说必须是这样的:
git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
"$revisions" ${restrict_revision+^$restrict_revision} \
>"$GIT_DIR/rebased-patches"
...
git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
因为git am
仅获取Index:
行以用于构建基础文件,所以在代码中很容易定位。但在git rebase
代码中,关键部分深藏在sequencer.c中,难以找到。 在旧版Git中,您可以查看shell脚本并了解其如何运行git cherry-pick
。
git cherry-pick
只是直接进行三方合并"?文档说它应用更改。我认为它是通过首先应用差异<remote-commit>^..<remote-commit>
来实现的。 - Romain Pelleringit cherry-pick
调用了序列器,序列器选择适当的父节点作为合并基础,然后根据需要运行do_recursive_merge
或try_merge_command
。文档中的解释是一个善意的谎言,旨在更易于理解。 - torekgit rebase
不同,后者可能(根据参数)调用git format-patch
和git am
。如果补丁可以干净地应用,则git am
步骤可以应用补丁而无需经过三方合并。否则,由于使用了--3way
和索引行(使用--full-index
生成),它将进行三方合并。 - torek