如何git樱桃选择只更改某些文件?

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

假设名为stuff的Git提交对文件ABCD进行了更改,但我只想合并stuff对文件AB的更改。这听起来像是git cherry-pick的工作,但cherry-pick只知道如何合并整个提交,而不是文件的子集。

386424 次浏览

也许这种方法相对于Jefromi的回答的优势在于,您不必记住git重置的哪个行为是正确的:)

 # 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 -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-pickgit commit

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

# unstage everythinggit reset HEAD
# stage the modifications you do wantgit add <path>
# make the work tree match the index# (do this from the top level of the repo)git checkout .

樱桃选择是从特定的“提交”中选择更改。最简单的解决方案是选择某些文件的所有更改

 git checkout source_branch <paths>...

例如:

$ git branch* mastertwitter_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.rbcreate mode 100644 db/migrate/20090223104419_create_avatars.rbcreate mode 100644 test/functional/models/avatar_test.rbcreate 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 reset --soft HEAD^

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

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

git show SHA -- file1.txt file2.txt | git apply -

它实际上并不add文件或为你做一个提交,所以你可能需要跟进它

git add file1.txt file2.txtgit commit -c SHA

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

git show SHA -- file1.txt file2.txt | git apply --cached -

您也可以对整个目录执行相同的操作

git show SHA -- dir1 dir2 | git apply -

我通常使用-p标志和来自另一个分支的git签出,我发现它比我遇到的大多数其他方法更容易和更细粒度。

原则上:

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(否)等信号。

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

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

我发现了另一种方法,可以防止任何冲突的合并,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 merge --squash branch_name,这将从另一个分支获取所有更改,并为您准备提交。现在删除所有不需要的更改并保留您想要的更改。git不会知道有合并。

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

git checkout mastergit 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

情况:

您在您的分支上,假设master并且您在任何其他分支上都有您的提交。您必须从该特定提交中只选择一个文件。

方法:

步骤1:在所需分支上签出。

git checkout master

步骤2:确保您已复制所需的提交哈希。

git checkout commit_hash path\to\file

步骤3:您现在在所需的分支上拥有所需文件的更改。您只需要添加并提交它们。

git add path\to\filegit commit -m "Your commit message"

您可以使用:

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

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

请注意,这还不会提交任何东西(正如git cherry-pick所做的那样)。所以如果你想要,你必须这样做:

git add <path>git commit

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

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

它做的正是OP想要的。它在需要时解决冲突,类似于merge的做法。它做了add但不是commit你的新更改,参见status

有时使用结帐从提交中获取特定文件可能更容易。在我看来,它为您提供了更多的控制权,并且没有必要在樱桃采摘后检查和取消暂存。

我会这样做:

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

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

自动化多一点:

#!/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中来定义它。你不需要这里的文件

使用补丁标志结帐

完成您所描述的一个简单方法是通过签出--patch。修改我在顶部提供的四个变量。脚本执行以下操作:

  1. 克隆包含您的提交的分支(源分支)
  2. 签出要将文件移动到的分支(目标分支)
  3. 在所需的提交修订时签出源分支,提供补丁标志和文件的相对位置作为参数。
  4. 添加,提交,推送

这对我来说一直是最简单的方法。步骤#3确实创建了一个交互式shell,但如果您想破坏目标,您可以默认选择整个选项。

source_branch=branch_with_file_you_wantdestination_branch=file_go_hererev=757c47d4relative_file_path=structures/serializers/node.pymessage="Patching a file from $source_branch to $destination_branch"
git clone https://github.com/yourepo/app.git -b $source_branch $source_branchcd $source_branchgit checkout $destination_branchgit checkout $rev --patch $source_branch $relative_file_pathgit add $relative_file_pathgit commit -m "$message"git push

如果你是远程的GitLab,如果你需要MR,你可以使用git push -o merge_request.create