Monday, March 21, 2011

Mercurial Mq

Mercurial is an awesome distributed version control system, abbreviated "hg."  Hg's command approach emphasizes small well named commands to make it easier to learn and understand.  It also comes "safe" and "easy" out of the box, but allows you to simply turn on built-in extensions to gain all kinds of shoot-yourself-in-the-foot power.

One of those extensions is called Mq.  This allows you to manage a set of patches in a queue.  You may be asking yourself, "What the hell does that mean?!"  Instead of describing how it works, I'll describe when and how you'd use it.

Suppose you are developing a new feature.  You're changing code here, changing code there, adding tests here and there, and so on.  But suddenly you notice some code that could use improving.  But that code doesn't have anything to do w/ the feature you are working on.  What do you do?!

You could just change it.  But if you do, you will have two unrelated changes in the same changeset.  This isn't the end of the world...  But it's not good either.  For one thing, you might introduce a bug.  And now when someone is trying to track down that bug, it will be really non-intuitive that your changeset may have introduced that bug.  Basically, tangling changes is just bad.  It means you're moving too fast, doing too many things at once.  So let's not just change it.

You could hope you'll remember about it for later.  Or you could write it down and hope you'll see your note and come back to it.  But, come on, that ain't gonna happen.

OR you could use the mq extension.  mq will allow you to take all the changes you've made on your feature and put them in a patch in a queue.  Then create a new patch in the queue, and improve the unrelated code.  Now go back to the first patch and keep working.  When you're all done, "finish" all the patches in the queue and they turn into permanent changesets!  ta da!

Here's what that little story would look like, assuming it's the first time you've ever used mq in this repo:
# work on your feature, notice code that needs to be improved...

hg init --mq #only needed first time you use mq
hg qnew -m "my new feature" new-feature #automatically includes all working dir changes in the patch
hg qnew -m "improved some code" improve-code

# improve the code...

hg qrefresh #adds working dir changes to current patch
hg qpop #takes improve-code changes out of working dir, drops you back to just the new-feature changes

# finish your feature...

hg qrefresh #adds working dir changes to new-feature patch
hg qfinish -a #converts all patches into permanent changesets
This might look like a lot to you at first glance, but it's not. And in practice its surprisingly simple.

You move between patches with hg qpop and qpush.  qpop takes a patch out of your working directory and drops to the previous patch in the queue.  qpush does the opposite, adding the changes of the next patch in the queue back into your working directory.  You can have any number of patches, one after the other, in your queue.  And, if you need to for some reason, you can reorder patches.  Check out the MqExtension page for more.  And after you enable it, "hg help mq" makes it very easy to learn all the various commands.

Very useful feature, and addictive once you get used to it!

2 comments:

  1. This is exactly what I wanted a while back and now that I mostly have hg under control it's time to give this a whirl because it would have saved me a ton of time and like you mentioned in your post, I would prefer refactors were in their own change sets.

    This is also great when you have to make a refactor somewhere to finish a feature.

    ReplyDelete
  2. I had to try it out first to see how it works. But right after had a chance to take it into real world use, since I was doing code cleaning and new features at the same time. It was actually simpler than it looked like and worked great!

    Thanks for sharing!

    ReplyDelete