部分与Git挑选承诺


500

我正在2个不同的分支机构工作:releasedevelopment

我注意到我仍然需要将提交到发布分支的某些更改重新集成到开发分支中。

问题是我不需要所有提交,只需要某些文件中的块,所以很简单

git cherry-pick bc66559

不能解决问题。

当我做一个

git show bc66559

我可以看到差异,但真的不知道将其部分应用于当前工作树的好方法。

Answers:


791

您在这里想要的核心是git add -p-p是的同义词--patch)。这提供了一种交互式的方式来检入内容,让您决定是否应插入每个块,甚至在必要时还可以手动编辑补丁。

结合使用它与樱桃采摘:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(感谢蒂姆·海尼根(Tim Henigan)提醒我,git-cherry-pick具有--no-commit选项,也感谢Felix Rabe指出您需要重新设置! ,您可以git reset <path>...用来取消暂存这些文件。)

当然,您可以根据需要提供特定的路径add -p。如果你已经从一个补丁,你可以替换cherry-pick使用apply


如果您确实想要git cherry-pick -p <commit>(该选项不存在),则可以使用

git checkout -p <commit>

这会将当前提交与您指定的提交进行比较,并允许您分别应用该差异中的块。如果您要拉入的提交在您不感兴趣的部分提交中具有合并冲突,则此选项可能更有用。(但是请注意,这checkoutcherry-pickcheckout尝试<commit>完全应用的内容,cherry-pick应用diff的差异不同。从其父级提交的指定提交。这意味着它不仅checkout可以应用该提交,还可以应用更多。


实际上,如果我遵循git 1.7.5.4的建议,则'git add -p'表示“无更改”,因为它已经在索引中了。我需要在'git add'之前执行'git reset HEAD'-是否可以通过某些选项避免该重置?
Felix Rabe'1

@FelixRabe:实际上,我惊讶的是,在某些时候cherry-pick -n似乎没有离开的变化上演-会展无疑是一个--no-commit选择停止正确提交之前,即所有的变化上演。我将重置添加到答案中。
卡斯卡贝尔2012年

1
@Blixt也就是说,如果是在另一个分支而非当前分支的提交是ABCDEF,git checkout -p <F>不能只是让你从F中的变化,它可以让你ABCDEF所有一起捣碎,并让您整理出的哪一部分要。将其削减到F中的一些变化是很痛苦的。另一方面,git cherry-pick -n <F>仅使您获得F中的更改-如果其中一些更改发生冲突,它会帮助您了解情况,以便您确定如何正确合并。
卡斯卡贝尔2014年

当更改是添加文件时,我遇到了问题。该git reset会删除暂存文件和add -p只想说“没有补充”。
伊恩·格兰杰


36

假设您要进行的更改位于要进行更改的分支的开头,请使用git checkout

对于单个文件:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

对于多个文件,只是菊花链:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb

5
那将复制整个文件:不仅是更改。
杰里米·李斯特

1
就像到目前为止的其他答案一样,该答案有一个很大的缺点:它不会保留更改的原始作者,而是使用您的名字进行提交。如果改变是好的,那么您就是在窃取别人的信誉,如果改变是不好的,您就是在火上浇油。似乎没有办法让git cherry-pick -p消失,可惜它仍然不存在。
pfalcon

15

Mike Monkiewicz答案的基础上,您还可以指定一个或多个文件从提供的sha1 /分支中检出。

git checkout -p bc66559 -- path/to/file.java 

这将允许您交互式地选择要应用于当前文件版本的更改。


5
如果文件的当前版本与该版本明显不同,这将成为问题。系统将提示您进行许多不相关的更改(这些更改不会出现在提交中)。另外,您真正想要的更改可能以“伪装形式”显示为与当前的区别,而不是原始的区别。您想要的原始差异可能会发生冲突,并且必须正确合并;你不会在这里有这个机会。
哈兹2015年

1

如果要在命令行上指定文件列表,并通过一个原子命令完成全部操作,请尝试:

git apply --3way <(git show -- list-of-files)

--3way:如果补丁不能很好地应用,Git将创建合并冲突,因此您可以运行git mergetool。省略--3way将使Git放弃无法完全应用的补丁。


0

如果“部分采摘”的意思是“在文件内,选择一些更改,但放弃其他更改”,则可以通过输入以下内容来完成git stash

  1. 做完整的樱桃采摘。
  2. git reset HEAD^ 将整个精心挑选的提交转换为未分阶段的工作更改。
  3. 现在git stash save --patch:以交互方式选择不需要的材料进行存储。
  4. Git从您的工作副本中回滚隐藏的更改。
  5. git commit
  6. 扔掉不必要的更改藏匿处:git stash drop

提示:如果您给不需要的更改存储区起了个名字:git stash save --patch junk那么如果您现在忘记做(6),稍后您将识别该存储区的含义。


如果您只是采摘,然后重设...您将不会藏任何东西。如上所述,您必须使用--no-commit进行重选,或重置--hard HEAD〜。请注意,您的操作是否定的(选择不需要的操作)。上述公认的解决方案允许采用正面或负面的方法。
Pierre-Olivier Vares

0

使用git format-patch到出片的一部分提交您的关心和git am将它应用到另一个分支

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.