[git] How to git-cherry-pick only changes to certain files?

If I want to merge into a Git branch the changes made only to some of the files changed in a particular commit which includes changes to multiple files, how can this be achieved?

Suppose the Git commit called stuff has changes to files A, B, C, and D but I want to merge only stuff's changes to files A and B. It sounds like a job for git cherry-pick but cherry-pick only knows how to merge entire commits, not a subset of the files.

This question is related to git github cherry-pick

The answer is


The situation:

You are on your branch, let's say master and you have your commit on any other branch. You have to pick only one file from that particular commit.

The approach:

Step 1: Checkout on the required branch.

git checkout master

Step 2: Make sure you have copied the required commit hash.

git checkout commit_hash path\to\file

Step 3: You now have the changes of the required file on your desired branch. You just need to add and commit them.

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

The other methods didn't work for me since the commit had a lot of changes and conflicts to a lot of other files. What I came up with was simply

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

It doesn't actually add the files or do a commit for you so you may need to follow it up with

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

Or if you want to skip the add you can use the --cached argument to git apply

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

You can also do the same thing for entire directories

git show SHA -- dir1 dir2 | git apply -

Perhaps the advantage of this method over Jefromi's answer is that you don't have to remember which behaviour of git reset is the right one :)

 # 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

I usually use the -p flag with a git checkout from the other branch which I find easier and more granular than most other methods I have come across.

In principle:

git checkout <other_branch_name> <files/to/grab in/list/separated/by/spaces> -p

example:

git checkout mybranch config/important.yml app/models/important.rb -p

You then get a dialog asking you which changes you want in "blobs" this pretty much works out to every chunk of continuous code change which you can then signal y (Yes) n (No) etc for each chunk of code.

The -p or patch option works for a variety of commands in git including git stash save -p which allows you to choose what you want to stash from your current work

I sometimes use this technique when I have done a lot of work and would like to separate it out and commit in more topic based commits using git add -p and choosing what I want for each commit :)


For the sake of completness, what works best for me is:

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

It does exactly what OP wants. It does conflict resolution when needed, similarly how merge does it. It does add but not commit your new changes, see with status.


I found another way which prevents any conflicting merge on cherry-picking which IMO is kind of easy to remember and understand. Since you are actually not cherry-picking a commit, but part of it, you need to split it first and then create a commit which will suit your needs and cherry-pick it.

First create a branch from the commit you want to split and checkout it:

$ git checkout COMMIT-TO-SPLIT-SHA -b temp

Then revert previous commit:

$ git reset HEAD~1

Then add the files/changes you want to cherry-pick:

$ git add FILE

and commit it:

$ git commit -m "pick me"

note the commit hash, let's call it PICK-SHA and go back to your main branch, master for example forcing the checkout:

$ git checkout -f master

and cherry-pick the commit:

$ git cherry-pick PICK-SHA

now you can delete the temp branch:

$ git branch -d temp -f

Use git merge --squash branch_name this will get all changes from the other branch and will prepare a commit for you. Now remove all unneeded changes and leave the one you want. And git will not know that there was a merge.


Cherry pick is to pick changes from a specific "commit". The simplest solution is to pick all changes of certain files is to use

 git checkout source_branch <paths>...

In example:

$ 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

Sources and full explanation http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/

UPDATE:

With this method, git will not MERGE the file, it will just override any other change done on the destination branch. You will need to merge the changes manually:

$ git diff HEAD filename


You can use:

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

The notation <commit>^ specifies the (first) parent of <commit>. Hence, this diff command picks the changes made to <path> in the commit <commit>.

Note that this won't commit anything yet (as git cherry-pick does). So if you want that, you'll have to do:

git add <path>
git commit

I would just cherry-pick everything, then do this:

git reset --soft HEAD^

Then I would revert the changes I don't want, then make a new commit.


Merge a branch into new one (squash) and remove the files not needed:

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

Examples related to git

Does the target directory for a git clone have to match the repo name? Git fatal: protocol 'https' is not supported Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) git clone: Authentication failed for <URL> destination path already exists and is not an empty directory SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443 GitLab remote: HTTP Basic: Access denied and fatal Authentication How can I switch to another branch in git? VS 2017 Git Local Commit DB.lock error on every commit How to remove an unpushed outgoing commit in Visual Studio?

Examples related to github

Does the target directory for a git clone have to match the repo name? Issue in installing php7.2-mcrypt How can I switch to another branch in git? How to draw checkbox or tick mark in GitHub Markdown table? How to add a new project to Github using VS Code git clone error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054 How to add empty spaces into MD markdown readme on GitHub? key_load_public: invalid format git - remote add origin vs remote set-url origin Cloning specific branch

Examples related to cherry-pick

Git Cherry-Pick and Conflicts What does cherry-picking a commit with Git mean? git cherry-pick says "...38c74d is a merge but no -m option was given" How to git-cherry-pick only changes to certain files? How to cherry pick from 1 branch to another Is it possible to cherry-pick a commit from another git repository? Remove specific commit How to cherry-pick multiple commits Git Cherry-pick vs Merge Workflow