从另一个从master重新分支的分支重新启动分支

时间:2016-09-20 12:23:53

标签: git rebase

我有这种情况:

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分支的基本提交。

我现在该怎么办?我立刻修改中间分支是错误的吗?我该怎么办?

1 个答案:

答案 0 :(得分:5)

这个问题有两种相对简单的解决方案。一个是尝试可能正常工作的rebase。但如果没有,它仍然很容易。这是第二种更强大的方式,其次是为什么

git checkout topic; git rebase --onto next next@{1}

发生了什么:rebase如何运作

我认为,如果您绘制图表的方式有点不同,那么视图会更加清晰。这是原始的,稍微重新绘制:

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可以访问的所有提交的列表 - 提交ABFGH等等一直到L。然后它排除所有可从next到达的提交,即提交ABCD,{{1 } {},E(但不是F'),F(但不是G'),依此类推G。一些被排除的提交不在包含列表中并不重要:我们只是排除包含列表中 的那些。

不幸的是,这会将J'FGHI提交到要复制的提交堆中。正如您所注意到的,这就是出问题的地方。但不知何故,大部分时间,无论如何都是如此。它运作时会发生什么?为什么总是工作?

答案是rebase 排除&#34;补丁等效提交&#34;。这里的一般想法是,当你重新定位时,Git会扫描&#34;排除&#34;列表以查看是否有任何提交做同样的事情作为提交在&#34; include&#34;名单。由于JF'的副本,因此它可能是&#34;相同的&#34;至少F,至少在补丁等价意义上。 FGG'H相同,依此类推。

只要提交的修补程序与其修订版本相同,就会被排除在外。

如果这是真的 - 它通常是 - 你可以简单地H'它就可以了。当git checkout topic; git rebase nextF中的某些内容被修改后, 时,<#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 用于的人:nextABFGHI。如果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'相同的更改集,这会导致问题。但是,处理这些问题的答案与处理因冲突而产生的手工折扣的答案相同。