How to cherry-pick multiple commits
Asked Answered
B

18

1545

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):

A commit showing a 'before' and 'after' state. In the 'before' state, commits a through f are connected in one contiguous sequence. In the after state, commits c through f have been relocated to directly connect to a without reordering, leaving the b commit behind.

Beneficent answered 4/11, 2009 at 0:7 Comment(2)
the rebase you mention is not really relevant for the question is it? (I get that you may want b to be based on f later on, but that has nothing to do with the cherry-picking.)Calva
See also a sort of inverse corollary to this: Revert a range of commits in gitBlabbermouth
S
2339

Git 1.7.2 introduced the ability to cherry pick a range of commits. From the release notes:

git cherry-pick learned to pick a range of commits (e.g. cherry-pick A..B and cherry-pick --stdin), so did git revert; these do not support the nicer sequencing control rebase [-i] has, though.

To cherry-pick all the commits from commit A to commit B (where A is older than B), run:

git cherry-pick A^..B

If you want to ignore A itself, run:

git cherry-pick A..B

Notes from comments:

  • A should be older than B, or A should be from another branch.
  • On Windows, it should be A^^..B as the caret needs to be escaped, or it should be "A^..B" (double quotes).
  • In zsh shell, it should be 'A^..B' (single quotes) as the caret is a special character.
  • For an exposition, see the answer by Gabriel Staples.

(Credits to damian, J. B. Rainsberger, sschaef, Neptilo, Pete and TMin in the comments.)

Singly answered 14/10, 2010 at 13:8 Comment(19)
In the "cherry-pick A..B" form, A should be older than B. If they're the wrong order the command will silently fail.Yonatan
If you have git 1.7.1 or earlier and can't update, you can pretty quickly cherry-pick them in order by running git cherry-pick f~3 then git cherry-pick f~2 etc. up to git cherry-pick f (pressing the up arrow gets the previous command so I can quickly change the number and run it, should be similar in most consoles).Peltry
It may be good to know that this syntax works with branch names too. git cherry-pick master..somebranch will pick all commits on somebranch since master (assuming is already rebased onto master), and apply them to your current branch.Cataclinal
I am getting conflicts while doing a git cherry-pick A^..B I want to resolve all these conflicts using theirs. I tried git cherry-pick -X theirs A^..B Still getting conflicts Any help ?Selfaggrandizement
You can also do git cherry-pick f~3 f which will take commits from branch f and 3 commits before that (inclusive)Interactive
Is there a way to cherry-pick all commits for given file?Keelia
On Windows in cmd.exe, ^ is a special character and is silently ignored in A^..B. You have to double it (^^) or put the commit reference in quotes.Floeter
@VibhuDadhichi Try this: git cherry-pick --strategy=recursive -X theirs A^..BSteatite
Note that A^ is selecting one of A's parent commits. This means you have to be careful if it has multiple parent commits (e.g., it's a merge commit).Euglena
cherry-pick ... -n gives you the most control. It only stages, does not commit. So you can still look at what you are going to do.Cybele
Note 3 should be Note 0 :)Intellectuality
"A should be older than B" is not entirely correct. It picks everything from B that is not reachable from A, but A can be a different branch (with newer commits than in B), in which case everything that is in the B branch is picked.Fisc
This command didn't work for me in the console of GIT Extension (3.4.1.9675). Always picked the first commit and not the range ...Strath
I just ran into an issue where this wasn't working with zsh because ^ is a special character in zsh. Wrapping the range of commits in singe quotes got this to work. git cherry-pick 'A^..B' github.com/ohmyzsh/ohmyzsh/issues/4398 The error I was seeing was "zsh: no matches found: A^..B"Belongings
I just expounded upon the above answer in my answer here..Blabbermouth
That's strange but when I used double dots between the commits I got empty commits. Adding the third dot resolved the issue. git cherry-pick A^...B. I was cherry-picking from the different origin. In other words I had to cherry-pick commits from other repository. Maybe this was the reason, no idea.Specular
thank you! this has been a great answer!Lasalle
@PrzemekNowicki, the difference between 2 dots and 3 dots 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...Blabbermouth
...and the answers here: What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges?Blabbermouth
I
242

If you have selective revisions to merge, say A, C, F, J from A,B,C,D,E,F,G,H,I,J commits, simply use the below command:

git cherry-pick A C F J
Intermediate answered 20/2, 2018 at 10:0 Comment(0)
B
145

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

  1. 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

  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
  4. 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
