说我有以下情况:A -- B -- C -- D -- H master \ / E -- F -- G topicA \ I -- J -- K topicB使用--squash开关将topicA合并到master中,这意味着master不知道topicA的历史记录。如果我现在将master合并到topicB中,然后执行diff master...topicB,则差异就变得混乱了,其中包含许多不应存在的更改或撤消。在这种情况下,在执行上一段所述内容之前,我通常将master合并为topicA,然后将topicA合并为topicB。但是,有时这是不可能的(例如,删除了分支),并且我遇到了很多冲突。在这种情况下我应该如何进行?我有什么误解吗?rebase --onto master topicA topicB是正确的解决方案吗? 最佳答案 作为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如果您进行了真正的合并OK,让我们将其绘制为提交图,以确保这意味着您的意图。我将使用通常的形式(稍微紧凑一些,将分支名称中的箭头略微移到右边):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)请注意,我绘制的是真正的合并,而不是伪造的,并非全部合并的“南瓜合并”。正如我们将看到的,这确实很重要。当Git进行新提交L时,它必须合并提交H和K。为此,它必须找到其合并基础(在某些情况下可以有多个合并基础,但是这里只有一个)。任意两个提交的合并基础为/是最低公共祖先:也就是说,最接近两个启动提交(H和K)的提交都可以通过这两个启动提交进行访问。让我们先从H和K开始。当然可以从H访问H,但是不能从K访问。当然可以从K访问K,但不能从H访问。现在,我们可以检查D与H和K:从D可以访问H,但不能从K访问。现在我们可以检查J,但是无法通过H进行访问。现在我们考虑C和I,F和E,但是直到我们回到提交B的方式之前,我们才发现可以从H和。提交K也可以,但是它与A和H都较远,因此,提交K是合并的基础。然后,合并从两个差异开始:git diff B H和git diff B K第一个差异显示了我们从B更改为B的过程。当然,H具有我们在H和C中所做的更改,以及我们在D,E和F中所做的更改。第二个差异显示了我们从G更改为B的过程。当然,K具有我们在K中所做的更改,以及我们在E中所做的更改。这具有我们在I--J--K中进行过两次更改的内容,但是Git通常(并非总是如此,但通常)很好地注意到了这一点,并且仅接受了一次更改。因此,commit E可能具有上一次提交中的所有内容,只需完成一次。 然后做一个L请注意,这是使用三点diff master...topicB语法,而不是两点...语法。我不确定您在这里打算做什么,但是三点语法实际上意味着“找到(或一个)合并基础”。因此,让我们再次进行此练习:..仍然指向提交master,并且H现在指向新的合并提交topicB,并且我们找到L和H的合并基础,现在我们有一个真正的合并(对于我们来说,没有任何愚蠢的“壁球合并”伪造合并内容!)。因此,让我们先从L和H开始。当然可以从L访问L,但是不能从L访问。当然可以从H和H访问H。这意味着L和H的合并基数是L:H和master的合并基数是master。 ... topicB由于diff master...topicB位于三点的左侧,因此将其替换为合并基础,即提交master。三点符号的右侧被解析为其提交,即提交H。差异然后向您显示L和H之间的区别。在这种情况下,效果与L相同,这意味着与git diff master..topicB相同:按此顺序比较提交git diff master topicB和H。尽管我们最初制作L的过程是可怕的假壁球合并,但这应该是一个非常明智的区别。真正的合并类型至少在H与H中修复了此问题。如果您进行的是假冒而非合并的“南瓜合并”让我们再次绘制该对象,但是这次使用伪造的not-a-merge L技术。我们的新提交git merge --squash的内容将与我们进行真正的合并的内容相同,但是图形将有所不同: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)现在我们回到: L再一次,我们需要找到diff master...topicB和H之间的合并基础,但是现在L不再指向L和K,而仅指向H。 K和H都不是合并基础。 L和D都不起作用:我们不能从J向后退到D,也不能从L向后退到J。实际上,合并基础提交还是再次提交H,因此与以下内容相同:git diff B L这个差异会大不相同。我不知道您希望从差异中得到什么,因此我无法解决这一部分: 差异被弄乱了,包含了许多不应该存在的更改或撤销。返回合并,变基等。现在让我们回到问题: 在这种情况下,在执行上一段所述内容之前,我通常将B合并为master,然后将topicA合并为topicA。但是,有时这是不可能的(例如,删除了分支),并且我遇到了很多冲突。请注意,删除分支名称对其提交没有立即影响。它的作用是停止保护那些提交。也就是说,由于每个分支名称都使提交可访问,因此可以从Grim Collector ... er ... Grim Reaper垃圾收集器安全地进行那些提交。我们做了几次可及性的工作以找到合并基础。但是,在GC期间,Git甚至更经常地查找要保留的提交和丢弃的提交。承诺在topicB和push期间进行转移;等等。如果提交受到其他某种方式的保护(通过真正的合并实现可访问性,或者通过另一个分支或标记名的可访问性,等等),它们将始终存在。如果可以通过哈希ID找到它们,则可以将它们带回。对于fetch案例更重要的是,如果可以通过rebase找到它们,则可以将其切断。我们稍后会看到。壁球“合并”的一般注意事项由于壁球“合并”实际上根本不是合并,因此它们将无法保护其他提交链,并且-这通常是将来发生合并冲突的关键-它们不会为将来的合并提供更新的合并基础。这意味着那些将来的合并必须检查巨大的差异而不是小的差异,然后Git的自动“冗余更改”检测失败。在实践中,这意味着什么,取决于您如何使用这些壁球而不是“合并”。当您使用它们进行一条开发线并将其缩减为单个提交时,最好完全停止使用另一条开发线。您可以保存它(使用分支或标记名称,或者甚至在分支和标记名称空间之外使用其他引用,以使您通常看不到它,从而防止提交链被GC编辑)或只是让它获取取得了收益,但是无论哪种方式,您都可能不应该继续进行此工作,其中包括您拥有从其某些提交派生出来的任何其他分支。使用git log git rebase是正确的解决方案吗?使用rebase --onto master topicA topicB,您可以将这些其他链(在这种情况下为git rebase)复制到新链,然后将标签(topicB)指向复制的链的尖端。您要复制的提交是未压缩的提交:这里是topicB链。使用I--J--K作为topicA的<upstream>参数将选择正确的提交集。请注意,git rebase到达提交topicA,G,F,E和B,而A到达topicB,K,J,I,,依此类推;因此,使用F作为E可以截断topicA背面的所有内容,但是需要您提供的显式<upstream>。如果标签F被删除,您仍然可以重新设置基准,这会变得更加棘手。您需要做的是通过其哈希ID指定提交--onto或topicA之一,以便将提交G和更早的版本砍掉。 F的哈希ID从难以找到(GC尚未删除它,但从任何实时引用都无法访问)到不存在(GC已删除它)的任何地方。但是,F的ID就在G链中:F的父母是topicB,K的父母是J,而J的父母是。问题在于,没有简单的方法来确定提交I是否在较早的I处理的链中的提交集中。(这与我加粗的较早的说法有关,但又不完全相同。) 08-27 22:40