Branching and merging in real life

At work I still mostly use Subversion for version control. Its main selling points: stable, performs as expected, integrates nicely with Trac, holds all our old stuff (legacy inertia).

Note that “pain-free branching and merging” is not on that list. (And don’t give me the old “branching is cheap in svn!” line. It’s not about the branching, it’s about the merging.) A couple years ago I started also using Mercurial and plan to eventually replace svn with it entirely. The aspect of Mercurial that made my life better recently is its support for branching and merging.

The scenario: an important internal web app (in use all day every school day) needed some significant changes on a short timetable. Normally I’d work on the app thus: edit the staging copy, commit, update the live copy. I didn’t want to take that approach here. I knew that during the development window there might arise unrelated urgent change requests; I wanted to keep the new code isolated during development, but also deploy and track those unrelated urgent changes. Branching seemed like the right approach.

I could have made a full clone of the app (hg clone mainrepo newrepo). However, handling environment dependencies (web server, PythonPath, database) would have added time and fussiness to the job, and time was in short supply. So, using Mercurial’s named-branches feature, I made a new branch (hg branch newstuff) right inside the fully-functional staging copy of the app. That way I was able to develop and test as usual, secure that my unproven work-in-progress was not “polluting” the current app’s revision history.

To handle “unrelated urgent changes” as mentioned above, I’d:

  1. Commit any current work on the “newstuff” branch
  2. Switch to the main branch (hg update -r default)
  3. Make the urgent change, test, commit, update the live copy
  4. Switch back to the new branch (hg update -r newstuff)

It took me a couple tries to understand how branch-switching worked, but it’s simple: you really are updating your working directory to a new revision, it just happens to be a revision stored in a different branch from the current one.

It was fun looking at the graph (via HgWeb) and seeing my two parallel branches with their individual commits.

The moment of truth came at the end of the day Friday, when it was time to merge the tested and complete “newstuff” code with the current live codebase. It was dead simple, and effectively instantaneous. Condensed version: hg update -r default; hg merge -r newstuff; hg ci -m "merged new stuff". Followed by: update live copy and let out a big sigh.

Joe commented :

You haven’t made it terribly clear, but I guess you were making the significant changes on the staging server as it already had the infrastructure ready. Was why it was important to be able to switch between branches and make changes on the original branch?

Is this correct?

Paul commented :

Sorry for the confusion. The challenge in writing a post like this is giving enough detail about the working setup to make the story clear, but not descending into uninteresting details. Anyway, your suppositions are correct.

Chris commented :

How would one bring forward the changes in default into newstuff branch to test before merging into default?

codekoala commented :

It’s neat stuff huh! I was very wary of branching/merging when I used SVN, but I find it to be very easy and reliable with Mercurial. Mercurial FTW!

Paul commented :

Chris – for my purposes “bring forward the changes” and “merge” mean the same thing. So, the command sequence in the last paragraph of my post is the answer to your question as I understand it. After I’ve tested the merged code on the staging side, I update the live side by doing an hg fetch from staging.

Chris commented :

Paul, I was thinking more along the lines of: working on a new feature newstuff but want to bring in the bugfixes being made in default. I’m not done with newstuff yet, I just want to make sure it tests against the default bugfixes.

If I’m reading right your last paragraph is merging into default, whereas I wasn’t to merge into newstuff from default. Do I just need to reverse the commands in the last paragraph?

Paul commented :

Chris: Sorry for the delay in responding, but: yes. Swapping “default” and “newstuff” in the last paragraph’s example will do exactly what you want.

(Just had occasion to do that very thing a few minutes ago, in fact, which reminded me to come here and reply to you!)

Paul commented :

Just wanted to follow up to note that, many months later, this workflow continues to please. At times I’ve had three separate branches underway, but never any confusion. Subversion is only a legacy system for me now and I look forward to leaving it behind.