正常变基ignores合并。对于您的情况,这实际上会有帮助!
背景
首先,请记住变基副本提交。无论哪个提交实际上都会被重新定位,都会被复制。 (原始版本保留在您的存储库中,只是“放弃”,这样如果您不喜欢变基的结果,您可以挽救它们。一段时间后,默认情况下为 30 天,放弃变得更加永久,因为垃圾收集器现在允许真正删除它们。)
让我们从重新绘制图表开始git log --oneline
以水平格式显示(这很有用并且有很多有用的信息),但会丢弃一些信息。这通常可能不太有用,但它应该更容易“看森林” https://en.wiktionary.org/wiki/see_the_forest_for_the_trees(可以说,忽略了一些树木)。我不确定你实际上在哪个分支,所以我只会使用标签somebranch
(您当前的分支很可能是master
,这是我的猜测,但我无法从您引用的日志输出中证明这一点)。
o o B--C
/ \ / \ / \
o--o o--o o--A F--G <-- somebranch
\ / \ / \ /
o o D--E
All the o
提交是您的最早(最低行)的提交git log
输出,而标记的提交A
通过G
是来自前 7 行。犯罪G
(这是我们的单字母代码,代表80e2fa1
) 是你想要“压缩”到提交中的那个C
(真的6077ae7
)。犯罪F
是你说你想放弃的合并(真的7850013
).
现在,最简单的形式git rebase
只是git rebase
没有任何争论,但这可能不是我们想要的。相反,我们需要复制并因此展平一些以以下任一开头的提交B--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
每个提交都有一个勾号(例如C'
) 意味着提交是通过复制并更改原始内容进行的。
We 也许不会需要改变B根本不。原本的B
has A
作为其父提交。如果我们不改变B
,不过,我们必须复制D
to D'
。之间的明显区别D
and D'
就是它D'
has C'
作为其父级。这意味着我们还必须复制E
to E'
,这很像E
但有D'
作为其父级。
或者,我们might能够离开D
and E
完全单独,通过复制B
to B'
(with E
作为其父级)。
无论如何我们一定要复制C
to C'
,就像C
但是(1)可能有不同的父母并且(2)有G
被压扁了。
实际结果将是这些更复杂的图表之一,这就像我们想要的结果,但更混乱,因此更难查看:
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
,不太正确,因为6077ae7
是我们在这里绘制为“commit”的提交C
“。你传递给的参数git rebase
是一个应该提交的提交exclude,默认情况下,新提交会去after那一点。我们must已提交C
(真的6077ae7
)在指令表中,这样我们就可以移动提交G
之后并使其成为squash
。 (第一个操作永远不可能squash
.) 我们希望 rebase 排除的提交,以及之后的副本,实际上是提交A
(真的7607733
).
因此所需的命令是:
git rebase -i 7607733
它告诉 Git 制作一个指令表,其中包含pick
每次提交的命令after A
,直到当前提交(G
),不包括合并提交(F
).
我不太清楚是否B--C
将首先出现,或之后出现D--E
,在说明书中,但这并不重要:所有B
, C
, D
, E
, and G
将在那里,按某种顺序,与B
绝对之前C
, D
绝对之前E
, and G
最后的。所以这将是:
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
Or:
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
无论他们按什么顺序show在,现在你的工作是改编他们这样80e2fa1
紧随其后6077ae7
。 (在第二种情况下,它已经这样做了,在第一种情况下,您必须将其向上移动,或重新洗牌其他pick
行。)然后您可以更改pick
for 80e2fa1
to squash
,写出说明表,然后退出编辑器,rebase 将开始其复制(和压缩)操作。
附加信息
请注意,rebase 现在将复制所有B
, C
, D
, and E
即使我们要复制B
and C
after E
。这似乎可能与我们上面的图纸相矛盾,我们只得到了副本D'
and E'
如果他们必须这样做的话,为了追赶C'
.
这里的技巧实际上是一个关键的 Git 属性:如果某个提交被复制到其自身的逐位相同版本,则新提交具有same ID作为原始提交。即使副本中只有一个东西不同,尽管新提交有一个不同的ID.
提交的数据包括作者和提交者,以及一些时间戳。 “现在”的时间与“几秒钟前”的时间不同,因此看起来副本总是会被更改 - 事实上,它would总是会被改变,但是git rebase
代码检查是否可以通过按原样重新使用原始提交来完好无损地保留原始提交。几乎可以肯定没有什么可以阻止它离开原来的D--E
链条完好无损if新的结果是A--D--E
.
在某种程度上,这并不重要:出于您的目的,您可能不在乎是否得到A-D'-E'-B'-C'
vs A-D-E-B'-C'
。如果你去A-B-C'-D'-E'
你可能不会关心 Git 是否生成了版本A-B'-C'-D'-E'
反正。但变基代码确实会尽可能保留原始代码,因为在某些情况下,哈希 ID 确实很重要。
(如果我们迫切需要保护A-D-E
ID,以及git rebase
坚持通过复制到新的时间戳来更改它们,我们可以通过使用来做我们想做的事git cherry-pick
在提交之上构建我们新的提交链E
。但实际上我们可以得到git rebase
使用其特殊情况的“尝试保留 ID”代码。事实上,如果有的话,有时恰恰相反:有时——尽管很少——我们迫切需要avoid保存 ID,这就是git rebase -i
has a --no-ff
flag:告诉它avoid快进分支指针,即避免保留 ID。)