git cherry-pick和git format-patch有什么区别? git是吗?

时间:2018-08-31 17:04:38

标签: git git-cherry-pick git-patch git-am

我有时需要在分支中挑选具有一定修复能力的标签,然后通过

git cherry-pick tags/myfix

这有效,但是进行“ inexact重命名检测”的过程中采摘樱桃的时间越来越长。

我的直觉是,这样做可能会更快

git format-patch -k -1 --stdout tags/myfix | git am -3 -k

实际上,事实证明,这是立即应用此修复程序,使我的分支处于与选择樱桃完全相同的状态。

现在我的问题是,摘樱桃到底有什么不同?我以为摘樱桃基本上就是这样实现的,但我一定误会了。

2 个答案:

答案 0 :(得分:7)

cherry-pick是作为合并实现的,合并基础是您要引入的cmomit的父级。在没有合并冲突的情况下,这应与生成和应用完全相同。修补程序(但请注意torek's answer,但am在理论上可能做错了事)。

但是,通过进行合并,cherry-pick可以尝试更优雅地处理更改可能会冲突的情况。 (实际上,您赋予-3的{​​{1}}选项告诉它,如果需要的话,如果补丁中有足够的上下文可以执行此操作,则它应该执行相同的操作。会回到最后一点...)

应用补丁程序时,默认情况下,如果补丁程序更改的代码块与应用补丁程序中的提交(与生成该补丁程序的父提交中的代码)不同,则应用程序将失败。但是am /合并方法将查看这些差异是什么,并从中产生合并冲突-这样您就有机会解决冲突并继续下去。

作为冲突检测的一部分,cherry-pick会重命名检测。例如,假设您有

cherry-pick

,然后您o -- x -- x -- A <--(master) \ B -- C -- D <--(feature) cherry-pick提交到C上。假设您在master创建了o,并且在file.txt中对A进行了修改。但是落实file.txtB移动到file.txt,而落实my-old-file.txt则修改了C

my-old-file.txt中对my-old-file.txt的更改可能与在C中对file.txt的更改冲突;但是要看到这种可能性,git必须进行重命名检测,以便可以找出Afile.txt是“同一件事”。

您可能知道您没有这种情况,但是git在尝试检测重命名之前不知道。我不确定为什么在这种情况下会很费时;根据我的经验,通常不是,但是在具有许多添加和删除路径的回购中(在我们的示例中,my-old-file.txtBC之间)。

当您生成并应用补丁时,它会尝试在没有冲突的前提下应用补丁。仅当这遇到问题时(然后,因为您提供了A选项),它才可以使用冲突检测进行合并。只要它的第一次尝试完全适用,它就可以跳过所有这些操作以及所有可能的重命名检测。


更新-如对该问题的评论所述,如果重命名检测无济于事且运行缓慢,您也可以将其关闭。如果您在实际上将重命名为合并的情况下使用此方法,则在重命名检测可以解决这些冲突的地方可能会引起冲突。尽管我认为它不应该,但我不能排除它可能也只是计算出错误的合并结果并悄悄地应用它-这就是为什么我很少使用此选项的原因。

对于默认的合并策略,-3选项将关闭重命名检测。您可以将此选项传递给-X no-renames

根据torek的评论,似乎重命名检测应该不是cherry-pick的问题。也就是说,我可以确认它能够正确处理合并仅适用于重命名检测的情况。我将回到尝试不是星期五星期五的某个时候来了解这个事情的来龙去脉。

答案 1 :(得分:7)

Mark Adelsberger's answer是正确的(已投票,也许您应该接受)。但是这里有一个历史古怪之处。

实际上,cherry-pick曾经被实现为 as git format-patch | git am -3,而git rebase仍然使用这种特殊的复制提交方法来进行某种类型的变基。 1 这里的问题是,它无法检测到重命名的文件,有时(在难以描述的(罕见)条件下,但我会尝试)在适当的三向合并正确应用它们的情况下错误地应用了更改。例如考虑这种情况:

@@ -123,5 ... @@
     }
   }
-  thing();
   {
     {

,其中被删除的行周围的上下文只是花括号(或更糟的是空格),换句话说,就是没有用的东西。在同一文件的您的版本中,由于某些其他事件, 行123至127所在的行现在早于同一文件。例如,假设它们现在位于第153-158行。同时,您的文件中的第123-127行显示为:

    }
  }
  thing();
  {
    {

但是这些行是正确的:应该删除对thing()的调用(因为它是错误的),但是对thing()的调用应该<在同一位置删除。

三向合并会将合并基础与您的版本进行比较,并且也许-也许(取决于运气和可传播的上下文)会发现您插入了各种行,以便应该删除的错误调用现在位于第155行,而不是第125行。由于它将知道基准行125是您的行155,因此它将执行正确的删除操作。 / p>

重命名检测是format-patch-then-apply与真正的三路合并之间的两个区别中的更重要的,但是在某些情况下,两者确实很重要。运行git cherry-pick可以完成更彻底,更慢,更经常正确的事情。


1 特别是,只有非交互式git rebase曾经使用过format-patch,即使如此,除非您使用{ {1}}选项,用-m指定合并策略,或用-s指定扩展选项。这三者中的任何一个都强制非交互式变基使用cherry-pick方法。

请注意,Git文档将-X参数称为“ strategy-option options”或“ strategy-option arguments”,这两种方式都非常笨拙。我喜欢这里的“扩展”一词,因为它解释了为什么它是-X,即被扩展。扩展选项只是将您通过-X选择的策略传递给 的选项:Git不知道每个-s可以理解的其他选项,因此您对{{1 }},Git给出了选定的策略,然后该策略本身要么接受-s选项并执行某项操作,要么抱怨它为未知。