如何 git-cherry-pick 仅更改某些文件?
Posted
技术标签:
【中文标题】如何 git-cherry-pick 仅更改某些文件?【英文标题】:How to git-cherry-pick only changes to certain files? 【发布时间】:2011-08-08 16:34:00 【问题描述】:如果我想将仅对特定提交中更改的某些文件所做的更改合并到 Git 分支中,其中包括对多个文件的更改,如何实现?
假设名为stuff
的Git 提交对文件A
、B
、C
和D
进行了更改,但我只想合并stuff
对文件A
和@ 的更改987654328@。这听起来像是 git cherry-pick
的工作,但 cherry-pick
只知道如何合并整个提交,而不是文件的子集。
【问题讨论】:
【参考方案1】:我会使用 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
如果绝大多数修改是您不想要的,那么您可以重新设置所有内容,而不是检查单个路径(中间步骤),然后添加您想要的内容:
# 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 .
【讨论】:
除了git checkout .
,我还建议git clean -f
删除任何新的但不需要的文件,这些文件是由精心挑选的提交引入的。
后一种方法的附加说明:我使用git add -p
,它可以让您以交互方式决定要添加到索引中的更改每个文件
这不是很好的情况下,樱桃挑选的提交不适用于当前的工作副本,因为它是如此不同,但 one 文件 将干净地应用。
您也可以使用git reset -p HEAD
选择性地取消暂存。它相当于add -p
,但很少有人知道它的存在。
非常有用的技巧。我把它放在一个要点中,以防有人需要它作为一个快速脚本gist.github.com/PiDayDev/68c39b305ab9d61ed8bb2a1195ee1afc【参考方案2】:
其他方法对我不起作用,因为提交对很多其他文件有很多更改和冲突。我想出的只是
git show SHA -- file1.txt file2.txt | git apply -
它实际上并没有 add
文件或为你做一个提交,所以你可能需要跟进它
git add file1.txt file2.txt
git commit -c SHA
或者,如果您想跳过添加,您可以使用 --cached
参数到 git apply
git show SHA -- file1.txt file2.txt | git apply --cached -
你也可以对整个目录做同样的事情
git show SHA -- dir1 dir2 | git apply -
【讨论】:
有趣的方法,谢谢。但是show SHA -- file | apply
与checkout SHA -- file
的作用不一样吗?就像Mark Longair's answer 一样?
不,checkout SHA -- file
将准确检查 SHA 中的版本,而 show SHA -- file | apply
将仅应用 SHA 中的更改(就像 cherry-pick 一样)。如果 (a) 有多个提交更改源分支中的给定文件,或者 (b) 有一个提交更改了当前目标分支中的文件,这很重要。
刚刚发现了另一个很好的用途:选择性还原,当您只想还原一个文件时(因为git revert
撤消了整个提交)。在这种情况下,只需使用git show -R SHA -- file1.txt file2.txt | git apply -
@RoeiBahumi 有完全不同的含义。 git diff SHA -- file1.txt file2.txt | git apply -
表示将文件的当前版本与 SHA 上的版本之间的所有差异应用到当前版本。本质上它与git checkout SHA -- file1.txt file2.txt
相同。请参阅我之前的评论,了解为什么这与 git show
版本不同。
如果您必须解决冲突,请使用git apply -3 -
而不是仅使用git apply -
,然后如果发生冲突,您可以使用标准的冲突解决技术,包括使用git mergetool
。跨度>
【参考方案3】:
我通常将 -p
标志与来自另一个分支的 git checkout 一起使用,我发现它比我遇到的大多数其他方法更容易和更细化。
原则上:
git checkout <other_branch_name> <files/to/grab in/list/separated/by/spaces> -p
示例:
git checkout mybranch config/important.yml app/models/important.rb -p
然后您会看到一个对话框,询问您想要在“blob”中进行哪些更改,这几乎适用于连续代码更改的每一块,然后您可以为每个代码发送 y
(是)n
(否)等代码块。
-p
或 patch
选项适用于 git 中的各种命令,包括 git stash save -p
,它允许您从当前工作中选择要存储的内容
当我完成大量工作并希望将其分离出来并使用git add -p
提交更多基于主题的提交并为每个提交选择我想要的内容时,我有时会使用此技术:)
【讨论】:
我经常使用git-add -p
,但我不知道git-checkout
也有-p
标志 - 这是否解决了the non--p
answer 的合并问题?
至少-p
将允许手动编辑这样一个冲突的部分,cherry-pick
无论如何也可能会产生。下次需要时我会测试它,绝对是一种有趣的方法
不会杀死对分支的并发更改的两个最佳答案之一。
查看这个答案以了解如何选择要应用的帅哥:***.com/a/10605465/4816250 's' 选项特别有用。
git reset -p HEAD
还允许使用-p
,当您只想从索引中删除一些补丁时,可以方便地退出。【参考方案4】:
也许这种方法相对于Jefromi's answer 的优势在于您不必记住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
【讨论】:
感谢您的回答。现在这启发了我思考,为什么不跳过cherry-pick
,直接使用git checkout stuff -- A B
?对于git commit -C stuff
,提交消息也将保持不变
@Tobias:只有在 stuff
上修改的文件尚未在您当前的分支或 HEAD
和 stuff
的共同祖先与 @ 的尖端之间的任何地方修改时,这才有效987654331@。如果有,那么 cherry-pick
会创建正确的结果(本质上是合并的结果),而您的方法会丢弃当前分支中的更改,并保留从共同祖先到 stuff
的所有更改 -不仅仅是那个单一提交中的那些。
@Tobias Kienzler:我假设您的起点与stuff
的父级有很大不同,因此樱桃采摘的结果将使A
和B
的内容与他们的不同提交 stuff
中的内容。但是,如果它只是一样,你是对的 - 你可以照你说的做。
@Jeromi,@Mark:感谢您的反馈,在我的情况下,我正在使用完全不相干的文件处理分支,这导致了我的建议。但事实上我迟早会遇到麻烦,所以谢谢你提出这个问题
我认为my answer in this other thread 可能是你所追求的。【参考方案5】:
Cherry pick 是从特定的“提交”中挑选更改。最简单的解决方案是选择某些文件的所有更改是使用
git checkout source_branch <paths>...
例如:
$ 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
来源和完整解释http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/
更新:
使用这种方法,git 不会合并文件,它只会覆盖在目标分支上所做的任何其他更改。您需要手动合并更改:
$ git diff HEAD 文件名
【讨论】:
I thought so too,但是如果文件在 两个 分支上都发生了更改,这将非常失败,因为它会丢弃当前分支的更改 你是对的,必须澄清这种方式 git 不会合并,它只是覆盖。然后,您可以执行“git diff HEAD filename”以查看更改的内容并手动进行合并。【参考方案6】:情况:
你在你的分支上,比如说master
,你在任何其他分支上都有你的提交。您只需从该特定提交中选择一个文件。
方法:
第 1 步:在所需的分支上结帐。
git checkout master
第 2 步:确保您已复制所需的提交哈希。
git checkout commit_hash path\to\file
第 3 步:您现在对所需分支上的所需文件进行了更改。您只需要添加并提交它们。
git add path\to\file
git commit -m "Your commit message"
【讨论】:
太棒了!对于我来说,也适用于 \path\to\directory\ 目录中的所有更改 非常感谢!这比cherry-pick简单得多 覆盖对目标分支所做的任何更改。 迄今为止最好的解决方案!!【参考方案7】:为了完整起见,最适合我的是:
git show YOURHASH --no-color -- file1.txt file2.txt dir3 dir4 | git apply -3 --index -
它完全符合 OP 的要求。它会在需要时解决冲突,类似于merge
的做法。它确实 add
但不是 commit
您的新更改,请参阅 status
。
【讨论】:
真漂亮 谢谢!你能提一下3
的用途吗(... git apply -3 --index ...
)?
-3
表示使用“三向合并后备”。这实际上意味着 git 可以插入熟悉的冲突标记,也可以绕过已经应用的更改。默认的git apply
行为对我来说太严格了,它拒绝应用整个补丁,只要有一点点差异。
你救了我。谢谢。【参考方案8】:
我会挑选一切,然后这样做:
git reset --soft HEAD^
然后我会还原我不想要的更改,然后进行新的提交。
【讨论】:
【参考方案9】:使用git merge --squash branch_name
这将从其他分支获取所有更改,并为您准备提交。
现在删除所有不需要的更改并保留您想要的更改。并且 git 不会知道有合并。
【讨论】:
谢谢,我不知道合并选项。如果您想挑选整个分支的大部分,这是一个可行的选择(但与樱桃挑选相比,如果没有共同的祖先,它就行不通)【参考方案10】:我找到了另一种方法,可以防止在挑选樱桃时出现任何冲突合并,IMO 很容易记住和理解。由于您实际上不是在挑选提交,而是其中的一部分,因此您需要先拆分它,然后创建一个适合您需求的提交并挑选它。
首先从要拆分的提交中创建一个分支并签出它:
$ git checkout COMMIT-TO-SPLIT-SHA -b temp
然后恢复之前的提交:
$ git reset HEAD~1
然后添加您想要挑选的文件/更改:
$ git add FILE
并提交:
$ git commit -m "pick me"
注意提交哈希,我们称之为 PICK-SHA 并回到你的主分支,例如 master 强制结帐:
$ git checkout -f master
然后挑选提交:
$ git cherry-pick PICK-SHA
现在你可以删除临时分支了:
$ git branch -d temp -f
【讨论】:
【参考方案11】:你可以使用:
git diff <commit>^ <commit> -- <path> | git apply
符号<commit>^
指定<commit>
的(第一个)父级。因此,这个 diff 命令选择在提交 <commit>
中对 <path>
所做的更改。
请注意,这不会提交任何内容(就像 git cherry-pick
所做的那样)。因此,如果您想这样做,则必须这样做:
git add <path>
git commit
【讨论】:
这样您可以指定任何有效范围,如果结果不适用,您可以选择使用-3
或--3way
进行三向合并开关:... | git apply --3way
【参考方案12】:
将一个分支合并到新分支(壁球)并删除不需要的文件:
git checkout master
git checkout -b <branch>
git merge --squash <source-branch-with-many-commits>
git reset HEAD <not-needed-file-1>
git checkout -- <not-needed-file-1>
git reset HEAD <not-needed-file-2>
git checkout -- <not-needed-file-2>
git commit
【讨论】:
【参考方案13】:有时使用 checkout 从提交中提取特定文件可能更容易。在我看来,它为您提供了更多的控制权,并且没有必要在挑选后检查和取消暂存。
我会这样做:
git checkout <branch|hash> -- path/to/file1 path/to/filen
然后取消暂存必要的更改以适应代码并在提交之前对其进行测试。如果一切都按预期工作,则提交。
【讨论】:
与多个现有答案相同。以上是关于如何 git-cherry-pick 仅更改某些文件?的主要内容,如果未能解决你的问题,请参考以下文章
在 JS/JQuery 中按 ENTER 键之前,如何仅捕获您在文本区域中输入的行? [复制]
如何在 C# 应用程序范围内更改仅日期部分将用于日期时间比较