1060

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

JJD
  • 44,755
  • 49
  • 183
  • 309
tig
  • 21,082
  • 10
  • 60
  • 93
  • 5
    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.) – Superole Aug 28 '17 at 11:31

12 Answers12

1612

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

(Credit goes to damian, J. B. Rainsberger and sschaef in the comments)

Sridhar Ratnakumar
  • 68,948
  • 61
  • 139
  • 172
Eric Darchis
  • 18,107
  • 4
  • 22
  • 48
  • 274
    In the "cherry-pick A..B" form, A should be older than B. If they're the wrong order the command will silently fail. – damian Jan 11 '11 at 16:16
  • 19
    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). – David Mason Mar 28 '14 at 05:59
  • 21
    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. – Tor Klingberg Jul 12 '16 at 13:42
  • 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 ? – Vibhu Dadhichi Mar 30 '17 at 13:40
  • You can also do `git cherry-pick f~3 f` which will take commits from branch `f` and 3 commits before that (inclusive) – dieend Aug 05 '17 at 00:33
  • Is there a way to `cherry-pick` all commits for given file? – Eugen Konkov Jan 24 '18 at 08:39
  • 9
    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. – Neptilo Apr 23 '18 at 11:10
  • @VibhuDadhichi Try this: `git cherry-pick --strategy=recursive -X theirs A^..B` – Muhammad Qasim Nov 02 '18 at 04:54
  • 2
    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). – jpmc26 Dec 04 '18 at 19:05
  • 1
    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. – user18099 Jul 16 '19 at 13:15
  • Note 3 should be Note 0 :) – Bojan Radivojevic Bomber Oct 24 '19 at 14:27
  • "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. – Pete May 03 '20 at 07:21
  • 1
    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 ... – testing Jul 03 '20 at 15:46
117

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
CB Bailey
  • 648,528
  • 94
  • 608
  • 638
  • 1
    Thank you! Could you also add `git checkout secondbranch && git rebase mybranch` for full answer – tig Nov 04 '09 at 16:30
  • 1
    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! – Oliver May 20 '13 at 13:11
  • 2
    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". +1 – Michael Merickel Mar 21 '14 at 20:00
  • this is a genious command, a bit tricky to wrap your head around, but it works wonders. – Valerio Mar 15 '20 at 00:29
  • 1
    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. – asontu May 14 '20 at 17:32
91

Or the requested one-liner:

git rebase --onto a b f
wolfc
  • 2,049
  • 1
  • 15
  • 7
  • 5
    If only for its brevity, this is the best answer. – Nate Chandler Oct 23 '12 at 16:02
  • 9
    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 below](http://stackoverflow.com/a/12646996/281545) – Mr_and_Mrs_D Sep 16 '14 at 10:53
  • Why bother with the rest? – Jason S Apr 22 '21 at 12:37
  • 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. – Ivan Kurmanov May 14 '21 at 11:07
85

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 below command:

git cherry-pick A C F J

72

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.

Community
  • 1
  • 1
JJD
  • 44,755
  • 49
  • 183
  • 309
  • 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 ? – Mr_and_Mrs_D Sep 16 '14 at 10:54
  • 2
    @Mr_and_Mrs_D Thanks for your comment. I think I used http://cacoo.com to draw the pictures. – JJD Sep 16 '14 at 12:43
52

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
Andy
  • 2,207
  • 1
  • 12
  • 22
  • 5
    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. – kuskmen May 03 '16 at 13:06
  • I did exactly same, but fatal: Cannot find 'a..b' – Amit Karnik Jul 11 '16 at 11:39
  • 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? – Samuel Aug 01 '19 at 18:41
  • @Samuel 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..f` – Andy Aug 02 '19 at 19:06
23
git rev-list --reverse b..f | xargs -n 1 git cherry-pick
Dustin
  • 81,492
  • 18
  • 106
  • 131
  • 2
    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. – Ruslan Kabalin Oct 07 '11 at 11:37
  • 6
    Please add comments explaining what this is doing – Mr_and_Mrs_D Sep 16 '14 at 10:47
  • 7
    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}; ...` – coderatchet Oct 11 '16 at 03:08
12

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

git cherry-pick commit_id^..branch_name

ut9081
  • 315
  • 2
  • 7
  • This is already part of answer https://stackoverflow.com/a/31640427/96823 – tig Jun 20 '19 at 12:32
  • 4
    This answer is actually different and was helpful to me. It specifies the branch name, not the final commit SHA. – Subtletree Jun 24 '19 at 23:14
12

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)

Nick F
  • 8,686
  • 5
  • 66
  • 84
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)

ealfonso
  • 5,383
  • 3
  • 31
  • 57
1
git format-patch --full-index --binary --stdout range... | git am -3
marc_s
  • 675,133
  • 158
  • 1,253
  • 1,388
Roger Wang
  • 1,378
  • 10
  • 8
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
nickboldt
  • 722
  • 1
  • 6
  • 12