How to pick individual commits, multiple commits, or a series of commits
To your current checked-out branch:
-
Pick a branch named [branch] or a commit [commit hash]
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. Pick multiple commits
Note that you can pick any number of commit hashes in any order you prefer. They will be applied one at a time in the order you specify. If conflicts arise, you must resolve one issue at a time, then use git add my_file followed by git cherry-pick --continue to proceed with the cherry-pick process.
git cherry-pick [commit1] [commit2] [commit3] [commit4] [commit5]
3. Pick a series of commits
This approach was inspired by @Eric Darchis's answer here.
Note that to pick a series of commits, you must specify the starting and ending commit hashes along with .. to denote the range. However, the starting commit is excluded from the range. To include it, you must specify the commit before the starting commit. The syntax for specifying commits before the starting commit is placed after the commit using ~, ~1, or ^ (e.g., beginning_commit~), which means "the previous commit". beginning_commit^ is equivalent to beginning_commit~1.
# 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 represent "the previous commit" (i.e., the immediate parent of commit). To specify the commit two steps back, use:
commit~~
commit~2 # preferred syntax
commit^^
To specify the commit three steps back, use:
commit~~~
commit~3 # preferred syntax
commit^^^
This syntax is invalid:
commit^3 # INVALID syntax
This is valid but applies only to merge commits (see my Q&A: Show only unique commits a person had in their PR feature branch before the merge in a git merge-style workflow):
# 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~ # this is the immediate **left** parent of the two parents involved in the merge (which made commit `commit`)
To test these "previous commit" syntax concepts, use git log:
git log [commit]
git log [commit]~
git log [commit]~1
git log [commit]^
git log [commit]~~
git log [commit]~5
# etc.
4. Pick a series of commits from a peer's branch onto your branch
...when their branch peer_branch forks from 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 cherry-picked
git push # Push your changes to the remote
The difference between two dots (..) and three dots (...) in commit ranges is significant. git diff branch1...branch2 is equivalent to git diff $(git merge-base branch1 branch2) branch2. This is useful when you want to view changes since branch1 diverged, not the differences between the current states of both branches. See this and this comment as well as this answer.
Detailed Explanation and Workflow Walkthrough
Assume you're developing a feature branch my_branch, and a colleague wants to help you make changes to complete the feature. You've pushed my_branch to a remote named origin. They fetch my_branch locally, fork their branch peer_branch from it, and push to origin as peer_branch. Once they do this, you can immediately cherry-pick all their changes. The process starts as follows:
# **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've pushed all changes to remote origin as their own branch peer_branch, you can cherry-pick all their additions to your work as follows:
# **You** do this to cherry-pick your peer's helpful changes
# 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're 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 cherry-picked
git push # Push your changes to the remote
For clarity, the cherry-pick command with three dots is equivalent to this longer command:
git cherry-pick $(git merge-base my_branch origin/peer_branch)..origin/peer_branch
This finds the common parent commit hash between my_branch and origin/peer_branch using git merge-base, which is where they diverged. Then, you cherry-pick from that point to origin/peer_branch.
For more details on the three-dot syntax, see What are the Differences between double-dot ".." and Triple-dot "..." in Git diff commit Ranges?. For help with git checkout -b new_branch from_branch, see my answer: Various ways to create a branch from another branch in Git.
Official Git Documentation
- https://git-scm.com/docs/gitrevisions - Mentions git commit three-dot (
...) range syntax, two-dot range syntax, ^commit ("not" commit), commit^ (parent of commit), etc.
Further Reading
- One more thing to know: a
git rebase is essentially a sequence of git cherry-picks. See my other answers (Who is "we" and who is "they" in Git?), where I show an ASCII diagram illustrating how git rebase works.
- What are the Differences between double-dot ".." and Triple-dot "..." in Git diff commit Ranges?
- My answer: Various ways to create a branch from another branch in Git
- My Q&A: Show only unique commits a person had in their PR feature branch before the merge in a
git merge-style workflow