如何git-cherry-pick仅更改某些文件?

Tob*_*ler 525 git github cherry-pick

如果我想要合并到Git分支中,那么只对某个特定提交中更改的某些文件进行了更改,其中包括对多个文件的更改,如何实现?

假设git的承诺称为stuff具有对文件的改变A,B,C,和D,但我想只合并stuff的对文件的更改AB.这听起来像是一项工作,git cherry-pickcherry-pick只知道如何合并整个提交,而不是文件的子集.

Cas*_*bel 631

我会用cherry-pick -n(--no-commit)来做,它允许你在提交前检查(和修改)结果:

git cherry-pick -n <commit>

# unstage modifications you don't want to keep, and remove the
# modifications from the work tree as well.
# this does work recursively!
git checkout HEAD <path>

# commit; the message will have been stored for you by cherry-pick
git commit
Run Code Online (Sandbox Code Playgroud)

如果绝大多数修改都是您不想要的,而不是检查单个路径(中间步骤),您可以重置所有内容,然后添加您想要的内容:

# unstage everything
git reset HEAD

# stage the modifications you do want
git add <path>

# make the work tree match the index
# (do this from the top level of the repo)
git checkout .
Run Code Online (Sandbox Code Playgroud)

  • 除了`git checkout .`之外,我还建议使用`git clean -f`删除由挑选的提交引入的任何新的但不需要的文件. (7认同)
  • 如果樱桃挑选的提交不适用于当前工作副本,因为它是如此不同,但是*****将*干净地应用,这不是很好. (6认同)
  • 后一种方法的附加说明:我使用`git add -p`,它允许您以交互方式决定要添加到索引_per file_的哪些更改 (3认同)
  • 你也可以选择性地使用`git reset -p HEAD`取消暂存.它相当于`add -p`,但很少有人知道它存在. (3认同)
  • 非常有用的技巧。我已经把它放在一个要点中,以防有人需要它作为一个快速脚本 https://gist.github.com/PiDayDev/68c39b305ab9d61ed8bb2a1195ee1afc (2认同)
  • 这似乎并没有保留作者身份(根本)。在某些情况下这并不重要,但在其他情况下却很重要。 (2认同)
  • 对于那些尝试学习的 git 初学者来说:当您输入 `gitcherry-pick` 命令时,您需要位于 *目标分支* 上,而 `&lt;commit&gt;` 是 * 的短或长哈希值源代码提交* - 因为我知道几年前我有很多“我需要在哪里才能做到这一点?” git 的一些问题。另外,如果您想在提交消息中添加有关此文件已被挑选的注释,请使用“gitcherry-pick-xn”而不是仅使用“-n”。 (2认同)

Mic*_*son 124

其他方法对我不起作用,因为提交对很多其他文件有很多更改和冲突.我想出的只是简单

git show SHA -- file1.txt file2.txt | git apply -
Run Code Online (Sandbox Code Playgroud)

它实际上不是add文件或为您提交,因此您可能需要跟进它

git add file1.txt file2.txt
git commit -c SHA
Run Code Online (Sandbox Code Playgroud)

或者如果你想跳过添加,你可以使用--cached参数来git apply

