最近我不得不做一些改变来使用git rebase master
来解决一些合并冲突。 Git,令我惊讶的是忽略了合并提交导致很多令人头痛的问题,代码就会消失。最终我发现-p
是我正在寻找的但是为什么git rebase
的默认行为忽略了合并提交?
来自man git-rebase
,据说:
交互式rebase命令最初设计用于处理单个补丁系列。因此,从todo列表中排除合并提交是有意义的,因为开发人员可能在处理分支时合并当时的主设备,但最终将所有提交重新绑定到master上(跳过合并提交)。
我想补充一点,如果你使用git rebase --interactive
合并,你应该(根据man git-rebase
)使用--rebase-merges
而不是--preserve-merges
。它会阻止你很多其他的麻烦。
任何时候你问为什么问题你进入哲学领域,这可能是非常棘手的。但我无论如何都可以提出两个答案(其中一个是文档支持的,如padawin's answer。
第一个原因是,重新定位背后的最初想法是,这是个人在合并到某种更权威的存储库之前或期间所做的事情。
让我们发明两个球员,爱丽丝和鲍勃。 Alice有authoritative版本:任何想要最新和最好版本软件的人都会去Alice。
在Alice的存储库中,有各种开发方式:
...--o--o--o--o--o <-- master
\
o--o--o <-- feature/tall
等等。鲍勃克隆了爱丽丝的存储库,或许在这一点上:
...--o--o--o <-- master, origin/master
\
o--o--o <-- origin/feature/tall
在最后两次提交之前,Alice加入了她权威的master
。
鲍勃然后从他的feature/short
开发了他的功能master
,所以他有:
A--B <-- feature/short
/
...--o--o--o <-- master, origin/master
\
o--o--o <-- origin/feature/tall
他认为他已经准备好将他的结果交给Alice了。所以他运行git fetch origin
以获得她的任何更新,现在他有这个:
A--B <-- feature/short
/
...--o--o--o <-- master
|\
| o--o <-- origin/master
\
o--o--o <-- origin/feature/tall
他现在可能会更新他自己的master
,以便它指向与他的origin/master
(Alice目前的master
尖端)相同的提交:
A--B <-- feature/short
/
...--o--o--o--o--o <-- master, origin/master
\
o--o--o <-- origin/feature/tall
在向Alice发送他的A--B
系列提交之前,他应该确保它们正常工作。所以他可以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
鲍勃可以测试M
并看到它有效 - 所以现在可以安全地在A
的顶端重新设置B
和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
已经从重新定位的feature/short
消失了:Bob现在应该向Alice提供新的和改进的A'-B'
提交链,并且Alice可以选择是合并它们,还是快进到B'
,或者任何她喜欢的。
这里的第二个想法是,实际上不可能复制合并提交。将提交A
复制到A'
只需要进行A
与其父级相比所做的相同更改。将B
复制到B'
只是在做B
对A
做出的改变。但你无法复制合并;你必须做一个全新的合并。那当然是可能的;这就是旧的-p
或新奇的--rebase-merges
实际上做的事情:他们只是确定合并发生在哪里,并做一个新的 - 如果新的合并基础是不同的 - 可能会有非常不同的结果 - 只要有意义。
新合并的两个父项之一是显而易见的:它是来自原始合并的父级的一些原始提交的重新定位的复制提交。另一个父 - 假设一个双父合并 - 无论如何 - 实际上并不那么明显:有时它是一个未更改的原始提交,有时它是一个重新提交的提交。所以这项工作比起初看起来更难。旧的,非-p
rebase代码简单地说:这很难,而且在大多数情况下我们根本不想这样做,所以让我们懒得去尝试。
(我认为这是错误的 - 如果你天真地重新定义涉及合并的链,你可能也想复制合并 - 但与此同时,我会争辩说,如果你天真地重新定位一个链涉及合并,你没有充分考虑你正在做什么。所以默认行为排序是有道理的。如果它检测到它将跳过的合并,并警告或需要一个标志,它可能更有意义。)