Monday, April 4, 2011

Mercurial: Record

Here's another post about some of the more advanced features of Mercurial.  The last two posts were about the Mq extension, which allows you to maintain a queue of patches with distinct changes in them.  This allows you to keep separate changes separate, and to test those changes separately.

The trick with Mq though is that you have to know ahead of time that you are going to make separate changes and you have to go create a new patch for those changes.  Before I go any further, I want to stress that this is actually a good thing!  You should proactively keep your changes separate.  This helps ensure you don't bite off too much at once, or fail to test you changes.

But sometimes you forget to be proactive about separating your changes and then you want to untangle them after the fact.  You can do this using the Record extension.  This is another extension that ships with Mercurial, but you have to enable it to use it.

The Record extension is invoked with "hg record" and will go through the files you've changed (or just the ones you tell it to look at) and will iterative over each change "hunk" in the file.  A change hunk is a set of sequentially changed lines.  For each hunk it asks you if you want to record the change or not.  You simply say "y" if you want it, and "n" if you don't.

The result is a new commit that contains the changes you recorded.  It's a surprisingly easy process.

However, there's a flaw with this approach.  The resulting commit might be broken: it might not compile, or the tests might not pass.  And since it turns directly into a commit, you don't really get the chance to test it...  Now you could qnew the other changes in your working directory, then pop that patch to get back to the last commit, and test.  And if you notice any issues you can qimport the change that record created in order fix the problems. Then you can qfinish it, and qpush to get your pending changes back.  Follow that?

Or instead of using hg record which results in a new commit, you can use hg qrecord which results in a new mq patch.  You'll still need to qnew the working directory changes.  But now you wont have to qimport if you need to make any changes to the original patch.  I prefer this method because it seems more sane that you end up with mutable patches after prying changes apart instead of finished commits.