我有这种情况:
o---o---o---o---o master
\
o---o---o---o---o next
\
o---o---o topic
如上所述,我在master上做了一些提交,我想更新其他两个分支并且有:
o---o master
\
o---o---o---o---o next
\
o---o---o topic
我已经在git rebase master
分支上执行了next
,而没有想到topic
现在没有next
分支的基本提交。
我现在该怎么办?我立刻修改中间分支是错误的吗?我该怎么办?
答案 0 :(得分:5)
这个问题有两种相对简单的解决方案。一个是尝试可能正常工作的rebase。但如果没有,它仍然很容易。这是第二种更强大的方式,其次是为什么:
git checkout topic; git rebase --onto next next@{1}
我认为,如果您绘制图表的方式有点不同,那么视图会更加清晰。这是原始的,稍微重新绘制:
A---B---C---D---E <-- master
\
F---G---H---I---J <-- next
\
K---L---M <-- topic
以下是git checkout next; git rebase master
后的内容:
A---B---C---D---E <-- master
\ \
| F'-G'-H'-I'-J' <-- next
\
F---G---H---I---J [was "next", but not anymore]
\
K---L---M <-- topic
这是因为rebase通过复制提交工作,并进行一些轻微(或重大)更改。新提交F'
是原始F
的副本,G'
是G
的副本,依此类推。
如果您现在运行git checkout topic; git rebase next
, 通常正常工作 - 但并非总是如此。这样做的原因是,默认情况下,rebase
会从单个参数中选择提交复制和从哪里复制。这有点复杂,所以让我们分两步。
当您在此处说git rebase next
时,单个参数为next
:副本将在next
(现在为J'
)提示之后进行。这部分非常简单:一个标识符next
找到附加副本的提交--onto
。 (这就是我拼写这个--onto
而不只是&#34;到&#34 ;. :-))的原因
被复制的内容部分更加棘手,有两种方式。首先,next
指定要复制的不,而当前分支(topic
)指定 复制的内容。 Git获取了topic
可以访问的所有提交的列表 - 提交A
,B
,F
,G
,H
等等一直到L
。然后它排除所有可从next
到达的提交,即提交A
,B
,C
,D
,{{1 } {},E
(但不是F'
),F
(但不是G'
),依此类推G
。一些被排除的提交不在包含列表中并不重要:我们只是排除包含列表中 的那些。
不幸的是,这会将J'
,F
,G
,H
和I
提交到要复制的提交堆中。正如您所注意到的,这就是出问题的地方。但不知何故,大部分时间,无论如何都是如此。它运作时会发生什么?为什么总是工作?
答案是rebase 也排除&#34;补丁等效提交&#34;。这里的一般想法是,当你重新定位时,Git会扫描&#34;排除&#34;列表以查看是否有任何提交做同样的事情作为提交在&#34; include&#34;名单。由于J
是F'
的副本,因此它可能是&#34;相同的&#34;至少F
,至少在补丁等价意义上。 F
与G
,G'
与H
相同,依此类推。
只要提交的修补程序与其修订版本相同,就会被排除在外。
如果这是真的 - 它通常是 - 你可以简单地H'
它就可以了。当git checkout topic; git rebase next
到F
中的某些内容被修改后, 时,<#34>过多&#34;被复制时,它们的副本不再是补丁等同的 - 它失败了。
作为一种经验法则, 1 如果Git能够自己完成第一个rebase,那么它也可以自己完成第二个。主要是当它因冲突而停止,并让你手动修复它,补丁变成&#34; not-patch-equivalent&#34;。但是处理这个问题很容易,因为H
有更长的形式。
而不是:
git rebase
我们可以写:
git rebase next
根据我们绘制的图表,此处使用的git rebase --onto next <something>
是指定提交<something>
的任何内容。也就是说,我们需要找到J
的位置。 next
提交了哪些提交,之前我们重新提交了它?它现在指向next
,但过去常常指向J'
。我们需要Git排除的提交是那些J
用于的人:next
,A
,B
,F
, G
,H
和I
。如果Git无法通过补丁等价来找到它们,我们就可以告诉它。
这仍然留下了如何拼写标识提交J
的名称的问题。一种方法是通过原始哈希ID,它总是有效,但是有点痛苦(无论是查找还是复制,虽然剪切和粘贴对复制哈希很有效)。但请再次查看重新绘制的图形,其中的位为J
。如果我们只能告诉Git查找was "next", but not anymore
的之前的值。
但是等等! Git有 reflogs !我们可以告诉它这样做! next
的先前值拼写为next
。 (每次我们更改标签next@{1}
时,通过向其添加新提交,该数字会递增,但我们刚才所做的只是next
,所以它是&#39;仍然是rebase
。)所以在这一点上我们可以这样做:
next@{1}
(取决于您的shell,您可能需要在其中包含大括号的内容周围使用引号,例如git checkout topic; git rebase --onto next next@{1}
或'next@{1}'
或其他类似内容。
1 有一些例外。例如,在这种情况下,如果next@\{1\}
由于差异拾取不同的半无关匹配(如空行或近支撑线)而不会产生与git diff E F'
相同的更改集,这会导致问题。但是,处理这些问题的答案与处理因冲突而产生的手工折扣的答案相同。