代码之家  ›  专栏  ›  技术社区  ›  ruohola

如何恢复非线性合并后的线性git历史?

  •  1
  • ruohola  · 技术社区  · 5 年前

    几天前,我意外地在我的主分支中进行了非线性合并。我有一个习惯,总是试图保持一个线性历史,所以现在我想恢复线性。

    我做了一个虚拟回购,它模拟了我的真实情况,目的是让这个更简单。这里有一个GitHub链接: https://github.com/ruohola/merge-question

    git log --oneline --graph --date-order :

    * 88a4b7e (HEAD -> master, origin/master, origin/HEAD) 11
    * 5aae63c 10
    *   5506f33 Merge branch 'other'
    |\
    | * b9c56c9 9
    * | 3c72a2a 8
    | * 8d2c1ea 7
    | * 35f124b 6
    * | 7ca5bc1 5
    * | b9e9776 4
    | * fd83f02 3
    |/
    * 4fa8b2e 2
    * cbdcf50 1
    

    Sourcetree中的相同图形:

    git log in sourcetree


    (本质上应该是在合并之前我会重新调整基准)

    wanted end result

    0 回复  |  直到 4 年前
        1
  •  11
  •   ruohola    5 年前

    我觉得没那么难,只要记住这需要重写师父的历史:

    git checkout b9c56c9
    git rebase 3c72a2a # rebase on top of the other branch
    git cherry-pick 5506f33..master # reapply changes from merge revision (dropping it) up until the tip of master
    # if you like the results
    git branch -f master
    git checkout master
    

    现在如果你已经有了 古老的 另一个遥控器中的主机

        2
  •  6
  •   Lasse V. Karlsen    5 年前

    一种方法是使用rebase。

    不管你选择哪种方法 必须重写存储库的历史记录。你必须接受这一点,否则你将不得不接受你目前的历史。

    让我们总结一下你的不同历史:

    • 提交4,5和8,这些在主控台上
    • 提交3、6、7和9,它们现在也在master上,但最初在另一个分支上
    • 在合并上述两个并行历史之后,Commit 10和Commit 11在master上

    要解决这个问题,我将执行以下操作:

    1. 检查“原始分支”,即commit nr.9
    2. 在这里创建一个新分支,以确保我们可以玩一点
    3. 将这个新分支(由提交3、6、7和9组成)在master上重新设置为初始合并时的状态,在提交8上也是如此
    4. 解决任何合并冲突(最初合并时也会遇到这些冲突,但现在可能需要以不同的方式处理它们,因为与合并相比,rebase的操作方式不同)
    5. 一旦你完成了,看看最后一个 以前的 在主分支上提交11,并在新分支上重新设置提交10和11的基础
    6. 如果 现在看起来不错,你可以硬重置主机到这个新的分支,并强制推到你的遥控器,使之成为新的历史

    以下是流程图,一步一步(命令如下):

                             master
                                v
    1---2---4---5---8---M--10--11
         \             /
          3---6---7---9
    

    9的新分支:

                             master
                                v
    1---2---4---5---8---M--10--11
         \             /
          3---6---7---9
                      ^
                    TEMP1
    

    ' 表示“提交副本,相同内容,新哈希”)

                                TEMP1
                                  v
                      3'--6'--7'--9'
                     /
    1---2---4---5---8---M--10--11
         \             /        ^
          3---6---7---9      master
    

    为11创建一个新分支(我不喜欢和master乱搞)

                                TEMP1
                                  v
                      3'--6'--7'--9'
                     /
    1---2---4---5---8---M--10--11
         \             /        ^
          3---6---7---9      master
                                ^
                              TEMP2
    

    在TEMP1上重新设置分支(10和11)的基:

                                TEMP1   TEMP2
                                  v       v
                      3'--6'--7'--9'-10'-11'
                     /
    1---2---4---5---8---M--10--11
         \             /        ^
          3---6---7---9      master
    

    确认TEMP2与当前主控相同,没有丢失,没有添加,等等。

    然后将主机硬重置为TEMP2:

                                        master
                                          v
                                TEMP1   TEMP2
                                  v       v
                      3'--6'--7'--9'-10'-11'
                     /
    1---2---4---5---8---M--10--11
         \             /
          3---6---7---9
    

    然后删除TEMP1和TEMP2分支。

    请注意,commit 3、6、7、9、M、10和11仍然存在于存储库中,但它们不是直接可用的,因为没有引用它们。因此,它们有资格进行垃圾收集,实际上,存储库的实际历史现在如下所示:

    1---2---4---5---8---3'--6'--7'--9'-10'-11'
                                            ^
                                         master
    

    (第0步:制作本地文件夹的完整副本,包括工作文件夹和.git存储库,然后,如果可以,在该副本中执行以下命令,如果出错,请删除副本并重新开始, 没有安全网不要跳 )

    1. git checkout <HASH-OF-9>
    2. git checkout -b TEMP1 (是的,您可以在一个命令中使用 git checkout -b TEMP1 <HASH-OF-9>
    3. git rebase -i --onto <HASH-OF-8> <HASH-OF-2> TEMP1
    4. 解决合并冲突并提交(如果有)
    5. git checkout -b TEMP2 <HASH-OF-11>
      git rebase --onto TEMP1 <HASH-OF-MERGE> TEMP2
    6. git checkout master
      git reset --hard TEMP2

    最后,清理:

    git branch -d TEMP1 TEMP2
    git push -f
    

        3
  •  3
  •   ruohola    4 年前

    也许最简单的方法就是“滥用”用户的默认行为 git rebase . 也就是说,没有明确传递 --rebase-merges git再基 ,它实际上将从历史记录中删除所有合并提交。这使我们能够非常容易地获得所需的结果:

    之前:

    ~/merge-question (master) $ git log --oneline --graph --date-order
    * 88a4b7e (HEAD -> master, origin/master, origin/HEAD) 11
    * 5aae63c 10
    *   5506f33 Merge branch 'other'
    |\
    | * b9c56c9 9
    * | 3c72a2a 8
    | * 8d2c1ea 7
    | * 35f124b 6
    * | 7ca5bc1 5
    * | b9e9776 4
    | * fd83f02 3
    |/
    * 4fa8b2e 2
    * cbdcf50 1
    

    运行命令:

    ~/merge-question (master) $ git rebase 3c72a2a
    First, rewinding head to replay your work on top of it...
    Applying: 3
    Applying: 6
    Applying: 7
    Applying: 9
    Applying: 10
    Applying: 11
    

    ~/merge-question (master) $ git log --oneline --graph --date-order
    * d72160d (HEAD -> master) 11
    * 90a4718 10
    * 3c773db 9
    * ba00ecf 7
    * 9e48199 6
    * 24376c7 3
    * 3c72a2a 8
    * 7ca5bc1 5
    * b9e9776 4
    * 4fa8b2e 2
    * cbdcf50 1
    

    在这之后,只是一个简单的 git push --force-with-lease origin master 遥控器的历史又回到了线性。