tl;dr: 你可以使用类似下面的命令做你想做的事情:
git rebase --onto master feature-branch~3 feature-branch
在这个例子中,feature-branch~3
是有效的,但通常可以是任何解析为提交 B
的表达式 - 比如 B
的提交哈希等。
要了解原因,请继续阅读...
首先,我们应该稍微澄清一下您的图表。你说:
我一直在 feature-branch 上工作,意外地(复杂的故事)合并了 C & D 提交到了 master分支。其他人在回滚了这些提交,因为它们破坏了构建。
(强调添加)。现在merge操作有两种可能[1],但不会复制提交 C
和 D
。默认行为看起来像这样:
A
\
E <-(feature-branch)
由于在意外合并时,master
显然处于 B
,默认行为将是将 master
快进到现有的 D
提交。 这样就好像 feature-branch
是在 D
之后创建的。
(我还进行了一些注释更改。 !CD
表示撤消了 C
和 D
的合并(无需注释)。 我认为这种画线方式更易读。 但重要的是如何表示 C
和 D
...)
另一个可能性是,如果您使用了 --no-ff
选项(或者如果 master
在合并之前与 feature-branch
分歧),则会更像:
C - D
/ \
A
再次执行merge
操作不会重复创建C
或D
,而是会创建一个“合并提交”M
,将C
和D
的更改合并到master
的历史记录中(并使C
和D
从master
可达)。
在两种情况下,合并后feature-branch
和master
之间的“合并基础”都是D
。因此,您需要解决两个问题:文档中描述的“重复补丁ID”问题;以及合并基础处于排除您提交的位置——因为rebase
甚至不会考虑已经从上游可达的提交[2]。
通常解决“合并基础”问题很有用。最后,我们将看到为什么您不必为了您的目的而这样做,但是为了完整起见,这里是如何做:
首先找到一个解析为提交B
的表达式(例如,与B
相关的提交,或上述示例中的feature-branch~3
)。在类似以下的命令中使用它:
git rebase -f feature-branch~3 feature-branch
这将复制提交记录
C
、
D
和
E
,以便您拥有它们。
E
/
A
\
C'
(其中E
在技术上仍然存在,但无法访问,因此可能最终会被gc
销毁)。当然,这假设是“快进”的情况;如果您有一个合并提交,则看起来会有所不同,但其结果将是相同的。
而且,现在您可以毫无问题地将feature-branch
合并到master
中。但是,如果您想将feature-branch
rebase
到master
(为了准备快进),则仍然必须解决您指出的原始问题: C'
和D'
将被跳过作为C
和D
的副本。
您需要的是挫败补丁ID重复检查的方法;虽然似乎没有实际的选项可用,但您可以通过防止git知道重复项的位置来实现它。不要将master
作为上游给出,而是将提交B
作为上游;然后--onto master
重新安排。
git rebase --onto master feature-branch~3 feature-branch
不仅在执行此命令时,
git
没有理由考虑
C
和
D
,而且因为您不再将
master
用作上游,所以
master
到
feature-branch
合并基础不再重要。您明确表示
B
是您的上游,因此这解决了两个问题,这就是为什么,如我上面所述,最终您不必担心
rebase -f
命令的原因。
[1]实际上,它至少可以做第三件事。如果您指定了--squash
选项,那么它将不进行真正的合并,而是在master
上创建一个单独的新提交CD
。这与通常创建的合并提交基本相同,只是不包括D
作为父提交。
C - D
/
A
这在某些特定情况下很有用,但一般情况下我不建议这样做,因为git会“失去追踪”C
和D
已经在master
中“被考虑”。在你的情况下,这种方法可能有效,这就是为什么我知道你没有这样做并且没有在答案的主要文本中包括这种可能性的原因。但在大多数情况下,它会使分支之间的未来交互更加困难。
[2] 这些问题看起来相似,但是是不同的。在一个情况下,C
已经从你要变基到的上游可达;在另一个情况下,它还没有,但是引入相同更改的另一个提交已经可达。
--onto
参数,但不知道如何在这里使用它。谢谢! - Nitheesh A S