如何在压缩父项的合并之后处理主题分支的合并

时间:2016-07-28 18:34:07

标签: git merge rebase

说我有以下情况:

A -- B -- C -- D -- H master
     \            /
      E -- F -- G   topicA
           \
            I -- J -- K topicB

topicA使用 - squash 开关合并到master,这意味着master不知道{{1}的历史记录}}

如果我现在将topicA合并到master然后执行topicB,则差异会混乱,并且包含许多不应该在那里的更改或撤消。< / p>

在这种情况下,我通常会将diff master...topicB合并到master,然后topicA合并到topicA,然后再执行前一段中的说法。但是,有时它是不可能的(例如分支被删除)并且我以很多冲突结束。

在这种情况下我该如何处理?我有任何误解吗?

topicB是正确的解决方案吗?

1 个答案:

答案 0 :(得分:4)

作为DavidN said in a comment,您的计划看起来足够健全。

(剩下的时间太长而且很乱;它是在其他任务之间写的。)

弃用和细节

由于H创建git merge --squash,因此绘图错误。它应该是:

A -- B -- C -- D -- H master
     \
      E -- F -- G   topicA
           \
            I -- J -- K topicB

关键区别在于提交H E--F--G序列相关,至少在任何Git检测的意义上都没有。提交H内容会受到E--F--G序列中发生的任何事件的影响(当然,还有C--D序列中发生的任何事情)但是就Git现在知道的那样,有人在H找到了E--F--G而没有寻找

我现在在这里有一个相当大的题外话。

  

如果我现在将master合并到topicB

如果您进行了真正的合并

好的,让我们将其绘制为提交图,以确保这意味着您的意图。我将使用我通常的形式(稍微更紧凑,从分支名称的箭头向右滑动到右侧):

A--B--C---D----H       <-- master (still points to H)
    \           \
     E--F--G     \     <-- topicA (still points to G)
         \        \
          I--J--K--L   <-- topicB (points to new L)

请注意,我绘制了一个真正的合并,而不是假的,不是合并的#34;壁球合并&#34;。正如我们将要看到的,这确实很重要。

当Git进入 make 这个新提交L时,它必须合并提交HK。要做到这一点,它必须找到他们的合并基础(在某些情况下可以有几个合并基础,但这里只有一个)。

任何两个提交的合并基础都是最低公共祖先:也就是说,最接近两个起始提交(HK)的提交可以从< em>两个的那些开始提交。

让我们先从HK开始。 H可以从H(当然)访问,但不能从K访问。 K可以从K(当然)访问,但不能从H访问。现在我们可以检查D vs HKD是否可以从H开始,但不能从K开始。现在我们可以检查J,但无法从H访问。现在我们考虑CIFE,但直到我们一直回到提交B我们发现可以从HK找到提交。提交A也可以,但距离HK更远,因此提交B是合并基础。

然后合并以两个差异开始:

git diff B H

git diff B K

第一个差异显示我们从B更改为H的内容。当然,H包含我们在CD中更改的内容,以及我们在EFG中更改的内容。第二个差异显示我们从B更改为K的内容。当然,K包含我们在E中更改的内容,以及我们在I--J--K中更改的内容。

这包括我们在E 两次中更改的内容,但Git通常并非总是如此,但通常 - 可以很好地注意到这一点变化只有一次。因此,提交L 可能包含每次提交的所有内容,只执行一次。

  

然后执行diff master...topicB

请注意,这是使用 -dot ...语法,而不是两个 -dot ..语法。我不确定你打算在这里做什么,但三点语法本质上意味着&#34;找到(或一个)合并基础&#34;。因此,让我们再次进行此练习:master仍指向提交HtopicB现在指向新的合并提交L,我们发现合并HL的基础,现在我们有一个真正的合并(没有这个愚蠢的&#34;壁球合并&#34; 假的为我们合并的东西,没办法!)。

所以,让我们先从HL开始。 L可以从L(当然)访问,但不能从H访问。 H可以从H(当然)也可以从L到达。这意味着HL的合并基础为HmastertopicB的合并基础为主。

  

... diff master...topicB

由于master位于三点的左侧,因此将其替换为合并基,即提交H。三点的右侧被解析为其提交,即提交L。然后,差异会显示HL之间的差异。

