我有一个看起来像这样的文件:
line 1
line 2
line 3
line 4
我提交了3件事:
line 5
22
我已经在3个单独的提交中完成了这个,所以我有以下图表:
* F - add line 5
* E - Merge branch 'branch' into 'master'
|\
| * D - change 2 to 22
* | C - capitalize line 2
|/
* B - adding line numbers
* A - Initial commit
我的文件现在看起来像这样:
line 1
Line 22
line 3
line 4
line 5
显然,E
的合并包含我手动解决的冲突。我想移动F
使其落在B
之后(最终压缩它们,但这是一个单独的问题)。所以我做了一个git rebase -i --preserve-merges A
,编辑器打开了,我这样做了:
pick B adding line numbers
pick F add line 5
pick C capitalize line 2
pick D change 2 to 22
pick E Merge branch 'branch'
问题出在rebase之后,我完全失去了提交F
。我的图表现在看起来像这样:
* E - Merge branch 'branch' into 'master'
|\
| * D - change 2 to 22
* | C - capitalize line 2
|/
* B - adding line numbers
* A - Initial commit
F
刚刚消失。它没有被rebase应用。出了什么问题,我该如何解决?
答案 0 :(得分:2)
之前的评论和答案(包括我的评论)事实上是正确的,因为你所做的事情可能不起作用,但可能并不是很有用的答案。
首先,虽然答案“这是一个记录错误”可能不是很令人满意,但我会指出你的例子中重新排序的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 -
到
A -- B -- F' - C' - E' <--(master)
\ /
- 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' <--(master)
/
A -- B - C - E -- F <--(temp)
\ /
- 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
到位,这种(概念上更简单的)方法即使在相互矛盾的情况下也可能有效。
答案 1 :(得分:1)
git文档中有关于rebase的警告,preserve-merges选项不应与交互模式一起使用。此组合的文档中还提到了一个错误:
BUGS 由--preserve-merges --interactive提供的待办事项列表不代表修订图的拓扑。编辑提交和重写他们的提交消息应该可以正常工作,但尝试重新提交提交往往会产生违反直觉的结果。
例如,尝试重新安排......