Thursday 13 December 2012

Who knew mq and rebase extensions play nice together?

Some days, you learn things that you immediately know will be useful again and again.

Several months ago, I started incorporating the mq extension into my mercurial overflow. Mutable local history changed my work-flow entirely, allowing me to do work on larger overall changes locally, but in organized units of change. Aside from aesthetics, this sort of had a similar effect as probably anyone who learned how to program and then learned how to use version control: once you realize what it does for you, you can work a lot faster without fear of breaking things because, well, you can always revert. Well, mutable local history made things even more forgiving for me.

Every so often, however, I run into a messy merge, where some patch series I've been working on for some time overlaps with a lot of the changes others have pushed in the meantime. I should say that I'd really only gotten used to
the qinit|qnew|qpop|qpush commands.  So, merging with upstream changes went kind of like this:

$ hg qnew patchname # and now make some changes
$ hg qrefresh # possibly repeat this and previous a number of times
$ hg qpop # repeat until empty
$ hg pull -u
$ hg qpush # rage at the conflicts preventing changes from applying cleanly


And at this point some manual inspection of a bunch of FooFactory.java.rej files and the corresponding FooFactory.java files, adding and munging my changes with the conflicting section. If there are any conflicts. Sometimes I am lucky and there aren't.

This was my work-flow, until yesterday. I had been working some improvements to a set of widely-used classes and interfaces, and someone else had been pushing a bunch of package and bundle reorganisation work. When I qpush-ed over this, I learned a thing: qpush (even with -m) does not seem to be aware in any way of file renames. Turns out that this meant that most of this 200kB patch simply was not able to apply.

This was unacceptable. No way am I applying all that manually. I made a fresh local clone and set out on a web hunt to find a solution. The mercurial wiki page for MqExtension was pointing to a page titled MqMerge which didn't sound a lot different from what I was already doing, but the page did seem quite outdated and referred to some work being done to create a rebase extension. It seems this work has gone quite well, and its wiki page is even up-to-date. And, as it turns out, the merging done by the rebase command tracks file renames properly, and when there are merge conflicts it know to open a 3-way vim session to help me sort them out. Where have you been all my life?!?

So now my workflow looks more like this:

$ hg qnew patchname # and now make some changes
$ hg qrefresh # possibly repeat this and previous a number of times
$ hg pull
$ hg rebase -s <rev1> -d <rev2>


Here, rev1 is the oldest applied patch tracked by mq, and rev2 of course is the most recent changeset in the newly pulled upstream changes. The series of applied patches is merged in sequence onto the new parent. Even the mq tags follow the rebase, so once it's done I still have mutable local history until I'm ready to qfinish. And sorting out the conflicts takes me far less time because of the 3-way vim session.

Game-changing. Really. For as long as I am involved with projects that use mercurial, learning that rebase and mq play nice is going to be saving me time and headaches over and over and over.

Got any similarly life-changing tips? Comments welcome.