735

如果我想将仅对特定提交中更改的某些文件所做的更改合并到 Git 分支中,其中包括对多个文件的更改,如何实现?

假设调用的 Git 提交stuff对文件A, B,进行了更改CD但我只想合并stuff对文件AB. 这听起来像是一项工作,git cherry-pickcherry-pick只知道如何合并整个提交,而不是文件的子集。

4

14 回答 14

867

我会用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 .
于 2011-04-19T14:04:56.887 回答
198

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

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 -
于 2015-04-22T05:12:34.027 回答
132

我通常将-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(否)等。

-porpatch选项适用于 git 中的各种命令,包括允许git stash save -p您从当前工作中选择要存储的内容

当我完成大量工作并希望将其分离出来并提交更多基于主题的提交时,我有时会使用此技术,并使用git add -p并为每个提交选择我想要的内容:)

于 2016-03-02T09:14:09.710 回答
47

与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
于 2011-04-19T13:30:18.907 回答
38

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 文件名

于 2013-12-04T13:16:05.120 回答
33

情况:

假设您在您的分支上,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"
于 2018-08-07T05:45:01.323 回答
19

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

git show YOURHASH --no-color -- file1.txt file2.txt dir3 dir4 | git apply -3 --index -

它完全符合 OP 的要求。它会在需要时解决冲突,类似地如何解决merge。它会add但不是commit您的新更改,请参阅status

于 2019-10-25T10:01:02.883 回答
16

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

git reset --soft HEAD^

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

于 2014-08-11T21:20:09.863 回答
11

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

于 2017-10-30T17:02:36.150 回答
7

我找到了另一种方法,可以防止在挑选樱桃时发生任何冲突合并,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
于 2017-02-15T21:57:41.593 回答
4

有时,使用checkout从提交中提取特定文件可能会更容易。在我看来,它为您提供了更多的控制权,并且没有必要在挑选之后进行检查和取消分级。

我会这样做:

git checkout <branch|hash> -- path/to/file1 path/to/filen

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

于 2021-11-20T19:49:24.843 回答
3

您可以使用:

git diff <commit>^ <commit> -- <path> | git apply

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

请注意,这不会提交任何东西(就像git cherry-pick那样)。因此,如果您想要这样做,则必须这样做:

git add <path>
git commit
于 2019-09-17T13:00:25.397 回答
2

将分支合并到新分支(壁球)并删除不需要的文件:

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
于 2018-04-11T14:04:16.513 回答
0

自动化一点:

#!/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 中来定义它。您不需要此处的文档

于 2022-03-03T22:27:37.910 回答