I often like to split patches up into independent pieces, for ease of reviewing by both reviewers and myself. You can split off preparatory refactorings, low-level mechanism from high-level users, features from tests, etc., making it much easier to evaluate the sanity of each piece.
But it’s something of a pain to do. If I’ve been hacking along and accumulated a monster patch, with stock hg and mq I’d do:
hg qref -X '*' # get all the changes in the working directory; only # needed if you've been qref'ing along the way hg qref -I '...pattern...' # put in any touched files hg qnew temp # stash away the rest so you can edit the patch hg qpop hg qpop # go back to unpatched version emacs $(hg root --mq)/patchname # hack out the pieces you don't want, # put them in /tmp/p or somewhere... hg qpush # reapply just the parts you want patch -p1 < /tmp/p ... # you get the point. There'll be a qfold somewhere in here...
and on and on. It’s a major pain. I even started working on a web-based patch munging tool because I was doing it so often.
Then I discovered qcrecord, part of the crecord extension. It is teh awesome with a capital T (and A, but this is a family blog). It gives you a mostly-spiffy-but-slightly-clunky curses (textual) interface to select which files to include, and within those files which patch chunks to include, and within those chunks which individual lines to include. That last part, especially, is way cool — it lets you do things that you’d have to be crazy to attempt working with the raw patches, and are a major nuisance with the raw files.
Assuming you are again starting with a huge patch that you’ve been qreffing, the workflow goes something like:
hg qref -X '*' hg qcrecord my-patch-part1 hg qcrecord my-patch-part2 hg qcrecord my-patch-part3 hg qpop -a hg qrm original-patchname hg qpush -a
Way, way nicer. No more dangerous direct edits of patch files. But what’s that messy business about nuking the original patch? Hold that thought.
Now that you have a nicely split-up patch series, you’ll be wanting to edit various parts of it. As usual with mq, you qpop or qgoto to the patch you want to hack on, then edit it, and finally qref (qrefresh). But many times you’ll end up putting in some bits and pieces that really belong in the other patches. So if you were working on my-patch-part2 and made some changes that really belong in my-patch-part3, you do something like:
hg qcrecord piece-meant-for-part3 # only select the part intended for part3 hg qnew remaining-updates-for-part2 # make a patch with the rest of the updates, to go into part2 hg qgoto my-patch-part2 hg qpush --move remaining-updates-for-part2 # now we have part2 and its updates adjacent hg qpop hg qfold remaining-updates-for-part2 # fold them together, producing a final part2 hg qpush hg qfold my-patch-part3 # fold in part3 with its updates from the beginning hg qmv my-patch-part3 # and rename, mangling the comment
or at least, that’s what I generally do. If I were smarter, I would use qcrecord to pick out the remaining updates for part2, making it just:
hg qcrecord more-part2 # select everything intended for part2 hg qnew update-part3 # make a patch with the rest, intended for part3 hg qfold my-patch-part3 # fold to make a final part3 hg qmv my-patch-part3 # ...with the wrong name, so fix and mess up the comment hg qgoto my-patch-part2 hg qfold more-part2 # and make a final part2
but that’s still a mess. The fundamental problem is that, as great as qcrecord is, it always wants to create a new patch. And you don’t.
Enter qcrefresh. It doesn’t exist, but you can get it by replacing your stock crecord with
hg clone https://sfink@bitbucket.org/sfink/crecord # Obsolete!
Update: it has been merged into the main crecord repo! Use
hg clone https://bitbucket.org/edgimar/crecord
It does the obvious thing — it does the equivalent of a qrefresh, except it uses the crecord interface to select what parts should end up in the current patch. So now the above is:
hg qcref # Keep everything you want for the current patch hg qnew update-part3 hg qfold my-patch-part3 hg qmv my-patch-part3
Still a little bit of juggling (though you could alias the latter 3 commands in your ~/.hgrc, I guess.) It would be nice if qfold had a “reverse fold” option.
Finally, when splitting up a large patch you often want to keep the original patch’s name and comment, so you’d really do:
hg qcref # keep just the parts you want in the main patch hg qcrec my-patch-part2 # make a final part2 hg qcrec my-patch-part3 # make a final part3
And life is good.
Is it the same what git add -p does?
Well… no. But also yes.
First, git’s -p or –interactive mode is roughly equivalent to hg’s ‘record’ extension. Both are prompt-based UIs, not curses. The ‘crecord’ extension performs the same function as ‘record’ but with a much nicer fullscreen curses UI.
But second, git’s -p operates on the cache/stage/index/whatever it’s called these days. hg doesn’t have that notion. But the commands I’m talking about here (qcrecord, qcrefresh) operate on mq’s patch queues, which if you only have one patch in your queue ends up being used for many of the same things as git’s index. But mq is really about a whole stack of patches. git’s equivalent is Stacked Git (stgit), which does things slightly differently from mq but overall is very, very similar. I don’t know if you can use interactive mode with stgit.
I might be in the minority, but I hate reviewing multiple patches that overlap the same areas of code. Being forced remember code changes over multiple patches is a huge pain. In the case of Mozilla, I tend to use MXR a lot to act as my baseline code when reviewing changes. I can’t do that when multiple patches muck around the same areas of code. The patches themselves usually don’t have enough context to be enough of a baseline on their own.
For code in different areas, multiple patches are fine.
I use the Mercurial Shelve extension in conjunction with MQ to move hunks from one patch to another. Well actually I use TortoiseHg which comes with both Shelve and MQ and which provides a very nice UI.
Phil
So this is the rough equivalent of using git add -p and interactive rebase on a temporary feature branch, then? Or is there a distinction I’m missing between an hg patch queue and a git branch?
Hm, it looks like there’s a vim plugin that can do a vimdiff-style interactive add-by-hunk, which I thank you for inspiring me to look for!
Eevee, fugitive.vim looks to be kind of awesome. If only it did mercurial!
That’d just be silly. fuhgive isn’t a word.