git rebase没有应用提交

时间:2018-02-05 16:42:33

标签: git merge rebase git-rebase

我有一个看起来像这样的文件:

line 1
line 2
line 3
line 4

我提交了3件事:

  1. 添加第5行line 5
  2. 将第2行中的L大写
  3. 在第2行22
  4. 中输入数字

    我已经在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应用。出了什么问题,我该如何解决?

2 个答案:

答案 0 :(得分:2)

之前的评论和答案(包括我的评论)事实上是正确的,因为你所做的事情可能不起作用,但可能并不是很有用的答案。

首先,虽然答案“这是一个记录错误”可能不是很令人满意,但我会指出你的例子中重新排序的TODO列表实际上并没有告诉git你想要重现什么拓扑。这是第一个线索,你想要做的事情不会那么容易,因为 你告诉git你想要什么拓扑?你如何向git解释你的意思是CD将父级从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提供的待办事项列表不代表修订图的拓扑。编辑提交和重写他们的提交消息应该可以正常工作,但尝试重新提交提交往往会产生违反直觉的结果。

     

例如,尝试重新安排......

请参阅https://git-scm.com/docs/git-rebase