Blabbermouth answered 6/10, 2021 at 20:14 Comment(2)
Regarding: commit~3 # my preferred syntax Perhaps it would be better to call it "revision" for consistency with git naming.Maganmagana
I agree with the commit~3 # my preferred syntax. It's one of the few things I can manage to remember HEAD~1 is one commit back from HEAD. In the same concept I think git cherry-pick beginning_commit~1..ending_commit is the best syntax to use. Since I do cherry-pick very rarely, it's useful to have the extra explicitness of the ~1 so that when I search for it in my bash history I don't have to go searching the internet again to figure out exactly what the ^ means.Januarius
I
144

The simplest way to do this is with the onto option to rebase. Suppose that the branch which current finishes at a is called mybranch and this is the branch that you want to move c-f onto.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b
Irony answered 4/11, 2009 at 8:13 Comment(5)
Thank you! Could you also add git checkout secondbranch && git rebase mybranch for full answerBeneficent
This answer helped me a lot to get my head around which commit is which in this scenario. And: you can use rebase's interactive mode, too. Thanks, @Charles!Speaks
The beauty of this approach is that you can use --interactive to remove some commits from the sequence or reorder them prior to the "cherry pick". +1Merthiolate
Wild that you have to give the commit that you don't want to rebase as one of the argumente (b in this example) but yes this has worked for me.Iritis
This answer did the trick for me. I used the rebase commands in conjunction with --committer-date-is-author-date to keep the original commit timestamps (incompatible with --interactive)Obscurant
G
100

Or the requested one-liner:

git rebase --onto a b f
Gerfen answered 25/5, 2011 at 15:12 Comment(4)
Upvoted but will leave you in detached HEAD state if f is a commit (as opposed to a branch) - you should edit to add that one should checkout a branch as in answer belowPivoting
Why bother with the rest?Polymath
One should note that cherry-picking a bunch of commits is not equivalent to rebasing them. The original poster seems to want the effect of rebasing, if one looks at the diagrams, but rebasing is not safe if you have already published your commits to a team or public repo.Gemmulation
I like this one but once you're in detached HEAD then you should git branch tmp (to save your detached HEAD state) + git checkout destination-branch (checkout to your destination, maybe develop) + git merge tmp (merge your detached HEAD into your destination)Shanahan
A
80

You can use a serial combination of git rebase and git branch to apply a group of commits onto another branch. As already posted by wolfc the first command actually copies the commits. However, the change is not visible until you add a branch name to the top most commit of the group.

Please open the picture in a new tab ...

Workflow

To summarize the commands in text form:

  1. Open gitk as a independent process using the command: gitk --all &.
  2. Run git rebase --onto a b f.
  3. Press F5 in gitk. Nothing changes. But no HEAD is marked.
  4. Run git branch selection
  5. Press F5 in gitk. The new branch with its commits appears.

This should clarify things:

  • Commit a is the new root destination of the group.
  • Commit b is the commit before the first commit of the group (exclusive).
  • Commit f is the last commit of the group (inclusive).

Afterwards, you could use git checkout feature && git reset --hard b to delete the commits c till f from the feature branch.

In addition to this answer, I wrote a blog post which describes the commands in another scenario which should help to generally use it.

Amoebaean answered 28/9, 2012 at 20:30 Comment(2)
If the mybranch (the a..f commits) is not needed anymore this can be simplified to: git rebase --onto a b mybranch and btw - which program does those nifty git pictures ?Pivoting
@Pivoting Thanks for your comment. I think I used cacoo.com to draw the pictures.Amoebaean
J
58

To apply J. B. Rainsberger and sschaef's comments to specifically answer the question... To use a cherry-pick range on this example:

git checkout a
git cherry-pick b..f

or

git checkout a
git cherry-pick c^..f
Jaguar answered 26/7, 2015 at 18:25 Comment(4)
I use git 2.7.0.windows.1 and noticed that when I try to cherry pick range of commits everything is ok but git doesn't tell you anywhere that you have to do git cherry-pick --continue | --abort | --quit before you try to commit/cherry-pick again. So if you cherry-pick range of commits you gonna need to run git cherry-pick --continue every time you are ready(resolving conflicts or such) with a commit from the given range.Kea
I did exactly same, but fatal: Cannot find 'a..b'Goebbels
I don't know where I'm wrong but when I do 'git cherry-pick c^..f' on my side, this includes the commit f but not the commit c. But as I read everywhere, it's supposed to define c and f as inclusive. Or am I wrong?Devise
@Devise yes, that's correct. The ^ after the c actually means "the commit before c" which is b in this case. This is why c^..f is synonymous to b..f. Try doing git log c^..f and you should see commits c through f, exactly the same as if you did git log b..fJaguar
G
36

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

