为什么不变的 rebase 会失败?

Why can a changeless rebase fail?

我有一个 git 回购,现在已经有相当多的历史了。 我试图对早期提交进行一些更改,但发现了奇怪的冲突。

我发现如果我做一个 git rebase COMMIT^ 并选择一个早期的,但是在列表上我没有触及任何东西,比如选择所有提交,它仍然会通过动作,重播所有提交,对我来说无法解释,在某些情况下失败了。

这怎么可能?如果历史没有因为所有提交都被选中而改变,它怎么会失败?

rebase 通过 "playing back" 每个 diff 中的更改来工作,使 new 提交(保持旧的完整,但隐藏,并最终到期)。提交的方法是 "played back" 更直接地可用 git cherry-pick,所以我将首先描述它。

cherry-pick 涉及提取提交中所做的更改。然后,通过使用差异中包含的上下文,可以将生成的差异应用于 ("patched into") 其他一些(不同的)提交。但是,由于提交未存储为 as 更改—each commit is a complete "snapshot" of the source—a cherry-pick 必须 compute 提供的更改一个承诺。它通过将提交与其 parent 提交进行比较来实现。

这适用于线性历史,但在合并时效果不佳。特别是合并提交,根据定义,至少有两个 parent(合并超过两个 parent 是不常见的,但 git 允许它们)。给定一个有两个 parent 的提交,并且没有明显的方法来选择 哪个 parent 在进行差异时要考虑,git cherry-pick 简单地停止抱怨。它使您可以使用 -m 选项提供您在 cherry-pick 合并提交时关心的 parent。

rebase 命令采用不同的方法:默认情况下,它完全消除合并,希望它们不是必需的。如果合并很重要(正如 apparently 他们在这里所做的那样),这将导致失败 cherry-picks.

rebase 命令确实有一个 -p(又名 --preserve-merges)选项。在这种情况下,它会尝试保留合并。在这种特殊情况下,您要将提交序列重播到原始基础提交上,merge-preserving rebase 应该可以工作。

有关详细信息,请参阅文档。