The cherry-pick
command in git allows you to copy commits from one branch to another, one commit at a time. In order to copy more than one commit at once, you need a different approach.
Cherry-picking a single commit
Say we have the following repository composed of three branches (master
, feature1
and stable
):
$ git tree --all
* d9484311 (HEAD, master) Delete test file
* 4d4a0da8 Add a test file
| * 5753515c (stable) Add a license
| * 4b95278e Add readme file
|/
| * a37658bd (feature1) Add fourth file
| * a7785c10 Add lines to 3rd file
| * 7f545188 Add third file
| * 2bca593b Add line to second file
| * 0c13e436 Add second file
|/
* d3199755 Add a line
* b58d925c Initial commit
The "git tree" command is an alias I defined in my ~/.gitconfig
:
[alias]
tree = log --oneline --decorate --graph
To copy the license file (commit 5753515c
) to the master
branch then we simply need to run:
$ git checkout master
$ git cherry-pick 5753515c
Finished one cherry-pick.
[master 08ff7d4] Add a license
1 files changed, 676 insertions(+), 0 deletions(-)
create mode 100644 COPYING
and the repository now looks like this:
$ git tree --all * 08ff7d4a4 (HEAD, master) Add a license * d94843113 Delete test file * 4d4a0da88 Add a test file | * 5753515c (stable) Add a license | * 4b95278e Add readme file |/ | * a37658bd (feature1) Add fourth file | * a7785c10 Add lines to 3rd file | * 7f545188 Add third file | * 2bca593b Add line to second file | * 0c13e436 Add second file |/ * d3199755 Add a line * b58d925c Initial commit
Cherry-picking a range of commits
In order to only take the third file (commits a7785c10
and 7f545188
) from the feature1
branch and add it to the stable
branch, I could cherry-pick each commit separately, but there is a faster way if you need to cherry-pick a large range of commits.
First of all, let's create a new branch which ends on the last commit we want to cherry-pick:
$ git branch tempbranch a7785c10 $ git tree --all * 08ff7d4a (HEAD, master) Add a license * d9484311 Delete test file * 4d4a0da8 Add a test file | * 5753515c (stable) Add a license | * 4b95278e Add readme file |/ | * a37658bd (feature1) Add fourth file | * a7785c10 (tempbranch) Add lines to 3rd file | * 7f545188 Add third file | * 2bca593b Add line to second file | * 0c13e436 Add second file |/ * d3199755 Add a line * b58d925c Initial commit
Now we'll rebase that temporary branch on top of the stable
branch:
$ git rebase --onto stable 7f545188^ tempbranch First, rewinding head to replay your work on top of it... Applying: Add third file Applying: Add lines to 3rd file $ git tree --all * ec488677 (HEAD, tempbranch) Add lines to 3rd file * a85e5281 Add third file * 5753515c (stable) Add a license * 4b95278e Add readme file | * 08ff7d4a (master) Add a license | * d9484311 Delete test file | * 4d4a0da8 Add a test file |/ | * a37658bd (feature1) Add fourth file | * a7785c10 Add lines to 3rd file | * 7f545188 Add third file | * 2bca593b Add line to second file | * 0c13e436 Add second file |/ * d3199755 Add a line * b58d925c Initial commit
All that's left to do is to make stable point to the top commit of tempbranch
and delete the old branch:
$ git checkout stable Switched to branch 'stable' $ git reset --hard tempbranch HEAD is now at ec48867 Add lines to 3rd file $ git tree --all * ec488677 (HEAD, tempbranch, stable) Add lines to 3rd file * a85e5281 Add third file * 5753515c Add a license * 4b95278e Add readme file | * 08ff7d4a (master) Add a license | * d9484311 Delete test file | * 4d4a0da8 Add a test file |/ | * a37658bd (feature1) Add fourth file | * a7785c10 Add lines to 3rd file | * 7f545188 Add third file | * 2bca593b Add line to second file | * 0c13e436 Add second file |/ * d3199755 Add a line * b58d925c Initial commit $ git branch -d tempbranch Deleted branch tempbranch (was ec48867).
It would be nice to be able to do it without having to use a temporary branch, but it still beats cherry-picking everything manually.
Another approach
Another way to achieve this is to use the format-patch
command to output patches for the commits you are interested in copying to another branch and then using the am
command to apply them all to the target branch:
$ git format-patch 7f545188^..a7785c10
0001-Add-third-file.patch
0002-Add-lines-to-3rd-file.patch
$ git am *.patch
Update: looking forward to git 1.7.2
According to a few people who were nice to point this out in a comment, version 1.7.2 of git, which is going to be released soon, will have support for this in cherry-pick
:
git cherry-pick 7f545188^..a7785c10
You have a mistake in your alias that got me confused at first: It's not "--pretty=oneline" but it's "--oneline" instead (or "--pretty=oneline --abbrev-commit"). Otherwise you'll get the full commit IDs which are rather unhandy.
Thanks for this helpful article, and even more for the useful alias.
Enjoy!
And with git v1.7.2 you'll be able to use:
git cherry-pick 7f545188^..a7785c10
For any older git:
git rev-list ^f545188 master --reverse | git cherry-pick --stdin