git cherry-pick commit_id^..branch_name
Gerdy answered 13/6, 2019 at 8:48 Comment(5)
This is already part of answer https://mcmap.net/q/12943/-how-to-cherry-pick-multiple-commitsBeneficent
This answer is actually different and was helpful to me. It specifies the branch name, not the final commit SHA.Palatalized
I got "fatal: bad revision" errorPeppercorn
@Peppercorn I get the same error, did you manage to find a reason for it?Balthazar
Nvm, it turns out I was using a - instead of ...Balthazar
I
28
git rev-list --reverse b..f | xargs -n 1 git cherry-pick

git rev-list prints all revisions from branch b to f (reversed) so that when each line (the commit hash) is passed in order, it will cherry pick each one onto the current git HEAD. i.e. git cherry-pick {hash of c}; git cherry-pick {hash of d}; ...

-- @coderatchet comment

Ipecac answered 4/11, 2009 at 3:59 Comment(5)
Works perfect if there is no conflicts, otherwise "rebase onto" might be easier as you will not have to figure out where it has stopped and re-applying the rest of the patches.Oblivious
Please add comments explaining what this is doingPivoting
since no one explained... git rev-list prints all revisions from branch b to f (reversed) so that when each line (the commit hash) is passed in order, it will cherry pick each one onto the current git HEAD. i.e. git cherry-pick {hash of c}; git cherry-pick {hash of d}; ...Insoluble
How is this different from git cherry-pick b..f? Maybe the cherry-pick b..f syntax didn't exist in 2009?Januarius
Without using xargs, as one final command, in case of merge conflicts (if your system has paste): git cherry-pick -x $(git rev-list --reverse b..f | paste -sd ' ' -)Goldy
J
19

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)

Jackquelin answered 13/11, 2019 at 10:27 Comment(0)
D
10

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 ...
Didi answered 4/4, 2022 at 8:17 Comment(0)
H
3

Actually, the simplest way to do it could be to:

  1. record the merge-base between the two branches: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. fast-forward or rebase the older branch onto the newer branch
  3. rebase the resulting branch onto itself, starting at the merge base from step 1, and manually remove commits that are not desired:

    git rebase ${SAVED_MERGE_BASE} -i
    

    Alternatively, if there are only a few new commits, skip step 1, and simply use

    git rebase HEAD^^^^^^^ -i
    

    in the first step, using enough ^ to move past the merge-base.

You will see something like this in the interactive rebase:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Then remove lines b (and any others you want)

Helot answered 12/12, 2017 at 7:13 Comment(0)
R
2

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

Rushton answered 1/12, 2021 at 5:37 Comment(0)
T
0

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
Thurstan answered 19/3, 2019 at 17:22 Comment(3)
Why would this script be needed when you can do that with Git directly?...Hyperplane
Less typing, and my script automatically picks the commits for you, so you don't have to cherry pick each one by its SHA. Anyway, YMMV.Thurstan
Never use a script if you don't understand what the script does, and why it does it. Otherwise you will never learn git.Enrichment
M
0

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".

Mercurialize answered 31/1, 2022 at 7:29 Comment(0)
K
0

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.

Kandace answered 8/3, 2022 at 9:48 Comment(0)
D
-1

Not really an answer on HOW to cherry pick multiple commits but a way to avoid too much merging. I did this using IntelliJ but it should be easy to do on the command line as well.

You can make a temporary branch from the last commit just before the first one you want to cherry pick from. Then cherry pick everything you need into this temporary branch. This should generally be easy without many conflicts.

Then squash all the cherry picked commits. This will turn all the back and forth changes into single changes.

Now merge that single commit into your actual target branch. You will not have to resolve conflicts more than once.

Destruct answered 21/2 at 6:38 Comment(1)
Thank you for your contribution to this question. While interesting in some ways, it's not really an answer to the question asked, unfortunately.Ahouh
P
-4
git format-patch --full-index --binary --stdout range... | git am -3
Perce answered 4/11, 2009 at 1:8 Comment(1)
Please add comments explaining what this is doingPivoting

© 2022 - 2024 — McMap. All rights reserved.