Monday, March 16, 2009

Bazaar - managing local feature branches with lightweight checkouts

Bazaar is the BEST source code versioning system in the world, or at least, the best non-commercial one (I can't comment on commercial ones except Visual SourceSafe, which epitomises dog-ness).

Well, that's my opinion, after using SVN and Visual SourceSafe, and reading fairly extensively about other systems.

But it can be hard to get started, just because it is SO powerful and has SO many options and can be used in SO many different ways.

If you're a single developer who just uses it to track their own work, and never makes feature branches, life is very simple.

bzr init
bzr add
bzr commit -m "My project is now in Bazaar! Woot, woot!"
... edit your project ...
bzr add
bzr commit -m "I just made edits to my project, and Bazaar has been updated accordingly."

It's really sweet. Suppose you ship a new version, and you want to be able to access the exact source code that was used in that version :

bzr tag "Released version 1.2.3"

And to review previous tags? Simple :

bzr tags

If you mucked up?

bzr revert

(Throws away your changes since the last commit.)

That's all very sweet and simple. (Sure beats SVN and Visual SourceSafe!!!!!!!!!!!!!!!)

But I wanted to use feature branches.

There are various ways to do it, but here's a simplified version of how I'm doing it, with great success :

Create a root development folder. For sake of demonstration, let's call it "src".

src> bzr init-repo --no-trees
src> md trunk
src> cd trunk
src\trunk> bzr init

At this point, you have a trunk, with nothing in it. Not much use, but press on...

src\trunk> cd..
src> md wt

Here, "wt" is an acronym I've chosen somewhat arbitrarily. It stands for "working tree". It's the only place I'm going to have a copy of the (often extensive) set of files in the project.

Get it?

Bazaar lets you have as many branches as you want, without clogging your hard disk! THAT is AWESOME!

Now any time you embark on a change, you can afford to do it in a branch. That keeps your work well organised, and means you don't end up stuck with half-finished changes in your trunk. Very nice!

Did I mention that Bazaar is the best source code versioning system in the world? :o)

Ah - but we're not done yet. It's still useless at this point. Press on...

src> cd wt
src\wt> bzr checkout ..\trunk --lightweight

Woot, woot! At this point, copy any existing files for the project into src\wt, then proceed.

src\wt> bzr add
src\wt> bzr commit -m "Initial commit, directly into trunk. After this point, we will work on branches, not on the trunk."

Extra wootness! Now we have a trunk, and a working tree, and we are ready to start branching.

src\wt> cd..
src> bzr branch trunk "Branch001 - completely revolutionise colour scheme"
src> bzr branch trunk "Branch002 - add new powerful print-preview system"
src> bzr branch trunk "Small changes"

Yep - you guessed it. If you think there's a reasonable chance you'll get stuck halfway through a set of changes, and not have them finished before you have to work on some other aspect of the source code, then that's a good candidate for a branch. But if it's a REALLY small change (e.g. update the copyright notice in a single file), no need to create a new branch just for that. You could conceivably make that change directly in the trunk (although I advise against it, for reasons that are beyond the scope of this article). My solution? I have a permanent branch called "Small changes", and that's where I make small changes! :o)

So now, let's start work!

src> cd wt
src\wt> bzr switch "..\Branch001 - completely revolutionise colour scheme"
src\wt> bzr revert

At this point, the working tree matches the contents of Branch001.

Start work on that colour scheme.

Oh no! The boss has walked in and said the new print-preview system has to take priority.

Gotta change to a different branch.

Easy done :

src\wt> bzr commit -m "These are my changes so far."
src\wt> bzr switch "..\Branch002 - add new powerful print-preview system"
src\wt> bzr revert

Voila! Now the working tree matches Branch002. Code away merrily...

In comes the accounts lady. "The last digit on the bank account number should be seven, not three."

This change should be done immediately. No point waiting two weeks for the new uber-funky print-preview system, nor five months for the new colour scheme to finally be done.

