先前的评论和答案(包括我的评论)在说明为什么您正在做的事情可能行不通方面是事实正确的,但可能并不是有用的答案。
首先,虽然答案“这是已知的错误”可能并不令人满意,但我想指出,在您的示例中重新排序的TODO列表实际上并没有告诉git您要复制哪种拓扑结构。这是您想要做的事情不那么容易的第一个线索,因为您如何告诉git您想要什么拓扑结构呢?您如何向git解释,您的意思是让C和D都从B更改为F的父级,而不会被理解为“将F放在C之前,以便F'和D'各自具有B作为父级”或“将F放在D之前,以便F'和C'各自具有B作为父级”?当然,结果是git没有执行任何这些操作,这也许就是有缺陷的部分。(话说回来,我敢打赌它确实重写了F,但然后将F'、C'和D'的父级都设为B,这使得F'最终无法访问。)
总之,我认为您的问题只是:可以从哪里开始。
A -- B - C - E -- F <--(master)
\ /
- D -
to
A
\ /
- D' -
不需要手动重新解决合并冲突,就可以在 E
处完成。
答案是可以的,但不像找到正确的 rebase
选项并发出单个命令那么简单。
事实上,rebase
的设计目的是生成线性历史记录;其支持者倾向于坚持线性历史记录“更易于阅读”或“更清晰”,并且这比“准确”或“由已测试过的提交组成”更重要。这些陈述并非客观普遍存在,但这就是 rebase
工具的设计目的。 preserve-merges
选项试图让您既能拥有优点又能克服缺点,但它有很多问题(不仅是使用 -i
时的错误)。它可能适用于某些简单情况,但通常尝试通过您想要保留的合并进行变基最好只会带来麻烦。
那么应该怎么做呢?以下是一种方法...请注意,当引用提交时,我已经给出了一个表达式,以在此示例中解析到正确的提交,而不仅仅是使用占位符字母。
git checkout master
git branch temp
git rebase -i master~4
// in the editor, move F after B and remove D
此时,您应该已经具备了
F' -- C' <
/
A
\ /
- D -
然后您需要变基D
。(在这种情况下,由于只有一个提交,您可能可以使用cherry-pick
代替。)
git checkout temp^^2
git checkout -b branch
git rebase --onto master^ master branch
目前为止,您已经
D' <--(branch)
/
F' -- C' <--(master)
/
A -- B - C - E -- F <--(temp)
\ /
- D -
您需要重新合并。有几种方法可以解决这个问题,但简化观察是您的最终内容应与原始历史记录中的 F
相匹配。因此,有效的解决方案是:
git checkout master
git reset --hard $(git commit-tree -p master -p branch -m "Merging branch into master" temp^{tree})
(对于-m
,你可以使用原本在E
处的任何提交消息。) 然后你可以删除temp
分支,并且如果完成了,则删除branch
分支。
在更复杂的情况下,从现有提交中获取合并结果TREE
可能不起作用,因为可能没有现有提交匹配所需的状态。在这种情况下,我认为你最好尝试一下git rerere
(文档在https://git-scm.com/docs/git-rerere)。此命令的目的是记录解决合并冲突的结果,以便在不同的合并中看到相同的冲突时可以重新应用它,我想它也可以在这里应用。(话虽如此,我从不使用它,因为我不同意需要常规使用它的工作流程的前提条件,所以我无法确切地说它在这里的表现如何。)
我还应该补充说明,如果合并没有冲突(并且非“邪恶”;即默认合并策略和选项产生所需结果),并且保持在所需的重新排序下没有冲突,则以下步骤将代替上述步骤:
// first rewrite F
git checkout master
git checkout -b temp
git rebase --onto master~3 master^
// now move master to exclude the original F
git checkout master
git reset --hard HEAD^
// now move the rest of the history
git rebase --preserve-merges temp
因此,在使用rerere
的情况下,即使在存在冲突的情况下,这种(概念上更简单的)方法也可能起作用。
--preserve-merges
,它可以正常工作。这不是一个答案,但在这里有一个相当深入的关于如何在--preserve-merges
的上下文中使用rebase的概述。 - larsks--preserve-merges
时,合并提交就会丢失,我不得不手动重新合并。在这个历史上下文中,如果我执行git rebase -i A
且不使用--preserve-merges
,即使我没有对历史记录进行任何更改,重置也会因冲突而失败。 - ewok