How to cherry-pick a single commit, multiple commits, or a range of commits
...onto your currently-checked-out branch:
1. to cherry-pick a single branch or commit named commit
git cherry-pick commit
Examples:
git cherry-pick my_branch # by branch name
git cherry-pick 1e038f108a130831f108329b1083a8139813fabc # by full hash
git cherry-pick 1e038f10 # by partial hash
2. to cherry-pick multiple commits
Note that you can cherry-pick any number of commit hashes at once, and in any order you want. They will simply be applied one-at-a-time, and in the order you specify. If any conflicts arise, you will have to resolve them one-at-a-time then use git add my_file
then git cherry-pick --continue
when done to continue the cherry-pick process.
git cherry-pick commit1 commit2 commit3 commit4 commit5
3. to cherry-pick a range of commits
I originally learned the basics of this style from the most-upvoted answer by @Eric Darchis here.
Notice that to cherry-pick a range of commits, you must specify a starting and ending commit hash, with ..
between them. However, in a range of commits, the beginning commit is NOT included. Therefore, to include it, you must specify the commit before the beginning commit. The syntax to specify the preceding commit is to put ~
, ~1
, or ^
right after your commit, as in: beginning_commit~
, which means: "the commit right before beginning_commit
".
# A. INCLUDING the beginning_commit
git cherry-pick beginning_commit~..ending_commit
# OR (same as above)
git cherry-pick beginning_commit~1..ending_commit
# OR (same as above)
git cherry-pick beginning_commit^..ending_commit
# B. NOT including the beginning_commit
git cherry-pick beginning_commit..ending_commit
Note: commit~
, commit~1
, and commit^
all mean "one commit prior to commit
", or otherwise said: "the commit before commit
".
To specify two commits prior to commit
, you can use syntax like this:
commit~~
commit~2 # my preferred syntax
commit^^
To specify three commits prior to commit
, you can do this:
commit~~~
commit~3 # my preferred syntax
commit^^^
This does NOT work:
commit^3 # INVALID syntax
This does work, but is a very special case. See also my Q&A: In a git merge
-style workflow, show only the unique commits someone had in their PR's (Pull Request's) feature branch before merging:
# valid on **merge commits** only
commit^2 # this is the immediate **right** parent of the two parents
# involved in the merge (which made commit `commit`)
# valid on any commits (gets the left parent of a merge commit, or
# the only parent of a non-merge commit)
commit~ # and this is the immediate **left** parent of the two
# parents involved in the merge (which made commit `commit`)
To test the above "previous commit syntax" concepts yourself, the easiest way is with the git log
command. Ex:
git log commit
git log commit~
git log commit~1
git log commit^
git log commit~~
git log commit~5
# etc.
4. To cherry-pick a range of your peer's commits onto your branch
...when their branch peer_branch
is forked off of an earlier version of your branch my_branch
.
Quick summary
# you cherry-pick all of their extra commits from their `peer_branch` onto
# your `my_branch` (note: the 3 dots below are very important!)
git fetch origin peer_branch # get their latest changes from the remote
git checkout my_branch # ensure you're on your branch
# cherry-pick their range of commits
git cherry-pick my_branch...origin/peer_branch
git log # review the commits you just chery-picked
git push # push your changes to the remote
The difference between 2 dots and 3 dots in a commit range is very significant. git diff branch1...branch2
is the equivalent of git diff $(git merge-base branch1 branch2) branch2
. This is useful when you want to see the changes that have been made in branch2
since it diverged from branch1
, rather than the differences between the two branches in their current states. See my comment here and here and the answers here: What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges?
Full details and work-flow walk-through
Let's say you are working on your feature branch my_branch
, and your peer wants to help make some changes for you to help you on your feature. You have already pushed my_branch
to the remote named origin
. So, they are going to fetch your remote branch named my_branch
to their local computer, fork their own branch named peer_brach
off of it, then push to their own branch named peer_branch
. Once they do that, you will cherry-pick all of their additions at once. This is what the first part of this process looks like:
# **your peer** does this
# peer fetches your branch named `my_branch` and forks their `peer_branch`
# off of it
# they fetch your latest work from remote `my_branch` into their locally-stored
# remote-tracking "hidden" branch named `origin/my_branch`
# (note: you can see all locally-stored remote-tracking "hidden" branches
# with `git branch -r`)
git fetch origin my_branch
# create `peer_branch` as a fork off of `origin/my_branch`, and check it out
git checkout -b peer_branch origin/my_branch
# Now they can add their changes and commits and `git push` to remote `origin`
# as their own `peer_branch` when done.
Now that they have pushed all of their changes to remote origin
as their own branch named peer_branch
, you can cherry-pick all of their commits they added on top of your work like this:
# **you** do this to cherry-pick your peer's helpful changes they added to
# your work
# you fetch their latest work from their branch named `peer_branch` on remote
# `origin` into your locally-stored remote-tracking "hidden" branch named
# `origin/peer_branch`
# (note: you can see all locally-stored remote-tracking "hidden" branches
# with `git branch -r`)
git fetch origin peer_branch
# ensure you are on `my_branch` (if you aren't already)
git checkout my_branch
# you cherry-pick all of their extra commits from their `peer_branch` onto
# your `my_branch` (note: the 3 dots here are very important!)
git cherry-pick my_branch...origin/peer_branch
git log # review the commits you just chery-picked
git push # push your changes to the remote
For your understanding, that cherry-pick
command just above, with 3 dots in it, is exactly equivalent to this longer command:
git cherry-pick $(git merge-base my_branch origin/peer_branch)..origin/peer_branch
The git merge-base my_branch origin/peer_branch
part finds the common parent commit hash between branch my_branch
and branch origin/peer_branch
. This is the commit at which point they forked their peer_branch
off your your my_branch
. Then, you are, of course, cherry-picking the range of commits from that point to (..
) their final commit at origin/peer_branch
.
To read more about that 3-dot syntax, see here: What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges? [duplicate]. For help on git checkout -b new_branch from_branch
, see my answer here: Various ways to create a branch in git from another branch
Official Git documentation
- https://git-scm.com/docs/gitrevisions - mentions git commit 3 dot (
...
) vs 2 dot range syntax, ^commit
("not" commit
), commit^
(the parent of commit
), etc.
Going further
- Something else to know: a
git rebase
is just a bunch of sequential git cherry-pick
s. See my other answer here (Who is "us" and who is "them" according to Git?) where I show, among other things, an ASCII drawing I made of how a git rebase
works and what it's doing.
- What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges? [duplicate]
- My answer on Various ways to create a branch in git from another branch
- My Q&A: In a
git merge
-style workflow, show only the unique commits someone had in their PR's (Pull Request's) feature branch before merging
b
to be based onf
later on, but that has nothing to do with the cherry-picking.) – Calva