git show SHA -- file1.txt file2.txt | git apply --cached -
Run Code Online (Sandbox Code Playgroud)

  • 刚刚找到了另一个很好的用途:选择性还原,当你只想恢复一个文件时(因为`git revert`撤消整个提交).在这种情况下,只需使用`git show -R SHA - file1.txt file2.txt | git apply -` (8认同)
  • 如果你必须解决冲突,使用`git apply -3 -`而不仅仅是`git apply -`,那么如果发生冲突,你可以使用标准的冲突解决技术,包括使用`git mergetool`. (5认同)
  • 不,`checkout SHA - file`将完全检查SHA的版本,而`show SHA - file | apply`将仅应用SHA中的更改(就像cherry-pick一样).重要的是,(a)是否有多​​个提交更改源分支中的给定文件,或者(b)有一个提交更改当前目标分支中的文件. (4认同)
  • 有趣的方法,谢谢.但是不显示SHA - 文件| apply`基本上和`checkout SHA-file`一样,如[Mark Longair的回答](http://stackoverflow.com/a/5717140/321973)? (2认同)
  • @RoeiBahumi有着不同的含义.`git diff SHA - file1.txt file2.txt | git apply -`表示将当前版本的文件和SHA版本之间的所有差异应用于当前版本.本质上它与`git checkout SHA - file1.txt file2.txt`相同.请参阅我之前的评论,了解为什么这与`git show`版本不同. (2认同)
  • 这以最直接的方式回答了OP的问题。谢谢! (2认同)
  • 请注意,在提交之前,您必须执行“git add”精选文件。我使用 --cached 来 git apply 绕过它,确保您已将文件添加到索引中。 (2认同)
  • 我觉得这是解决问题最干净的方法。然而,git 配置设置可能会干扰这一点。我必须将参数 `--src-prefix=a/`、`--dst-prefix=b/` 和 `--no-color` 添加到 `git show` 才能正常工作。否则,“git apply”无法解释输入(其中包含颜色转义代码)。可能,这只是因为我使用了有点奇怪的 git 配置。(我正在使用 `diff.noprefix=true` 和 `color.ui=always`)。 (2认同)
  • @Chestera我会查看“|”符号之前命令部分的输出,看看它作为差异是否有意义。 (2认同)

Tyr*_*son 73

我通常使用-p带有来自另一个分支的git checkout 的标志,我发现它比我遇到的大多数其他方法更容易和更细化.

原则上:

git checkout <other_branch_name> <files/to/grab in/list/separated/by/spaces> -p
Run Code Online (Sandbox Code Playgroud)

例:

git checkout mybranch config/important.yml app/models/important.rb -p
Run Code Online (Sandbox Code Playgroud)

然后,您会看到一个对话框,询问您在"blob"中需要哪些更改,这几乎可以解决每一段连续代码更改,然后您可以为每个代码块发出信号y(是)n(否)等信号.

-ppatch选项可用于各种Git中包括的命令git stash save -p,它允许你选择你想从你目前的工作来隐藏什么

我有时会使用这种技术,当我做了很多工作并希望将其分离并提交更多基于主题的提交时使用git add -p并选择我想要的每个提交:)

  • 我经常使用`git-add -p`,但我不知道`git-checkout`也有一个`-p`标志 - 这样可以解决合并问题[非-p`答案](https: //stackoverflow.com/a/20376394/3219739)有吗? (3认同)
  • 不会杀死分支的并发更改的两个最佳答案之一。 (2认同)

Mar*_*air 42

也许这种方法优于Jefromi的答案的优势在于你不必记住git reset的哪种行为是正确的:)

 # Create a branch to throw away, on which we'll do the cherry-pick:
 git checkout -b to-discard

 # Do the cherry-pick:
 git cherry-pick stuff

 # Switch back to the branch you were previously on:
 git checkout -

 # Update the working tree and the index with the versions of A and B
 # from the to-discard branch:
 git checkout to-discard -- A B

 # Commit those changes:
 git commit -m "Cherry-picked changes to A and B from [stuff]"

 # Delete the temporary branch:
 git branch -D to-discard
Run Code Online (Sandbox Code Playgroud)

  • @Tobias:只有在`stuff`上修改过的文件没有在你当前的分支上修改过,或者在`HEAD`和`stuff`的共同祖先和`stuff`的尖端之间的任何地方都有效.如果他们有,那么`cherry-pick`会创建正确的结果(实际上是合并的结果),而你的方法会抛弃当前分支的变化,并保持从共同祖先的所有变化到`stuff ` - 不仅仅是那次提交中的那些. (8认同)
  • 感谢您的回答.现在这激发了我思考,为什么不跳过`cherry-pick`并直接使用`git checkout stuff-AB`?使用`git commit -C stuff`,提交消息也将保持不变 (2认同)
  • @Tobias Kienzler:我假设你的起点与`stuff`的父亲有很大的不同,樱桃选择的结果会留下`A`和'B`,其内容与提交`stuff`中的内容不同.但是,如果它只是一样,你是对的 - 你可以像你说的那样做. (2认同)

cmi*_*tti 27

Cherry pick是从特定的"提交"中选择更改.最简单的解决方案是选择要使用的某些文件的所有更改

 git checkout source_branch <paths>...
Run Code Online (Sandbox Code Playgroud)

例如:

$ git branch
* master
  twitter_integration
$ git checkout twitter_integration app/models/avatar.rb db/migrate/20090223104419_create_avatars.rb test/unit/models/avatar_test.rb test/functional/models/avatar_test.rb
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   app/models/avatar.rb
#   new file:   db/migrate/20090223104419_create_avatars.rb
#   new file:   test/functional/models/avatar_test.rb
#   new file:   test/unit/models/avatar_test.rb
#
$ git commit -m "'Merge' avatar code from 'twitter_integration' branch"
[master]: created 4d3e37b: "'Merge' avatar code from 'twitter_integration' branch"
4 files changed, 72 insertions(+), 0 deletions(-)
create mode 100644 app/models/avatar.rb
create mode 100644 db/migrate/20090223104419_create_avatars.rb
create mode 100644 test/functional/models/avatar_test.rb
create mode 100644 test/unit/models/avatar_test.rb
Run Code Online (Sandbox Code Playgroud)

