说一个git rebase等效于另一个方向的某些提交的git cherry-pick是否正确?

问题描述

我正试图加深我对git命令的理解(和交流)。

这样说是否正确

git checkout A
git rebase B

完全等同于

git checkout B
git cherry-pick <all_commits_from_common_ancestor_of_<A>_and_<B>_to_<A>>

如果没有,它们在什么情况下会发散?

解决方法

这不是很正确

这里有几个绊脚石。首先,可能没有一个共同的祖先,或者可能有一个以上的祖先。 (这是很小的事情:这意味着所有提交都将被复制,或者所有共同祖先都将被忽略。)其次,我们可能不会在此处签出 B 指定的提交。第三,可以省略某些提交,并且根据rebase的形式,这可能会变得有些复杂。最后,复制是在分离式HEAD 模式下进行的,此后,重新定位分支名称(就像通过git checkout -Bgit switch -Cgit branch -f git checkoutgit switch)。

要枚举的实际提交取决于rebase的 upstream 参数,可以通过以下方式指定该参数:​​

git rebase --onto <target> <upstream>

或:

git rebase <upstream>

如果省略了--onto <target>选项,则 target upstream 相同。这是被检出的提交(在分离HEAD模式下)。

The rebase documentation首先建议要枚举的提交是:

upstream..HEAD

(在结帐步骤之前,当然会移动HEAD)。事实并非如此,因此当前文档会立即对其进行一些纠正:

这与git log <upstream>..HEAD将显示的一组提交相同;或git log 'fork_point'..HEAD(如果--fork-point有效)(请参阅下面--fork-point的说明);或通过git log HEAD(如果指定了--root选项)。

稍后,它添加了以下内容:

请注意,在HEAD中进行的文本更改与HEAD中的提交相同的所有提交.. 将被忽略(即,将跳过上游已接受的具有不同提交消息或时间戳的补丁程序)。

直到很久以后才提到完全省略了 merge commits ,除非您使用(现已弃用)-p选项或(Git 2.18中的新功能){{1 }}选项。

实际上,这是Git在-r模式下为git rev-list使用三点语法的原因。 1 基本的三点语法:

--left-right

枚举所有 提交都可以访问的提交,但 提交都不能到达的提交。用图的理论术语来说,这是对称差the gitrevisions documentation中对此进行了简要描述。这迫使修订版本代码检查可从git rev-list upstream...HEAD 到达的提交,而不检查 HEAD 可从 upstream到达的提交,而不是upstream。在这样做的同时,Git在每次提交时执行一个git patch-id。这样,HEAD可以定位“相同”的提交(根据它们的更改),因此,如果它们已经被挑选到上游分支,则可以忽略它们。

那么,假设您有:

git rebase

,您运行...--o--*--D--E--B'--F <-- their-branch \ A--B--C <-- your-branch (HEAD) 复制了三个git rebase their-branch之后的提交A-B-C。重新编码的代码将计算来自FAB的补丁ID,以及来自CD,{{ 1}}和E。鉴于提交B'是您的提交F的副本,它可能 2 具有相同的补丁ID。因此,Git将从要复制的提交列表中省略 B'

B模式的介绍有些斜,但是您应该首先注意,在某些情况下B是默认选项,而在其他情况下是--fork-point是默认选项。叉点模式的工作方式是使用您的引用日志。有关更多信息,请参见Git rebase - commit select in fork-point mode

有一个相对较新的--fork-point选项确实可以进行合并基础计算。您可以使用三点语法直接调用它,也可以使用--no-fork-point选项将其打开。

最后,由于Git从字面上不能复制复制,所以发生了合并提交的遗漏(除了--keep-base或应避免使用的--keep-base选项之外)。合并的省略基本上与运行-r时使用-p选项相同。当您使用--no-merges选项时,Git将枚举合并并记下它们,并将使用更新颖的交互式脚本模式重新执行。也就是说,给定这样的图形片段:

git rev-list

一个-r将产生:

...--o--*-------F   <-- their-branch
         \
          \   B
           \ / \
            A   D--E   <-- your-branch (HEAD)
             \ /
              C

其中,新合并合并git rebase -r是通过在提交 B' / \ A' G--E' <-- your-branch (HEAD) / \ / / C' / ...--o--*-------F <-- their-branch \ \ B \ / \ A D--E [abandoned] \ / C G上实际运行git merge产生的。如果您使用B'C'设为evil merge,那么在重新合并过程中,邪恶将会消失。其余带有主要后缀(D等)的提交是通过使用底层的Cherry-pick机制进行复制来完成的。 3


1 在过去,git merge --no-commit是几个shell脚本,其中一个确实确实像这样运行A',尽管它使用了git rebase,据我回忆。从那时起,它已经用C重写,现在……变得更加复杂。 :-)

2 它是否具有相同的补丁ID,取决于是否有人在复制时必须对其进行修改。有关详细信息,请参见the git patch-id documentation

3 在旧版本的Git中, default 实际上是使用git rev-list--right-only --cherry-pick或内部等效项。这实际上也无法复制合并,并且错过了Cherry-pick检测到的一些重命名案例。在添加新的git format-patch选项期间,所有设置均已设置为将默认值切换为使用Cherry-pick,最近(2.25ish?)变成了新的默认值。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...