src\wt> bzr commit -m "Well, working on Branch002 was fun and all, but necessity demands my (temporary) departure."
src\wt> bzr switch "..\Small changes"
src\wt> bzr revert

Now fix that account number. And then proceed...

src\wt> bzr commit -m "Maggie informed me that the account number was incorrect. Fixed now."

Now, chances are, we want that change to go into the trunk right now. Let's do that.

A clue : BAZAAR IS NOT INTUITIVE WHEN IT COMES TO COMBINING FEATURE BRANCHES INTO THE TRUNK.

Oh - don't flame me. The problem is that most documentation talks about the 'bzr push" and "bzr pull" options.

But these will destroy everything. You'll lose the farm, the house, the boat...

There is a very simple rule to keep in mind when working on feature branches on your own computer :

bzr merge!!!!!!!

Yes - Bazaar's merging features are second-to-none. They are truly impressive.

So despite the myriad examples out there telling you to "bzr push" your changes to the central server - ignore it! Those instructions apply in certain common situations, but do not apply for our current purposes.

So here's how we get the latest changes from a branch into the trunk :

src\wt> bzr merge ..\trunk

That's the first step. Remember - whilst we've been busily working away on a branch, there might have been other branches committed to trunk. So the trunk we once knew is not necessarily the current trunk. We need to ensure that what we're about to commit to the trunk is compatible with the current version of the trunk.

And the way we do that is to first merge the latest changes from the TRUNK back into the current branch.

Having done that, we run our unit tests. If everything compiles and runs fine, we're happy chaps. If not, fix it! :o)

When we're happy with the merge :

src\wt> bzr commit -m "Merged with trunk"

Cool. So now we know that our changes in this branch are compatible with the trunk. That means it's safe to merge this branch into the trunk.

src\wt> bzr switch ..\trunk
src\wt> bzr revert
src\wt> bzr merge "..\Small changes"

There shouldn't be any conflicts or other problems at this point, because we already merged the trunk into our branch, and any problems should have been encountered then not now.

src\wt> bzr commit -m "Merged with branch 'Small changes'"

Woot, woot!

Oh - there's the boss again. I'd forgotten about the print preview. Better get back onto it :

src\wt> bzr switch "..\Branch002 - add new powerful print-preview system"
src\wt> bzr revert

Code away.

Oh no! I just discovered that the print preview cannot possibly work unless the account number is correct - but the account number was fixed in a different branch!

Not a problem.

src\wt> bzr merge "..\Small changes"
src\wt> bzr commit -m "Merged with branch 'Small changes'"

Ah - wonderful. Now the changes in the "Small changes" branch are also in Branch002.

What's better yet is that Bazaar is so smart that you can merge those two branches together as many times as you want, and Bazaar will keep a record of what has been previously merged and what hasn't been, so that you'll never have trouble with the one change being included multiple times!

THAT is POWERFUL.

In fact, suppose we made Branch003 for some purpose or other :

src\wt> cd..
src> bzr branch trunk Branch003
src> cd wt
src\wt> bzr switch ..\Branch003
src\wt> bzr revert

Suppose it adds some cool new features to our library of helper functions.

Suppose we finish with the branch :

src\wt> bzr merge ..\trunk
... compile and run unit tests ...
src\wt> bzr commit -m "Merged with trunk"
src\wt> bzr switch ..\trunk
src\wt> bzr revert
src\wt> bzr merge ..\Branch003
src\wt> bzr commit -m "Merged with Branch003"

At this point, Branch003, trunk, and "Small changes" ALL contain the account number change, and Branch003 and trunk both contain the new helper functions.

We don't need Branch003 any more, so we're going to switch away from it and delete it. (We could switch to any branch, but hey, for the time being, I'll switch to the trunk, 'coz I don't know where I'm going next.)

src\wt> bzr switch ..\trunk
src\wt> bzr revert
src\wt> rd ..\Branch003 /s
... and type "y" and Enter to confirm that you want to mercilessly annihilate Branch003.

