My take on Git rebasing versus cherry-picking

October 3, 2016

As I've noticed recently, there's a number of git problems that can be solved by either git rebase or git cherry-pick. Because I love git rebase I started out feeling that I should solve all of my problems with it (and resorting to 'git cherry-pick' was a weakness), but I've wound up convinced that sometimes this is wrong. However, until very recently I didn't feel that I understood when I wanted to use one or the other; instead it felt like I was making it up as I was going along. In large part due to learning from Aristotle Pagaltzis's useful comments here, I've now wound up with my take on when I want to try to use one versus the other.

Put simply, my take is that:

Git rebasing is for moving commits while cherry-picking is for copying them.

An obvious case of moving commits is 'git pull --rebase'; I'm moving my commits from the old origin/master to the new one. Ultimately what makes something a move is that I don't really care about the old versions of the commits I'm rebasing; I'll only go back to them (via the reflog) if something goes badly wrong (for example, perhaps the new upstream version I pulled is broken).

(If I look at rebasing as moving commits, git rebase always updating the current or target branch makes sense. The kind of things where I'd want to leave the 'source' of the commits intact is not a move.)

Generally if I want to leave the old version of my local commits around as well as have a new version, that's a copy instead of a move. At a mechanical level I may use 'git rebase' after the 'git cherry-pick' to reorder commits and so on, but I'll start with a cherry-pick. One case that I've come to see as a copy instead of a move is transplanting my local changes on top of another branch in order to use it for various reasons. This covers both the 'switch from development to stable branch' and 'switch from mainline to someone's experimental changes' cases in my collection of git tree shuffles I was uncertain about.

I've also realized that this describes what I want to do when I'm working with Github pull requests. My old writeup used 'git rebase', but it was clearly awkward; any time I'm making a branch and then later changing its upstream is a warning sign. A much simpler approach is to pull down the PR(s), check the appropriate one out as a new (local) branch, and then simply cherry-pick my changes on to it with 'git cherry-pick origin/master..master'.

(This makes working with PRs the same as working with any other branch, which is really what I'd expect. If I want to cherry-pick when I'm moving from development to stable or stable to development, I want to cherry-pick for PRs too.)

Comments on this page:

By JeremyH at 2016-10-04 10:53:41:

Can't you get copy semantics from rebase by simply using a temporary pointer to the source branch? Eg, to copy branch src onto target, create a temporary branch src-for-rebase-to-target that points to src, then rebase it onto target, fast-forward target and throw away the temporary branch.

Ok, that sounds more complicated than I expected it to, but that's how my mental model of rebase works for copying.

Seems to me that the key difference between rebase and cherry-picking is that rebase inherently goes back to the common ancestor of the source and target branches, while cherry-picking works with a more flexible set of commits with less regard for shared history.

Admittedly though, I'm not very familiar with cherry-picking multiple commits in one command.

By cks at 2016-10-04 11:49:33:

I believe that the end results from rebasing and cherry-picking are about the same and conceptually they both do the same thing (more or less turn one or more commits into patches, apply the patches, and turn them back into commits). Rebasing may work better if there are conflicts or other problems when applying the changes. So to me it's just a question of which is easier to use in any particular circumstance, which is where I wind up with my answer.

(If you need to do a bunch of modifications as part of the copy, perhaps an interactive rebase would be easier than a cherry-pick even if you have to play games with temporary branches.)

Rebasing does have a bunch of smarts that attempt to automatically determine the point to rebase from, but by the time you're using the three argument form I believe you're overriding all of them. It also probably depends on how tangled a relationship you have between the (nominal) 'upstream' and the branch; if you have a simple linear relationship, the choice of commits is clearly straightforward.

Written on 03 October 2016.
« Why I've put a Twitter client on my smartphone
Linux can be really stable under the right circumstances »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Oct 3 22:14:53 2016
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.