Fel*_*bek 6 git git-patch git-cherry-pick git-am
有时我需要在分支中挑选具有一定修复能力的标签,然后通过
git cherry-pick tags/myfix
Run Code Online (Sandbox Code Playgroud)
这是可行的,但是在进行“ inexact重命名检测”时,挑选樱桃的时间越来越长。
我的预感是,这样可以更快
git format-patch -k -1 --stdout tags/myfix | git am -3 -k
Run Code Online (Sandbox Code Playgroud)
实际上,事实证明,该解决方案可立即应用该修复程序,使我的分支处于与选择樱桃完全相同的状态。
现在我的问题是,摘樱桃到底有什么不同?我以为摘樱桃基本上就是这样实现的,但我一定误会了。
cherry-pick实现为合并,合并基础为您要引入的cmomit的父级。在不存在合并冲突的情况下,这应与生成和应用补丁的效果完全相同(但请参见托雷克(Terek)的回答有点警告,am理论上在哪里可以做错事情)。
但是通过进行合并,cherry-pick可以尝试更优雅地处理可能发生更改的情况。(实际上,-3您提供的选项am告诉它,如果需要的话,如果补丁中有足够的上下文可以执行此操作,则它应该执行相同的操作。最后我将回到这一点。 ..)
当您应用补丁时,默认情况下,如果补丁更改的代码块与您在其中应用的提交(如在生成它的父提交中)不同,则应用将失败。但是cherry-pick/ merge方法将查看这些差异是什么,并从中产生合并冲突-这样您就有机会解决冲突并继续进行。
作为冲突检测的一部分,请cherry-pick重命名检测。例如,假设您有
o -- x -- x -- A <--(master)
\
B -- C -- D <--(feature)
Run Code Online (Sandbox Code Playgroud)
你cherry-pick提交C到master。假设o您创建了file.txt,并且A您对进行了修改file.txt。但是提交B动作file.txt来my-old-file.txt,并提交C修改my-old-file.txt。
my-old-file.txtin 的更改C可能与file.txtin 的更改发生冲突A;但要看到这种可能性,Git有做重命名检测,因此可以弄清楚,file.txt而且my-old-file.txt是“同样的事情”。
您可能知道您没有这种情况,但是git在尝试检测重命名之前不知道。我不确定为什么在这种情况下会很费时;在我的经验,它通常不是,但有很多的添加和删除(之间的路径回购 B,要么C或者A我们的例子中),它可以。
当您生成并应用补丁时,它会假设没有冲突,并尝试应用补丁。仅当这遇到问题时(然后,仅由于您提供了该-3选项),它才会退回进行冲突检测合并。只要它的第一次尝试完全适用,它就可以跳过所有这些操作以及任何潜在的重命名检测。
更新 -如对问题的评论中所述,如果重命名检测无济于事且运行缓慢,您也可以将其关闭。如果您在实际上将重命名为合并的情况下使用此方法,则在重命名检测可以解决这些冲突的地方可能会引起冲突。尽管我认为它不应该,但我不能排除它可能也只是计算出错误的合并结果并悄悄地应用它-这就是为什么我很少使用此选项的原因。
对于默认的合并策略,该-X no-renames选项将关闭重命名检测。您可以将此选项传递给cherry-pick。
根据torek的评论,似乎重命名检测不应是带有的问题am。也就是说,我可以确认它能够正确处理合并仅适用于重命名检测的情况。我将回到尝试不是星期五下午的某个时候来了解这个事情的来龙去脉。
马克·阿德尔斯伯格(Mark Adelsberger)的回答是正确的(被投票赞成,也许您应该接受)。但是这里有一个历史古怪之处。
实际上,cherry-pick曾经被实现为 git format-patch | git am -3,并且git rebase仍然使用这种特殊的复制提交方法来进行某种类型的变基。1 这里的问题是,它无法检测到重命名的文件,有时(在难以描述的(罕见)条件下,但我会尝试)会误将更改应用于适当的三向合并将其正确应用的更改。考虑这种情况:
@@ -123,5 ... @@
}
}
- thing();
{
{
Run Code Online (Sandbox Code Playgroud)
删除的行周围的上下文只是花括号(或更糟的是空格),换句话说就是没有用的东西。在您使用的同一文件的版本中,由于其他事件的影响,同一文件中的第123至127行现在更早或更晚。例如,假设它们现在位于第153-158行。同时线123-127在你的文件中读取:
}
}
thing();
{
{
Run Code Online (Sandbox Code Playgroud)
但这些线是正确的:调用thing()应删除(因为它是错的)向下移动,但有一个呼叫thing()应该不会被删除,在同一个地方。
三向合并会将合并基础与您的版本进行比较,并且可能(取决于运气和可传播上下文)发现您插入了多行,因此应该删除的错误调用现在位于第155行,而不是行125。然后它将执行正确的删除,因为它知道基准行125是您的行155。
重命名检测是format-patch-then-apply和true三向合并之间的两个区别中的更重要的,但是在某些情况下,两者确实很重要。跑步可以git cherry-pick做得更彻底,更慢,更经常正确。
1特别是,只有非交互式用户才git rebase使用format-patch,即使如此,只有在不使用该-m选项的情况下,才使用来指定合并策略-s,或者使用来指定扩展选项-X。这三个选项中的任何一个都强制非交互式变基使用cherry-pick方法。
请注意,Git文档将-X参数称为“ strategy-option options”或“ strategy-option arguments”,无论哪种方式都非常笨拙。我喜欢这里的“扩展”一词,因为它解释了为什么它是-X,即扩展。扩展选项只是传递给您选择的策略的选项-s:Git不知道每个选项都能-s理解的其他选项,因此,无论您提供什么-X,Git都会为选择的策略提供信息,然后该策略本身要么接受该-X选项,然后执行某些操作,或抱怨为未知。