Coming to a better understanding of what git rebase
does
Although I've used it reasonably regularly, git rebase
has so far
been a little bit magical to me, as you may be able to tell from
my extensive explanation to myself of using it to rebase changes
on top of an upstream rebase. In my grand
tradition, I'm going to write down what I hope is a better understanding
of what it does and how its arguments interact with that.
What git rebase
does is that it takes a series of commits, replays
them on top of some new commit, and then gives the resulting
top commit a name so that you can use it. When you use the three
argument form with --onto
, you are fully specifying all of these.
Take this command:
git rebase --onto muennich/master old-muennich master
--onto
names the new commit everything will be put onto (usually
it's a branch, as it is here), the series of commits that will be
replayed is old-muennich..master
, and the new name is also master
.
You don't get a choice about the new name; git rebase
always makes
your new rebase into your branch, discarding the old value of the
branch.
(As far as I can tell there's no technical reason why git rebase
couldn't let you specify the branch name of the result; it's just
not in the conceptual model the authors have of how it should work.
If you need this, you need to manually create a new branch beforehand.)
The minimal version has no arguments:
git rebase
This only works on branches with an upstream. It replays your commits from the current branch on top of the current (ie new) upstream, and it determines the range of commits to rebase roughly by finding the closest common ancestor of your commits and the upstream:
A -> B -> C -> D [origin/master] \-> local-1 -> local-2 [master]
In this bad plain text diagram, the upstream added C and D while
you have local-1 and local-2. The common point is B, and so B..master
describes the commits that will be put on top of origin/master and
then your master
branch will be switched to them (well, the new
version of them).
A rebase is conceptually a push to cherry-pick's pull. In cherry picking, you start on the new clean branch and pull in changes from elsewhere. In rebasing, you start on your 'dirty' local branch and push its changes on top of some other (clean) branch. You then keep the name of your local branch but not its old origin point.
If you use the one or two argument form of git rebase
, you're
explicitly telling rebase what to consider the 'upstream' for both
determining the common ancestor commit and for what to put your
changes on top of. If I'm understanding this correctly, the following
commands are both equivalent to a plain 'git rebase'
on your
master
branch:
git rebase origin/master git rebase origin/master master
Based on the diagrams in the git-rebase manpage, it looks like the one and two argument forms are most useful for cases where you have multiple local branches and want to shuffle around the relationship between them.
In general the git-rebase manpage has helpful examples combined with extensive ASCII diagrams. If I periodically read it carefully whenever I'm confused, it will probably all sink in eventually.
(Of course, the git manual page that I actually should read carefully several times until it all sinks in and sticks is the one on specifying revisions and ranges for Git. I sort of know what a number of the different forms mean, but in practice it's one part folklore to one part actual knowledge.)
|
|