How to cherry-pick multiple commits

Viewed 808961

I have two branches. Commit a is the head of one, while the other has b, c, d, e and f on top of a. I want to move c, d, e and f to first branch without commit b. Using cherry pick it is easy: checkout first branch cherry-pick one by one c to f and rebase second branch onto first. But is there any way to cherry-pick all c-f in one command?

Here is a visual description of the scenario (thanks JJD):

enter image description here

17 Answers

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

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

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

Going further

  1. Something else to know: a git rebase is just a bunch of sequential git cherry-picks. 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.
  2. What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges? [duplicate]
  3. My answer on Various ways to create a branch in git from another branch

Another variant worth mentioning is that if you want the last n commits from a branch, the ~ syntax can be useful:

git cherry-pick some-branch~4..some-branch

In this case, the above command would pick the last 4 commits from a branch called some-branch (though you could also use a commit hash in place of a branch name)

To cherry pick from a commit id up to the tip of the branch, you can use:

git cherry-pick commit_id^..branch_name


I need cherry pick a commit from one branch to another on priority, but commits here were difficult to understand, hope below helps with a easy one:


Procedure to do below:

  1. Get 1 commit with name ( "Remove Last Name field") , from "dev" branch
  2. Commit it in "hotfix1" branch

1 . Get commit details from "dev" branch

// Go to "dev" branch
git checkout dev

// Get the commit id (1e2e3e4e1 here)
git log --oneline

    > ...
    > ...
    > 1e2e3e4e1     Remove Last Name field
    > ...
    > ...

2 . Push the commit to "hotfix1" branch

// Go to "hotfix1" branch
git checkout hotfix1

// Get the commit (1e2e3e4e1) from "dev" branch to "hotfix1" branch
git cherry-pick 1e2e3e4e1

// verify changes are correct
gitk

// push to "hotfix1" branch
git push

To do multiple at once, only 1 change in the above, give all commit ids in sequence:

git cherry-pick 1e2e3e4e1 1e2e3e4e2 1e2e3e4e3

Cherry-pick multiple commits:

Checkout to your branch where you want to cherry-pick the commits

Use this command: (by partial hash)

git cherry-pick 1e038f10 1e038f11 1e038f12 ...

Here's a script that will allow you to cherry-pick multiple commits in a row simply by telling the script which source and target branches for the cherry picks and the number of commits:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

To cherry-pick from your branch to master (uses the current branch as source):

./gcpl.sh -m

To cherry-pick the latest 5 commits from your 6.19.x branch to master:

./gcpl.sh -c 5 -s 6.19.x -t master

Alternatively using GitHub Desktop Application,

You can multi-select commits in the history tab of the source branch, then right-click to get the option "Cherry-Pick Selected Commits".

In addition to commit-ish, you can pipe in a list of SHAs from stdin.

git rev-list --reverse ..main -- path/ | git cherry-pick --stdin 

rev-list is basically the plumbing command (the "ugly" but fast cousin) of git-log NB that --reverse is needed.

You can do more advanced stuff this way rather than simply a commit range.

Related