Reputation: 4463
I am new to Git.
I have created a new branch new_branch
. I have done all changes and commits in a different branch old_branch
. Now I want to pick some commits from the branch old_branch
to new_branch
by git cherry-pick
. For that I first need to know which commits are in that branch and then pick them and merge with new_branch
.
How can I do this?
Upvotes: 2
Views: 128
Reputation: 487755
The method given by @naomi will work fine, but there's a much easier way. You have your old_branch
branch starting from some commit on some other branch, like this:
A --- B --- C --- D <-- devel (let's say it's branch devel, anyway)
\
E - F - G - H <-- old_branch
You've made a branch new_branch
coming off some commit (B or C or D, perhaps) and you want to pick out some of the commits E, F, G, and/or H (maybe a much longer string of commits, but this should illustrate things).
Normally, if you wanted to take an unpublished sequence of work (perhaps old_branch
is just such a sequence) and "move it" on top of the latest (commit D), you'd just do:
$ git checkout old_branch # get onto old_branch
$ git rebase devel # and rebase it onto commit D in "devel"
What that does is make "copies" of commits E through H, adding each one after "D". Then it peels off the label old_branch
(which used to name commit H) and pastes it onto the copy-of-H, giving this:
A --- B --- C --- D <-- devel
\ \
\ E' - F' - G' - H' <-- old_branch
\
E - F - G - H [abandoned, see footnote]
What you want is to pick some commit, let's say C (wherever you created your new_branch
above) and do the same thing, but not "peel off the label" old_branch
. Furthermore, you want to pick and choose which of E, F, G, and H go there. When it's all done you want the label new_branch
added. That's actually really easy. Instead of creating branch new_branch
at commit C, create it at commit H, the head of old_branch
:
$ git checkout old_branch; git checkout -b new_branch
Now new_branch
and old_branch
are identical content-wise, but have different names.
Now you can simply rebase -i
(interactive, lets you pick and choose) new_branch
onto the point you want it to go. I've been assuming commit C
here, which I have been assuming is one step back from the branch-tip named devel
, so I will run with that:
$ git rebase -i --onto devel~1 devel new_branch
After you pick and choose the way rebase -i
allows (and resolve any conflicts from deleting some commits and git rebase --continue
to continue after resolving), you'll end up with something like this, depending on which commits you take and which you delete. I'll assume you delete F but keep the rest:
A --- B --- C --- D <-- devel
\ \
\ E' - G' - H' <-- new_branch
\
E - F - G - H <-- old_branch
You can actually do this all with just two commands (vs 3 above), as git branch
can create new_branch
at the same point as old_branch
. Also, it's likely you want to rebase -i
onto the tip of whatever branch, i.e., rather than rebasing --onto devel~1
you just want to rebase onto (the tip of) devel
. In this case you don't need the --onto
part after all:
$ git branch new_branch old_branch
$ git rebase -i devel new_branch
The result here is almost the same as before except now commit E' comes off D instead of C:
A --- B --- C --- D <-- devel
\ \
\ E' - G' - H' <-- new_branch
\
E - F - G - H <-- old_branch
This is basically what I do when I am reworking unpublished changes. If I'm on (say) branch revise
I rename it to revise-0
, create a new revise
at the same place, then rebase -i
and clean it up a bit. After one pass of that I may decide to revise it again, so I rename revise
to revise-1
and create a new revise
at the same place as revise-1
, then rebase -i
again, etc. All my old stabs at whatever-it-is are still there if I want them, until I decide I don't want them; then I delete all the -0 -1 -2 ... names.
Footnote: The commits marked "abandoned" above are still there—they're held against garbage collection via the reflog—but eventually the reflog entry expires and they go away in a git gc
. Until then they're mostly invisible: git log --all
and gitk --all
won't show them, for instance. You can run these commands with --reflog
, but even then, with no branch labels, they're still a little tricky to find. I like hanging on to them for a while, hence the multiple branch name workflow above.
Upvotes: 0
Reputation: 8656
There are quite a few ways to accomplish this, here are a couple for the commit listing:
git log --cherry new_branch..old_branch --oneline
will show you all the commits in old_branch
that can't be reached from old_branch
.
Or another way:
git checkout old_branch
git cherry new_branch --abbrev=6 -v
which will show you commits within the old_branch
(without commits that introduce the same change), and will mark commits to indicate if they have -
, or have not '+' yet been cherry-picked into the new_branch
E.g:
+2bdcd1 1st commit (Has not been cherry picked)
-8de6cc 2nd Commit (Has been cherry picked)
+8ac1ee 3nd Commit (Has not been cherry picked)
In either case, you can then use
git cherry-pick <commit>
Although you'd want to checkout the 'new_branch' in the second case.
Upvotes: 0
Reputation: 1984
git checkout new_branch
git log old_branch
The log will show each commit in old_branch, with a long string like "cf5e845a13866239eb87f2593d6edc6e273decc5", just above the commit message. This is the commit hash. You can then do
git cherry-pick <commit hash>
for each commit you want. I would recommend doing them in chronological order (working up the log from the bottom).
Upvotes: 1