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

ruo*_*ola 6 git git-merge git-rebase git-rewrite-history

很少提交,我不小心在我的master分支中进行了非线性合并。我习惯于一直尝试保持线性历史记录,因此现在我想恢复线性度。

我做了一个虚拟仓库,它模拟了我为了使其更简单而遇到的真实情况,这是它的GitHub链接:https : //github.com/ruohola/test-merge

这是的输出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
Run Code Online (Sandbox Code Playgroud)

Sourcetree中的相同图形:

git登录sourcetree

这是mspaint可视化的图像,显​​示了我希望如何使我的主人看起来像:(
本质上应该像我在合并之前要重新设置基础)

想要的最终结果

我知道这可能不是最佳做法,并且我熟悉重写历史记录的后果(尽管没有人在此分支上工作),但仍然希望能够做到这一点。如何做到这一点?

ruo*_*ola 12

也许最简单的方法就是“滥用” 的默认行为git rebase。也就是说,如果没有显式传递--rebase-mergesgit rebase,它实际上会从历史记录中删除所有合并提交。这使我们能够非常轻松地获得所需的结果:

前:

~/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
Run Code Online (Sandbox Code Playgroud)

运行命令:

~/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
Run Code Online (Sandbox Code Playgroud)

后:

~/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
Run Code Online (Sandbox Code Playgroud)

此后,只需简单操作git push --force-with-lease origin master,遥控器的历史就恢复为线性。


eft*_*ft0 11

我认为这并不难,只要记住它需要重写 master 的历史:

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
Run Code Online (Sandbox Code Playgroud)

现在,如果您已经在另一个远程服务器中拥有旧主服务器,则可以强制推送该分支


ang*_*son 6

一种方法是使用 rebase。

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

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

  • 提交 4、5 和 8,这些都在 master 上
  • 提交 3、6、7 和 9,这些现在也在 master 上,但最初在不同的分支上
  • 在合并上面的两个平行历史之后,提交 10 和 11 是在 master 上

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

  1. 查看“原始分支”,即commit nr。9
  2. 在这里创建一个新分支,只是为了确保我们可以玩一下
  3. 将这个新分支(由提交 3、6、7 和 9 组成)重新设置在 master 之上,就像您最初合并时一样,因此在提交 8 之上
  4. 解决任何合并冲突(您最初合并时也会遇到这些冲突,但由于与合并相比 rebase 的操作方式,现在可能需要对它们进行不同的处理)
  5. 完成此操作后,查看master 上的最后一次提交,即 11,并将提交 10 和 11 重新绑定到新分支之上
  6. 如果现在一切看起来都不错,您可以将 master 硬重置到这个新分支并强制推送到您的遥控器以使其成为新的历史记录

这是该过程的分步图(命令如下):

现在状态:

                         master
                            v
1---2---4---5---8---M--10--11
     \             /
      3---6---7---9
Run Code Online (Sandbox Code Playgroud)

9 人的新分支:

                         master
                            v
1---2---4---5---8---M--10--11
     \             /
      3---6---7---9
                  ^
                TEMP1
Run Code Online (Sandbox Code Playgroud)

在 8 之上变基,这将创建 3', 6', 7', 9'('意思是“提交的副本,相同的内容,新的散列”)

                            TEMP1
                              v
                  3'--6'--7'--9'
                 /
1---2---4---5---8---M--10--11
     \             /        ^
      3---6---7---9      master
Run Code Online (Sandbox Code Playgroud)

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

                            TEMP1
                              v
                  3'--6'--7'--9'
                 /
1---2---4---5---8---M--10--11
     \             /        ^
      3---6---7---9      master
                            ^
                          TEMP2
Run Code Online (Sandbox Code Playgroud)

将此分支(10 和 11)重新设置在 TEMP1 之上:

                            TEMP1   TEMP2
                              v       v
                  3'--6'--7'--9'-10'-11'
                 /
1---2---4---5---8---M--10--11
     \             /        ^
      3---6---7---9      master
Run Code Online (Sandbox Code Playgroud)

验证 TEMP2 与当前主站相同,没有丢失,没有添加,等等。

然后将 master 硬重置为 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
Run Code Online (Sandbox Code Playgroud)

然后我将删除分支 TEMP1 和 TEMP2。

请注意,提交 3、6、7、9、M、10 和 11 仍然存在于存储库中,但它们不能直接使用,因为没有任何引用它们。因此,它们有资格进行垃圾收集,实际上,您的存储库的实际历史现在看起来像这样:

1---2---4---5---8---3'--6'--7'--9'-10'-11'
                                        ^
                                     master
Run Code Online (Sandbox Code Playgroud)

执行这些操作的命令是:

(第 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. 检查一切是否正常
  7. git checkout master
    git reset --hard TEMP2

最后,清理:

git branch -d TEMP1 TEMP2
git push -f
Run Code Online (Sandbox Code Playgroud)

只有在您知道一切正常时才强制推送