One of Git’s most popular features is the rebase command. Rebasing has several uses, but one of the most interesting is the ability to ‘merge without merging’. In other words, you can take a set of commits from a branch and reapply them to the same branch after adjusting the branch pointer. A couple of pictures will help to illustrate the concept as we try to keep a topic branch up to date with the latest work from master.
In this picture we start with a master branch, then make a topic branch and record a commit there. Next we switch back to master and record another commit. Then we merge master to topic to pick up the latest trunk work, and end up with a merge commit.
Rebasing lets us take a different approach.
We start out the same way, but instead of merging master to topic, we rebase topic to master. That takes our local commit (t-1) and reapplies an equivalent patch after moving the topic branch to the head of master. The net effect is that it looks like topic was created from the current head of master. This is a really useful way to keep topic up to date with master without recording a bunch of merge commits.
It’s not possible to do this directly in Subversion, but there is a recipe that gets you close. First let’s look at the normal situation in Subversion when I work on a topic branch and do a refresh merge from trunk.
The revision graph (from SmartSVN) shows what you’d expect, although note that Subversion revision graphs don’t show the merge arrows.
Now let’s look at how to get to this point instead:
I can’t change my branch pointer in Subversion like I can in Git, but I can create a new branch (I’ll call it feature_prime) that starts from the new tip of master and then apply any patches from the original feature branch. At this point I can throw away the original feature branch and continue working on feature_prime. It’s not an ideal solution, but may still be useful in some cases.
How did I get there? Here’s the recipe, starting after the m-2 commit on master:
# make feature_prime branch
svn copy ^/master ^/feature_prime -m "mkbranch"
# calculate diffs between feature and master
svn mergeinfo --show-revs eligible ^/feature ^/master
# for each rev found, run a cherry pick merge into feature_prime
svn merge ^/feature -c 8 --ignore-ancestry
svn commit -m "rb-8”
Essentially we find the deltas between feature and master, then run an ignore-ancestry merge to get them into the new branch. That type of merge ignores the normal merge history and just calculates diffs.
In my next post about rebasing in Subversion I’ll talk about the more general case of rebasing in Git, which turns out to be easier to emulate in Subversion.