为什么 git rebase 忽略合并提交?

Why does git rebase ignore merge commits?

最近我不得不使用 git rebase master 做一些变基来解决一些合并冲突。 Git,令我惊讶的是忽略了合并提交,这导致了很多令人头疼的代码消失的地方。最终我发现 -p 是我要找的东西,但为什么 git rebase 的默认行为忽略合并提交?

来自man git-rebase,据说:

The interactive rebase command was originally designed to handle individual patch series. As such, it makes sense to exclude merge commits from the todo list, as the developer may have merged the then-current master while working on the branch, only to rebase all the commits onto master eventually (skipping the merge commits).

我想补充一点,如果您将 git rebase --interactive 与合并一起使用,您应该(同样,根据 man git-rebase)使用 --rebase-merges 而不是 --preserve-merges。它会让你避免很多其他令人头疼的问题。

任何时候你问 为什么 问题,你都会进入哲学领域,这可能非常棘手。但是无论如何我可以提出两个答案(文档支持其中一个,如 .

首先,变基背后的最初想法是个人在合并到某种更权威的存储库之前或期间会做的事情。

让我们发明两个玩家,爱丽丝和鲍勃。 Alice 有 authoritative 版本:任何想要最新最好版本软件的人都可以去找 Alice。

在 Alice 的存储库中,有各种开发线:

...--o--o--o--o--o   <-- master
            \
             o--o--o   <-- feature/tall

等等。 Bob 在某个时候克隆了 Alice 的存储库,也许是在 this point:

...--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

在 Alice 添加到她的权威 master 的最后两个提交之前 master

Bob 然后从 他的 master 开发了他的特征 feature/short,所以他有:

             A--B   <-- feature/short
            /
...--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

他认为他已准备好将他的结果交付给爱丽丝。所以他运行 git fetch origin 来获取她的任何更新,现在他有这个:

             A--B   <-- feature/short
            /
...--o--o--o   <-- master
           |\
           | o--o   <-- origin/master
            \
             o--o--o   <-- origin/feature/tall

他现在可能会更新他自己的 master 以便它指向与他的 origin/master 相同的提交(爱丽丝当前的提示 master):

             A--B   <-- feature/short
            /
...--o--o--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

在将他的 A--B 系列提交提交给 Alice 之前,他应该确保它们有效。所以他可以 git checkout master && git merge feature/short 产生:

             A---B
            /     \
           |       M   <-- feature/short
           |      /
...--o--o--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

Bob 可以测试 M 并查看它是否有效——所以现在可以安全地将 AB 变基到 master 的顶端,给出:

           [old commits, no longer in use]
           |
           |       A'-B'  <-- feature/short
           |      /
...--o--o--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

请注意,提交 M 已从 rebased feature/short 中消失:Bob 现在应该将新的和改进的 A'-B' 提交链交付给 Alice,Alice 可以选择是否合并它们,或快进到 B',或她喜欢的任何内容。

这里的第二个想法是实际上不可能复制合并提交。将提交 A 复制到 A' 只是进行与 A 与其父项相比相同的更改。将 B 复制到 B' 只是进行与 BA 相同的更改。但是你不能复制合并;你必须做一个全新的合并。那当然是可能的;这就是旧 -p 或新花式 --rebase-merges 实际做的事情:他们只是确定以前发生合并的地方,然后做一个新的——如果新的合并基础不同,结果可能会非常不同——任何有意义的地方。

新合并的两个父项之一很明显:它是来自某个原始提交的变基副本提交,它是原始合并的父项。 other 父级——假设是双父级合并,无论如何——并不是那么明显:有时它是一个未更改的原始提交,有时它是一个重新设置的提交。所以这项工作比一开始看起来要难。旧的非 -p 变基代码简单地说:这很难,而且在大多数情况下我们根本不想这样做,所以我们不要费心去尝试。

(我认为这是错误的——如果你天真地变基一个涉及合并的链,你可能也想复制合并——但与此同时,我认为如果你是天真地 rebase 一个涉及合并的链,你对你在做什么考虑得不够多。所以默认行为有点有意义。如果它检测到要跳过的合并,并警告或需要一个,它可能更有意义标志。)