来源和完整解释http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/

更新:

使用此方法,git将不会合并文件,它将覆盖目标分支上执行的任何其他更改.您需要手动合并更改:

$ git diff HEAD文件名

  • [我也这么认为](http://stackoverflow.com/questions/5717026/how-to-git-cherry-pick-only-changes-to-certain-files/20376394?noredirect=1#comment6537524_5717140),但是这个如果文件在_both_分支上发生了变化,则会失败,因为它会丢弃当前分支的更改 (5认同)

eli*_*stg 22

有时,使用签出从提交中获取特定文件可能会更容易。在我看来,它给了你更多的控制权。

我会这样做:

git checkout <branch|hash> -- path/to/file1 path/to/filen
Run Code Online (Sandbox Code Playgroud)

然后取消暂存必要的更改以适应代码并在提交之前对其进行测试。如果一切按预期进行,则提交。


kub*_*zyk 14

为了完整起见,最适合我的是:

git show YOURHASH --no-color -- file1.txt file2.txt dir3 dir4 | git apply -3 --index -
Run Code Online (Sandbox Code Playgroud)

它正是 OP 想要的。它在需要时进行冲突解决,类似如何解决merge。它确实add但不是commit您的新更改,请参阅status

  • 那好美丽 (4认同)
  • `-3` 表示使用“3 路合并回退”。这实际上意味着 git 可以插入熟悉的冲突标记,或者可以绕过已经应用的更改。默认的“git apply”行为对我来说太严格了,它拒绝应用整个补丁,哪怕有一点点差异。 (4认同)

fun*_*oll 11

我会挑选一切,然后这样做:

git reset --soft HEAD^
Run Code Online (Sandbox Code Playgroud)

然后我会恢复我不想要的更改,然后进行新的提交.


小智 11

使用git merge --squash branch_name此功能将从其他分支获取所有更改,并为您准备提交.现在删除所有不需要的更改并保留所需的更改.并且git不会知道有合并.


tec*_*ams 11

情况:

你在你的分支上,让我们说master你在任何其他分支机构都有你的承诺.您必须从该特定提交中仅选择一个文件.

该方法:

第1步:签出所需的分支机构.

git checkout master
Run Code Online (Sandbox Code Playgroud)

第2步:确保已复制所需的提交哈希.

git checkout commit_hash path\to\file
Run Code Online (Sandbox Code Playgroud)

第3步:您现在可以在所需分支上更改所需文件.您只需要添加并提交它们.

git add path\to\file
git commit -m "Your commit message"
Run Code Online (Sandbox Code Playgroud)

  • 覆盖对目标分支所做的任何更改。 (3认同)
  • 惊人的!还适用于我的 \path\to\directory\ 目录中的所有更改 (2认同)

小智 7

我找到了另一种方法,可以防止在挑选樱桃时发生任何冲突的合并,IMO 有点容易记住和理解。由于您实际上不是挑选提交,而是挑选其中的一部分,因此您需要先拆分它,然后创建一个适合您需求的提交并挑选它。

首先从要拆分的提交创建一个分支并签出它:

$ git checkout COMMIT-TO-SPLIT-SHA -b temp
Run Code Online (Sandbox Code Playgroud)

然后恢复之前的提交:

$ git reset HEAD~1
Run Code Online (Sandbox Code Playgroud)

然后添加您想要挑选的文件/更改:

$ git add FILE
Run Code Online (Sandbox Code Playgroud)

并提交它:

$ git commit -m "pick me"
Run Code Online (Sandbox Code Playgroud)

注意提交哈希,我们称之为 PICK-SHA 并返回到你的主分支,例如强制结帐:

$ git checkout -f master
Run Code Online (Sandbox Code Playgroud)

并挑选提交:

$ git cherry-pick PICK-SHA
Run Code Online (Sandbox Code Playgroud)

现在您可以删除临时分支:

$ git branch -d temp -f
Run Code Online (Sandbox Code Playgroud)


Gar*_*lun 5

您可以使用:

git diff <commit>^ <commit> -- <path> | git apply
Run Code Online (Sandbox Code Playgroud)

该符号<commit>^指定 的(第一个)父级<commit><path>因此,此 diff 命令选择在 commit 中所做的更改<commit>

请注意,这还不会提交任何内容(确实如此git cherry-pick)。所以如果你想要这样,你必须这样做:

git add <path>
git commit
Run Code Online (Sandbox Code Playgroud)