【问题标题】:How do I apply changes from a specific Git commit somewhere else in the commit tree?如何在提交树的其他地方应用来自特定 Git 提交的更改?
【发布时间】:2021-01-26 18:23:11
【问题描述】:

假设我在 git repo 'master' 和 'feature' 中有两个分支。这两个分支在某个点上已经分道扬镳了。现在我想从'feature'分支上的提交'0123456789abcdef'中对'script.py'进行确切的更改,并将这些更改应用于'master'分支头部的'script.py'的相应行。git cherry-pick 对我来说似乎是显而易见的选择,但它并没有完全符合我的要求。问题是git cherry-pick 使“master”分支保持最新,“feature”上的所有更改都提交到“0123456789abcdef”。
我不希望应用所有这些更改。我只想要在提交 '0123456789abcdef' 中发生的变化——仅此而已。我认为,如果我能以某种方式利用共同历史来确定相对于来自“功能”的提交,相应的更改行可能在“主”中移动的位置,而不是在“主”上应用所有这些先前的更改,那将是明智的。 我怎样才能做到这一点?当然,我可以从 git diff 的输出中复制并粘贴它,但对 Git 了解得有些清楚,在我看来,它必须有更好的方法来做到这一点,而我还不知道。

编辑:看来上述行为一定是由于我无法再重现的错误造成的。基于@torek's answer,我重复了正确的樱桃采摘过程,它产生了我想要的结果。

【问题讨论】:

  • Git 不处理“文件”或“更改”。它处理 commits,这是项目整个状态的完整快照。如果您喜欢某个提交中某个文件的 state,并且希望它成为当前分支头部文件的 state,请使用 @987654325 @ 将提交中的文件提取到您的工作树中并提交该状态。
  • 就像你说的,你可以在两次提交 scripts.py 文件之间使用git diff,并将差异输出到一个文件,然后使用git apply 应用它。
  • 谢谢@matt,我知道了。但是,作为人类,我们通常能够以比计算机更少限制的词汇甚至更抽象的术语相互交谈,并且仍然可以相互理解...... Git 可以做到git diff 和我调用“更改”的输出。这些是我感兴趣的。我不喜欢存储库的任何特定状态。我喜欢我的存储库的两种状态之间的特殊差异,我希望将这种(相对)差异应用到我的存储库完全不同的状态之上。
  • 另外@matt,Git 确实处理“文件”,例如我可以git checkout [commit] -- [a particular file],所以在上下文中讨论文件也很有意义Git 也是如此。现在我们可以继续“解析”人类的实际问题,并避免像@rootkonda 那样讲题外话吗?
  • 感谢@rootkonda,这是一个有用的指针。但是,由于不同的历史记录,这些更改不适用于“master”上文件的同一行,就像源提交中一样。我希望 Git 足够聪明,可以使用历史记录来确定相应行现在在“master”文件中的移动位置。

标签: git patch cherry-pick


【解决方案1】:

在此声明中:

git cherry-pick 对我来说似乎是显而易见的选择,但它并没有完全符合我的要求。问题是git cherry-pick 使“master”分支与“feature”上的所有更改保持同步,直到提交“0123456789abcdef”。

您将cherry-pick 与merge 混为一谈。如果你要跑步:

git checkout master
git merge 0123456789abcdef

那个会定位到master的当前tip commit的merge base,并提交0123456789abcdef,照你说的做。但是git cherry-pick 0123456789abcdef 定位到0123456789abcdef 提交,然后在此处涉及的三个提交上调用Git 的合并引擎。

效果是git cherry-pick 将找到从0123456789abcdef^0123456789abcdef 的父级)到0123456789abcdef 的更改,并将它们应用于当前或HEAD 提交。这将影响多个文件,如果多个文件与0123456789abcdef^0123456789abcdef 有任何区别(使用git diff 0123456789abcdef^ 0123456789abcdef,或更简单的git show 0123456789abcdef,查看;注意0123456789abcdef 不能是合并提交)。

如果您只想应用某个特定文件的0123456789abcdef^-to-0123456789abcdef 更改,请使用git cherry-pick -n,以便git cherry-pick 完成工作的初始部分,但在提交结果之前停止。这使您有机会使用git resetgit restore 撤销对其他文件的更改。 (如果使用 git reset,请务必提供文件名;git restore 是 Git 2.23 中的新功能,在这方面是 git reset 的一个更有限但更灵活的变体。)

请注意,git cherry-pick 相当于:

git diff master 0123456789abcdef

也就是说,它不会使任何文件的master 版本匹配0123456789abcdef 中任何文件的版本。 Git 最终会做的是一个三路合并,结合由以下产生的 diffs

git diff --find-renames 0123456789abcdef^ HEAD

(即,相对于0123456789abcdef的父级所做的更改)与由以下人员生成的那些:

git diff --find-renames 0123456789abcdef^ 0123456789abcdef

(即,他们相对于他们自己的父母改变了什么)。然后将合并的更改应用到来自0123456789abcdef^ 的快照:这会将您的更改添加到它们的父级,并将它们的更改添加到它们的父级。最终结果(如果没有冲突)是您已将他们的更改添加到您当前的提交中,其中“他们的更改”是相对于他们提交的父快照的;但这也解释了提交的父级和您的提交之间的任何线运动,甚至文件名更改。

因此,根据您的问题,似乎樱桃挑选 的答案。如果不是,您将需要提供更多信息来说明为什么会这样。

【讨论】:

  • 感谢您的详尽解释。现在我重新尝试我的存储库,它完成了我最初想要的。当我写我的原始问题时,我一定犯了一些错误。奇怪的是,我现在无法重现它。我最好的猜测是,我可能选择了除“master”之外的另一个分支,或者我可能错误地输入了merge而不是cherry-pick
猜你喜欢
  • 1970-01-01
  • 2016-09-09
  • 1970-01-01
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 2016-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多