从历史记录中删除合并并重新定位非顺序提交

时间:2016-09-04 07:34:49

标签: git

我有以下git历史记录,我想压缩提交并删除多个合并

git log --graph --oneline --all
* 80e2fa1     I want to squash this commit
*   7850013   Merge branch 'master'  want to get rid of these branch merges
|\  
| * b645616 
| * 4aee207 
* | 6077ae7   Into this one
* | bc882a2  
|/  
* 7607733  
*   82643ed   Merge branch 'master'  
|\  
| * ead8b48  
* | 5fadbff  
|/  
* 2e0797c  
*   3552f8f   Merge branch 'master'  
|\  
| *  
* |  
|/  
* e1defac  
* 912c802  

但是当我尝试做一个git rebase --interactive 6077ae7时我最终得到了

  The previous cherry-pick is now empty, possibly due to conflict resolution.

当我在合并之前进行rebase时,我也没有得到合并这是删除它们的正确方法吗?这对我来说没有用git remove merge commit from history

1 个答案:

答案 0 :(得分:1)

Rebase通常忽略合并。在你的情况下,这实际上会有所帮助!

背景

首先,请记住 rebase复制提交。无论哪个提交确实得到重新定位,都会被复制。 (原件保留在您的存储库中,只是"放弃",如果您不喜欢rebase的结果,您可以拯救它们。经过一段时间,默认30天后,放弃变得更加永久,因为现在允许垃圾收集器真正删除它们。)

让我们首先重新绘制git log --oneline显示的图表(这是有用且有很多好信息),以横向格式丢弃某些信息。这通常不太有用,但它应该使"see the forest"更容易(通过忽略一些树,因为它)。我不确定你实际上是哪个分支,所以我只使用标签somebranch(你当前的分支可能是master,这是我的猜测,但我不能证明它来自您引用的日志输出。

     o      o      B--C
    / \    / \    /    \
o--o   o--o   o--A      F--G   <-- somebranch
    \ /    \ /    \    /
     o      o      D--E

所有o次提交都是git log输出中最早(最低行)的提交,而带标记的提交AG来自前7行。提交G(这是我们代表80e2fa1的单字母代码)是您想要&#34;压缩&#34;,进入提交C(真的{{} 1}})。提交6077ae7是您要放弃的合并(真的是F)。

现在,7850013的最简单形式只是git rebase,根本没有任何参数,但这可能不是我们想要的。相反,我们需要复制 - 并因此压缩 - 从git rebaseB--C开始的一些提交,以便看到此结果:

D--E

或者这个:

     o      o
    / \    / \
o--o   o--o   o--A--D--E--B'-C'  <-- somebranch
    \ /    \ /
     o      o

每个带有刻度标记的提交(如 o o / \ / \ o--o o--o o--A--B--C'-D'-E' <-- somebranch \ / \ / o o )都表示通过复制和更改原始提交来进行提交。

我们可能根本不需要更改 B 。原始C'具有B作为其父提交。但是,如果我们不改变A,我们就必须将B复制到DD'D之间的明显区别在于D'D'作为其父级。这意味着我们还必须将C'复制到E,这与E'非常相似,但E为其父级。

或者,通过将D'复制到DE,我们可以完全独自离开BB'作为其父母)。

无论如何,我们必须将E复制到C,这与C'类似,但(1)可能有不同的父级,(2)有C压扁-英寸

实际结果将是这些更复杂的图形之一,这就像我们想要的结果,但更加混乱,因此更难以查看:

G

(仔细比较第一个&#34;可能的预期结果&#34;)或:

     o      o      B--C
    / \    / \    /    \
o--o   o--o   o--A      F--G    [abandoned]
    \ /    \ /    \    /
     o      o      D--E--B'--C'  <-- somebranch

(仔细比较第二个)。

我们实际获得哪一项取决于我们向 C'-D'-E' <-- somebranch / o o B--C / \ / \ / \ o--o o--o o--A F--G [abandoned] \ / \ / \ / o o D--E 提供的指示。

到达那里

您提供的命令git rebase --interactive不太正确,因为git rebase --interactive 6077ae7是我们在此处提交的提交,因为&#34;提交{{1} }&#34 ;.您传递给6077ae7的参数是排除的提交,默认情况下,新提交在之后 。我们必须在说明表中提交C(真正git rebase),以便我们可以在它之后移动提交C并使其成为6077ae7 }。 (第一个操作永远不能是G。)我们希望我们的rebase要排除的提交,以及我们的副本之后的提交,实际上是提交squash(真的squash)。

因此所需的命令是:

A

告诉Git在 7607733之后为每个提交组成一个由git rebase -i 7607733 命令组成的指令表,直至当前提交(pick),排除合并提交(A)。

我并不完全清楚G是否会在说明书中首先显示,或F之后显示,但它并不重要:所有{ {1}},B--CD--EBC将以某种顺序存在,D肯定在{{1}之前} {,E肯定在G之前,B最后。所以这将是:

C

或者:

D

无论他们显示哪个订单,现在您的工作是重新安排,以便EG之后出现。 (在第二种情况下,它已经完成,在第一种情况下,您必须将其向上移动,或者重新调整其他pick bc882a2 yada yada yada pick 6077ae7 Into this one pick 4aee207 yede yede yede pick b645616 yide yide yide pick 80e2fa1 I want to squash this commit 行。)然后,您可以将pick 4aee207 yede yede yede pick b645616 yide yide yide pick bc882a2 yada yada yada pick 6077ae7 Into this one pick 80e2fa1 I want to squash this commit 80e2fa1更改为{{} 1}},写出说明书,然后退出编辑器,rebase将开始复制(和压缩)操作。

其他信息

请注意,即使我们要复制6077ae7,rebase也会复制所有pickpick80e2fa1squashB之后C。这似乎与我们上面的图纸相矛盾,我们只有DE才能获得副本,以便B之后。{/ p>

这里的技巧实际上是一个关键的Git属性:如果将提交复制到其自身的逐位相同版本,则新提交与原始提交具有相同的ID 承诺。如果副本中的任何内容都不同,尽管新提交具有不同的ID

进入提交的数据包括作者和提交者,以及一些时间戳。时间&#34;现在&#34;不同于几秒钟前的时间&#34;,所以看起来副本总是会被改变 - 实际上,它总是会改变 ,但是{{ 1}}代码检查以确定它是否可以完全保留原始提交,只需按原样重复使用即可。如果新的结果C ,那么几乎可以肯定没有任何东西可以阻止原始E链完整

在某种程度上,这并不重要:出于您的目的,如果您获得D' vs E',您可能并不在意。如果你选择C'版本,你可能不会在意Git是否产生了git rebase。但是如果可以的话,rebase代码会尝试保留原始代码,因为在某些情况下,哈希ID确实很重要。

(如果我们迫切需要保留D--E ID,A--D--E坚持通过复制到新的时间戳来更改它们,我们可以通过使用A-D'-E'-B'-C'来构建我们想要的东西在提交A-D-E-B'-C'之上的新提交链。但实际上我们可以让A-B-C'-D'-E'使用其特殊情况&#34;尝试保留ID&#34;代码。事实上,如果有的话,它是有时相反:有时 - 尽管很少 - 我们迫切需要避免保留ID,这就是A-B'-C'-D'-E'A-D-E标志的地方:告诉它避免快速转发分支指针,即避免保留ID。)