如果我想将仅对特定提交中更改的某些文件所做的更改合并到 Git 分支中,其中包括对多个文件的更改,如何实现?
假设名为 stuff
的 Git 提交对文件 A
、B
、C
和 D
进行了更改,但我只想合并 stuff
对文件 A
和 B
的更改。这听起来像是 git cherry-pick
的工作,但 cherry-pick
只知道如何合并整个提交,而不是文件的子集。
我会使用 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 show SHA -- file1.txt file2.txt | git apply -
它实际上并没有add
文件或为您执行提交,因此您可能需要跟进
git add file1.txt file2.txt
git commit -c SHA
或者,如果您想跳过添加,您可以使用 git apply
的 --cached
参数
git show SHA -- file1.txt file2.txt | git apply --cached -
你也可以对整个目录做同样的事情
git show SHA -- dir1 dir2 | git apply -
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 -
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
。
我通常将 -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”中进行哪些更改.
-p
或 patch
选项适用于 git 中的各种命令,包括 git stash save -p
,它允许您从当前工作中选择要存储的内容
当我完成大量工作并希望将其分离出来并使用 git add -p
提交更多基于主题的提交并为每个提交选择我想要的内容时,我有时会使用此技术 :)
-p
将允许对此类冲突部分进行手动编辑,而 cherry-pick
无论如何也可能会产生。下次需要时我会测试它,绝对是一种有趣的方法
git reset -p HEAD
还允许 -p
当您只想从索引中删除一些补丁时可以方便地退出。
也许这种方法相对于 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
提交消息也将保持不变
stuff
上修改的文件尚未在当前分支上或在 HEAD
和 stuff
的共同祖先与 stuff
的尖端之间的任何地方修改时,这才有效。如果有,则 cherry-pick
创建正确的结果(本质上是合并的结果),而您的方法将丢弃当前分支中的更改,并保留从共同祖先到 stuff
的所有更改 -不仅仅是那个单一提交中的那些。
stuff
的父级有很大不同,因此樱桃采摘的结果会使 A
和 B
的内容与提交 stuff
中的内容不同。然而,如果它只是一样,你是对的——你可以照你说的做。
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 文件名
git checkout e0032947bd37f44044962ae2f7229d339944f83c example.txt
情况:
您在您的分支上,假设 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"
为了完整起见,最适合我的是:
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
行为对我来说太严格了,它拒绝在最轻微的差异上应用整个补丁。
我会挑选一切,然后这样做:
git reset --soft HEAD^
然后我会恢复我不想要的更改,然后进行新的提交。
使用 git merge --squash branch_name
这将从其他分支获取所有更改,并为您准备提交。现在删除所有不需要的更改并保留您想要的更改。并且 git 不会知道有合并。
有时,使用 checkout 从提交中提取特定文件可能会更容易。在我看来,它为您提供了更多的控制权,并且没有必要在挑选之后进行检查和取消分级。
我会这样做:
git checkout <branch|hash> -- path/to/file1 path/to/filen
然后取消暂存必要的更改以适应代码并在提交之前对其进行测试。如果一切都按预期工作,则提交。
我找到了另一种方法,可以防止在挑选樱桃时发生任何冲突合并,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
您可以使用:
git diff <commit>^ <commit> -- <path> | git apply
符号 <commit>^
指定 <commit>
的(第一个)父级。因此,此 diff 命令选择提交 <commit>
中对 <path>
所做的更改。
请注意,这不会提交任何内容(就像 git cherry-pick
所做的那样)。因此,如果您想要这样做,则必须这样做:
git add <path>
git commit
-3
或 --3way
开关进行 3 路合并:... | git apply --3way
将分支合并到新分支(壁球)并删除不需要的文件:
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
自动化一点:
#!/usr/bin/env bash
filter_commit_to_files() {
FILES="$1"
SHA="$2"
git show "$SHA" -- $FILES | git apply --index -
git commit -c "$SHA"
}
示例用法:
filter_commit_to_files "file1.txt file2.txt" 07271c5e
我通过从这里复制并粘贴到我的 shell 中来定义它。您不需要 here document。
不定期副业成功案例分享
git checkout .
,我还建议git clean -f
删除任何由精选提交引入的新但不需要的文件。git add -p
,它可以让您以交互方式决定要将哪些更改添加到 每个文件 的索引中git reset -p HEAD
有选择地取消暂存。它相当于add -p
,但很少有人知道它的存在。