How to cherry-pick a range of commits and merge them into another branch?
Solution 1
When it comes to a range of commits, cherry-picking is was not practical.
As mentioned below by Keith Kim, Git 1.7.2+ introduced the ability to cherry-pick a range of commits (but you still need to be aware of the consequence of cherry-picking for future merge)
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.
In the "
cherry-pick A..B
" form,A
should be older thanB
.
If they're the wrong order the command will silently fail.
If you want to pick the range B
through D
(including B
) that would be B^..D
(instead of B..D
).
See "Git create branch from range of previous commits?" as an illustration.
As Jubobs mentions in the comments:
This assumes that
B
is not a root commit; you'll get an "unknown revision
" error otherwise.
Note: as of Git 2.9.x/2.10 (Q3 2016), you can cherry-pick a range of commit directly on an orphan branch (empty head): see "How to make existing branch an orphan in git".
Original answer (January 2010)
A rebase --onto
would be better, where you replay the given range of commit on top of your integration branch, as Charles Bailey described here.
(also, look for "Here is how you would transplant a topic branch based on one branch to another" in the git rebase man page, to see a practical example of git rebase --onto
)
If your current branch is integration:
# Checkout a new temporary branch at the current location
git checkout -b tmp
# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range
# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration
That will replay everything between:
- after the parent of
first_SHA-1_of_working_branch_range
(hence the~1
): the first commit you want to replay - up to "
integration
" (which points to the last commit you want to replay, from theworking
branch)
to "tmp
" (which points to where integration
was pointing before)
If there is any conflict when one of those commits is replayed:
- either solve it and run "
git rebase --continue
". - or skip this patch, and instead run "
git rebase --skip
" - or cancel the all thing with a "
git rebase --abort
" (and put back theintegration
branch on thetmp
branch)
After that rebase --onto
, integration
will be back at the last commit of the integration branch (that is "tmp
" branch + all the replayed commits)
With cherry-picking or rebase --onto
, do not forget it has consequences on subsequent merges, as described here.
A pure "cherry-pick
" solution is discussed here, and would involve something like:
If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently,git cherry-pick
accepts only a single commit, but if you want to pick the rangeB
throughD
that would beB^..D
in git lingo, so
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
But anyway, when you need to "replay" a range of commits, the word "replay" should push you to use the "rebase
" feature of Git.
Solution 2
As of git v1.7.2 cherry pick can accept a range of commits:
git cherry-pick
learned to pick a range of commits (e.g.cherry-pick A..B
andcherry-pick --stdin
), so didgit revert
; these do not support the nicer sequencing controlrebase [-i]
has, though.
Solution 3
Assume that you have 2 branches,
"branchA" : includes commits you want to copy (from "commitA" to "commitB"
"branchB" : the branch you want the commits to be transferred from "branchA"
1)
git checkout <branchA>
2) get the IDs of "commitA" and "commitB"
3)
git checkout <branchB>
4)
git cherry-pick <commitA>^..<commitB>
5) In case you have a conflict, solve it and type
git cherry-pick --continue
to continue the cherry-pick process.
Solution 4
Are you sure you don't want to actually merge the branches? If the working branch has some recent commits you don't want, you can just create a new branch with a HEAD at the point you want.
Now, if you really do want to cherry-pick a range of commits, for whatever reason, an elegant way to do this is to just pull of a patchset and apply it to your new integration branch:
git format-patch A..B
git checkout integration
git am *.patch
This is essentially what git-rebase is doing anyway, but without the need to play games. You can add --3way
to git-am
if you need to merge. Make sure there are no other *.patch files already in the directory where you do this, if you follow the instructions verbatim...
Solution 5
git cherry-pick start_commit_sha_id^..end_commit_sha_id
e.g. git cherry-pick 3a7322ac^..7d7c123c
Assuming you are on branchA
where you want to pick commits (start & end commit SHA for the range is given and left commit SHA is older) from branchB
. The entire range of commits (both inclusive) will be cherry picked in branchA
.
The examples given in the official documentation are quite useful.
![omar ba44](https://lh6.googleusercontent.com/-YWAU971W5xI/AAAAAAAAAAI/AAAAAAAAAAA/AAKWJJMFgm_Zf__zntSwwRdRHyPsJkE8jQ/photo.jpg?sz=256)
omar ba44
Updated on July 08, 2022Comments
-
omar ba44 almost 2 years
I have the following repository layout:
- master branch (production)
- integration
- working
What I want to achieve is to cherry-pick a range of commits from the working branch and merge it into the integration branch. I'm pretty new to git and I can't figure out how to exactly do this (the cherry-picking of commit ranges in one operation, not the merging) without messing the repository up. Any pointers or thoughts on this? Thanks!
-
Ibrahim Quraish over 13 yearsNote that
cherry-pick A..B
will not get commit A (you would needA~1..B
for that), and if there are any conflicts git will not automatically continue like rebase does (at least as of 1.7.3.1) -
MicroVirus about 8 yearsIt's also good to note that
git cherry-pick A..B C
doesn't work like you'd expect it to, naively. It will not pick everything in the rangeA..B
and commitC
! To do this, you need to split into two lines, firstgit cherry-pick A..B
and thengit cherry-pick C
. So, whenever you have a range, you need to execute it separately. -
aug almost 8 yearsIf you have commits that have parents that require the
-m
option, how do you handle those commits? Or is there a way to filter out these commits? -
VonC almost 8 years@aug
-m
is supposed to handle them for you, by selecting the mainline referenced by the-m
parameter you have chosen for this cherry-picking. -
aug almost 8 yearsThe thing is if you are cherry picking a range of commits, it will cherry pick the parent commits correctly but then when it hits a normal commit, it fails and says commit is not a merge. I guess my question is better phrased how to make it pass the
-m
option only when it hits a parent commit when cherry-picking range of commits? Right now if I pass-m
likegit cherry-pick a87afaaf..asfa789 -m 1
it applies to all commits within the range. -
VonC almost 8 years@aug Strange, I did not reproduce the issue. What is your git version and whayt is the exact error message you see?
-
aug almost 8 yearsAh I'm running git version 2.6.4 (Apple Git-63). The error I see would be something like
error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.
I actually realized you could justgit cherry-pick --continue
and it would be fine (but it wouldn't include the parent commit) -
dtc over 6 yearsi just cherry-picked a range and it's not the same as individually applying cherry-pick to each commit..ran into weird conflict errors
-
VonC over 6 years@dtc Strange: maybe you could ask a new question with more details (git version, OS, git config -l, ...), to see if that behavior is expected or not?
-
Kenny about 6 yearsI appreciate the details but I feel that this answer can be cleaned up quite a bit. Also, just an opinion, but I'd prefer not to link to anything on Google Groups. Tough to look at and sift through after coming from SO.
-
jocull over 5 yearsThe way I understand it is this. If you want to move commits, you need to
rebase
. If you want to copy commits (leaving the original branch alone), you should usecherry-pick
. I find that a cherry-pick followed by an interactive rebase is great for cleaning up work. Does that follow with everyone else's thinking? -
VonC over 5 years@jocull I don't see often a cherry-pick + rebase -i, but yes, that should work.
-
0andriy about 5 yearsDon't remember from which version, but
git cherry-pick -<NUMBER_OF_COMMITS> <HASH_OF_LAST_COMMIT>
works like a charm! -
Gino Mempin over 4 yearsNote that, same as with other revision ranges, it needs to be
A^
to includeA
. -
VonC over 4 yearsGood post. Upvoted. It reminds me of stackoverflow.com/a/38418941/6309. I recapped the drawbacks of cherry-picking back in 2012: stackoverflow.com/a/13524494/6309.
-
Clement Hoang over 4 yearsIs
git branch -f integration last_SHA-1_of_working_branch_range
necessary? Can't you just use the SHA-1's directly in the rebase command?git rebase --onto tmp first_SHA-1_of_working_branch_range~1 last_SHA-1_of_working_branch_range
-
Clement Hoang over 4 yearsUpdate: This isn't possible since you'll be on a detached HEAD after.
-
VonC over 4 years@ClementHoang True: a rebase starts by switching to (git switch, no more checkout since Git 2.23: stackoverflow.com/a/57066202/6309) the new upstream branch. If said upstream branch is not a branch but a SHA1...: detached HEAD.
-
JVM almost 4 yearsWhat does the "^" do in the "git cherry-pick <commitA>^..<commitB>" command at 4)?
-
Admin almost 4 years
-
lolololol ol over 2 yearsSomeone please edit the post that the
cherry-pick
range is NOT inclusive. -
Tomas Vancoillie over 2 years@JVM when you use cherry-pick without the
^
in the range, the first commit will not be included