Now, in some other source code versioning systems, you have to manually track which changes have been merged where. (Not mentioning any names... <cough - SVN> )

But with Bazaar, things just work, and they work perfectly!

We return to the print preview branch :

src\wt> bzr switch "..\Branch002 - add new powerful print-preview system"
src\wt> bzr revert

Oh - turns out we need those new helper functions.

If Branch003 still existed, we could get those new helper functions by merging it in to Branch002.

Or we can merge the trunk into Branch002.

But Bazaar is so cool, that it doesn't matter which one we do, or even if do both, or even if we do both multiple times! It just works!

src\wt> bzr merge ..\trunk
src\wt> bzr commit -m "Merged with trunk, to get those wonderful new helper functions."

So, merge is your friend.

See, because I grew up with Visual SourceSafe and SVN, "merge" had a negative connotation. I assumed that the change history, including the detailed comments I leave with every commit, would be marred by merging.

But with Bazaar, the history is preserved!

It's easy to prove :

src\wt> cd ..\trunk
src\trunk> bzr log

And now you get a nice big listing of all the changes that have happened. It doesn't matter how many times you merged, all changes and all commit messages are preserved.

THAT'S what I call a good source code versioning system (aka "revision control system").

So many branches and can't remember which one the working tree is currently bound to?

src\trunk> cd ..\wt
src\wt> bzr info

Ah! It tells us which branch we're bound to. Very nice.

Now, my setup is actually considerably more elaborate than this. I have multiple development machines that I switch between - one laptop for the road, and a much more powerful desktop that I prefer using when I can.

I wanted a "central trunk" from which I could run automated unit tests, and which could be my controlled release point. Plus, I have other developers help out here and there. So I really wanted a system that works this simply and this well, but works across multiple computers.

And I needed each computer to have its own "local trunk" so that the respective developers could branch away happily even when they were not connected to the internet and the central trunk.

So, in effect (although I accomplished it slightly differently), I have a folder called "central-trunk", on my controlled-release computer.

I have found that the Bazaar logs look nicest when you only have one level of indirection with merges. Since I like nice, readable logs, this is what I do :

I merge finished branches into the CENTRAL trunk, and I never delete a branch until I have merged it with that CENTRAL trunk (unless I'm abandoning the branch).

The LOCAL trunk exists ONLY TO ENSURE I CAN CREATE NEW LOCAL BRANCHES EASILY.

And to keep the local trunk in-sync with the central trunk, I run the following when connected :

src\trunk> bzr pull Z:\central-trunk

I NEVER merge or commit into my LOCAL trunk. If I'm working on two branches that need features from each other, I merge them into each other.

So basically there are three levels - the central trunk, the local trunk, and each feature branch.

The changes are "bzr pull"ed from the central trunk into the local trunk.

"bzr branch" from the local trunk creates local feature branches.

"bzr merge", NOT "bzr push", takes my local feature branch into the central trunk.

And "bzr merge" also handles inter-branch updates along the way.

-

Bazaar changed my programming life. It is a source code versioning system that works, and works brilliantly well, and isn't cumbersome. It is a pleasure to use. I enjoy the feeling of committing a change, and I enjoy merging a feature branch into the trunk. It feels like hammering another peg into the cliff face, as I climb the metaphorical cliff of whichever crazy software development endeavour I've embarked on. (And I usually embark on difficult ones! :o) )

It changed my life, even long before I started using feature branches. For a long time, I just used (and loved) the ability to track each version of the trunk, and tag releases.

Now with feature branches, things are even better again.

If I'd been given this information when I first found Bazaar, I would have accomplished more. Feature branches are a produtivity tool.

But the documentation was more focussed on contributing to open-source projects where you always branch directly off the head revision from a read-only URL somewhere, and where only specially-privileged people can commit to trunk. That's fine - that's a very powerful and very common use of Bazaar.

But it can do so much more! I hope in this article that I've helped some of you get up-to-speed with Bazaar in ways that will transform your programming life for the better.

No comments: