首页 > 解决方案 > git cherry-pick 和 git format-patch 有什么区别?是吗?

问题描述

我有时需要在我的分支中挑选一个带有特定修复的标签,并且过去常常通过

git cherry-pick tags/myfix

这行得通,但是挑选樱桃需要越来越长的时间来进行“不精确的重命名检测”。

我的预感是,这可能会更快

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

事实上,事实证明,这立即应用了修复,使我的分支处于与樱桃采摘完全相同的状态。

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

标签: gitgit-cherry-pickgit-patchgit-am

解决方案


cherry-pick被实现为合并,合并基础是您引入的提交的父级。在没有合并冲突的情况下,这应该与生成和应用补丁具有完全相同的效果(但请参阅torek 的回答有点警告,am理论上,哪里可能做错事)。

但是通过合并,cherry-pick可以尝试更优雅地处理更改发生冲突的情况。(事实上​​,-3你给它的选项am告诉它,如果需要的话,如果补丁中有足够的上下文可以这样做,它应该做同样的事情。我会在最后回到这一点。 ..)

当您应用补丁时,默认情况下,如果它更改了应用它的提交中与生成它的父提交中不同的代码块,则应用将失败。但是cherry-pick/merge 方法将查看这些差异是什么,并从中产生合并冲突 - 因此您有机会解决冲突并继续。

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

o -- x -- x -- A <--(master)
      \
       B -- C -- D <--(feature)

然后你cherry-pick承诺. 假设您已创建,并且您已对. 但是 commit移动到,并且 commit修改了。Cmasterofile.txtAfile.txtBfile.txtmy-old-file.txtCmy-old-file.txt

更改为my-old-file.txtinC可能与更改为file.txtin冲突A;但要看到这种可能性,git 必须进行重命名检测,以便它可以找出它file.txt并且my-old-file.txt是“同一件事”。

您可能知道您没有这种情况,但 git 直到它尝试检测重命名时才知道。我不确定为什么在这种情况下会很耗时。以我的经验,它通常不是,但在一个添加和删除了很多路径的仓库中(在我们的示例之间 或B在我们的示例中)它可能是。CA

当您生成并应用补丁时,它会在假设没有冲突的情况下尝试应用补丁。只有当这遇到问题时(然后,只是因为您提供了-3选项),它才会回退到进行合并,并进行冲突检测。只要它的第一次尝试干净地应用,它就可以跳过所有这些 - 以及任何潜在的重命名检测。


更新- 如对问题的评论中所述,如果重命名检测没有帮助并且运行缓慢,您也可以关闭它。如果您在实际上存在对合并“重要”的重命名时使用它,则可能会导致重命名检测可以解决它们的冲突。尽管我认为不应该,但我不能排除它也可能只是计算出不正确的合并结果并悄悄地应用它——这就是我很少使用此选项的原因。

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

根据 torek 的评论,似乎重命名检测应该不是am. 也就是说,我可以确认它能够正确处理合并仅适用于重命名检测的情况。在不是周五下午的时候,我会回到试图了解这件事的来龙​​去脉。


推荐阅读