在这种情况下,效果与git diff master..topicB相同,这意味着与git diff master topicB相同:比较提交H和{{1按顺序。

这应该是一个非常明智的差异,尽管我们最初做的L可怕的假壁球合并。 真实的合并修复了这种情况,至少对H vs H进行了修复。

如果你做了假的,不合并&#34;壁球合并&#34;

让我们再次画出这个东西,但这一次使用假的非合并L技术。新提交git merge --squash内容与我们完成真正合并的情况相同,但会有所不同:

L

现在我们回到:

  

A--B--C---D----H <-- master (still points to H) \ E--F--G <-- topicA (still points to G) \ I--J--K--L <-- topicB (points to new L)

我们需要再次找到diff master...topicBH之间的合并基础,但现在L并未指向两者 {{1} } L,但仅限于KHK都不是合并基础。 HL都不会工作:我们无法从D向后走J,我们无法向后走D来自L。实际上,合并基础提交再次提交J,因此这意味着:

H

这个差异将完全不同。

我不知道你对你的差异有什么期望,所以我无法解决这个问题:

  

差异混乱,包含很多变化或撤消,不应该存在。

返回合并,变基等

现在让我们回到问题:

  

在这种情况下,我通常会将B合并到git diff B L ,然后master合并到topicA,然后再执行前一段中的说法。但是,有时它是不可能的(例如分支被删除)并且我以很多冲突结束。

请注意,删除分支名称对其提交没有立竿见影的效果。它做的是停止保护这些提交。也就是说,因为每个分支名称使提交可达,所以这些提交对 Grim Collector 是安全的......呃... 死神垃圾集电极。为了找到合并基础,我们多次做了这个可达性的事情;不过,Git更经常地在GC期间找到保留和承诺丢弃的提交;承诺在topicAtopicB期间转移;等等。如果提交受到其他方式的保护 - 通过真实合并的可达性,或来自另一个分支或标签名称的可达性,或者其他任何 - 它们都会保留。如果您可以通过哈希ID找到它们,则可以将它们带回来。

更重要的是,对于push案例,如果您可以fetch找到它们,则可以将其删除。我们马上就会看到这一点。

壁球的一般考虑因素&#34;合并&#34;

因为壁球&#34;合并&#34;根本不是合并,他们不会保护另一个提交链,并且 - 这通常是未来合并冲突的关键 - 他们不提供 future 与更新的合并库合并。这意味着未来的合并必须检查巨大的差异,而不是小的差异,然后Git的自动化&#34;冗余变化&#34;检测失败。

这在实践中的意义取决于你如何使用这些壁球而不是合并&#34;合并&#34;。当您使用它们进行一系列开发并将其减少到一次提交时,完全停止使用其他开发线可能是一个好主意。您可以保存它(使用分支或标记名称,甚至是分支和标记名称空间之外的其他引用,以便您通常不会看到它,从而使提交链不被GC编辑)或者只是让它它得到了收获,但无论哪种方式,你可能不应该继续使用它,并且包括你从它上面的一些提交分叉的任何其他分支。

使用rebase

  

git log是正确的解决方案吗?

使用git rebase,您可以将这些其他链 - 您的rebase --onto master topicA topicB(在本例中)复制到新链,然后将标签(git rebase)指向复制链的顶端。您要复制的提交是那些未被压缩的提交:这里是topicB链。使用topicB作为I--J--K topicA参数将选择正确的提交集。请注意<upstream>到达提交git rebasetopicAGFE,而B到达{{1} }},AtopicBKJ等等;因此,使用I作为F会切断E背面的所有内容,但需要您提供的显式topicA

如果标签<upstream>被删除,你仍然可以做这个rebase,它变得更加棘手。您需要做的是通过其哈希ID指定提交F--onto,以便切断提交topicA和更早。 G的哈希ID是难以找到的任何地方(GC没有将其删除但是无法从任何实时引用中删除)到不存在(GC已将其删除)。但是,F的ID就在F链中:G的父级是FtopicB&#39;父级是KJ的父级是J。问题是没有简单的方法来确定提交I是否在早期I处理的链中的提交集中。

(这与我刚才加粗的早期评论有关,但并不完全相同。)