Some tips on Mercurial Patch Queues (mq)
Mercurial is a Distributed Version Control System and MQ is a method of creating and managing patches against 3rd party source. You can also use it for writing code that only is only submitted to the main repository when you are ready. This is a great way to play around with various changes without tainting the repo logs with all your failures
When I started using Mercurial Patch Queues (or just mq for short) it was confusing and frustrating as you keep running into things like :
abort: local changes found, refresh first
abort: cannot commit over an applied mq patch
And then you’re stuck. You often find that you were just doing some tests, or fixing a totally unrelated thing to the current patch in the queue – and the last thing you want to do is apply these changes to this patch. These thing were just frustrating the hell out of me. Everybody and his uncle is touting MQ to be the greatest thing that has ever been added to Mercurial, and I just couldn’t figure it out. That is, until the light went on, and I finally clicked some basic concepts with MQ.
Now that I understood these basic concepts of MQ, I am part of the crowd that says MQ is the best thing ever
Concept 1: The patches are a separate entity from your current repo.
You need to treat it as such. All your patches are housed in the .hg/patches directory as diff files. If you create a patch repository(hg qinit -c), it’s a separate entity with it’s own repo history. See the two areas as separate and that they should be managed separately, and MQ already becomes easier.
Concept 2: It’s okay to edit the .hg/patches/series file
It’s important to realize that MQ works in a queue fashion. You push and pop things onto and off of the queue and the queue pretty much stays the same. But sometimes you want patch 3 to apply before patch 1 and 2.
When I was trying to figure out how to use MQ, I really hit a dead end on this one. You read the HG book on MQ, and it tells you you can re-order the patches in the series file, but then proceeds to give you a big warning that “This could be dangerous”. It can be dangerous if patch 2 depends on patch1 and you then swap them around – but as long as you keep those two patches in the same order you can easily put unrelated patch 3 and 4 in front of them. Often, it’s even better to fold patch 2 into patch 1, so that you end up with 1 patch.
How do these 2 concepts actually help ?
I think the best would be to explain by examples.
The other day I was working on a patch with one of the mercurial crew members. He wrote a patch, and sent it to the mailing list. Now mercurial gives you various options of sharing code, and one of the more popular ways is to just send patches. So I grab this patch and save it to a file. Then I clone the main repo and apply the patch onto it.
There’s one of two ways to apply this patch: Either via hg import or via mq with qimport. I prefer using mq for this as it gives me the freedom to apply (qpush) or un-apply (qpop) the patch. Makes testing a breeze – especially if you want to see how the code performs with or without the patch.
hg clone devrepo patchtestrepo
cd patchtestrepo
hg qinit
#Import the patch
hg qimport ../path/to/patch.diff
#Apply the patch
hg qpush
#Remove the patch
hg qpop
Now, it’s time for trouble. I wanted to create a test case for the patch to make sure it’s working correctly – so I start hacking away with the patch applied. The test works great and the patch seems to be doing the right thing. But to ensure that the test is working, correctly, I want to run the test on clean version of the repo.
Easy, qpop to make the applied patch go away, right ? Oops, no can do: abort: local changes found, refresh first. Oh noes, I don’t want this change to be part of the patch. I would prefer if it’s separate. So, now I need to create a new patch.
hg qnew -f mytest
Nice, now I had a new patch with all the test code in that I just wrote. The -f takes all the uncommitted changes that I just made and posted them a new patch.
So, I then go hg qpop -a to remove all the patches. And I proceed to run the test. But, my test is gone – the patch I just wrote is the top most patch in the queue. In comes Concept 2. As long as patch 2 (mytest patch) doesn’t depend on changes in patch 1, I can just edit the ./hg/patch/series file and swap the order around. The series file will be changed from this:
patch.diff
mytest
into this:
mytest
patch.diff
Having done that, just do a qpush and the test is back without the patch.diff and I can make sure it’s failing on the clean version of the software like it’s supposed to do. (What use is a test if it doesn’t fail when it’s supposed to)
So, whenever you run into the “abort: local changes found, refresh first” issue, just do a qnew -f and this often solves most of the problems. If you’d like to have these new changes applied without the other changes, do a qpop -a, modfiy the series file, and qpush to your patch.
Well, that’s how I use MQ, in a nutshell, to assist with writing patches in a contained area without actually having multiple log entries the repo… MQ is really a great way of writing and sharing changes. There’s a lot more in the HG book on patches and MQ, but I just found that even after reading chapter’s 12 and 13 that these two concepts didn’t come through strongly and understanding them, in my opinion, made working with MQ a whole lot easier.
About Me…
The other day I was compiling my CV, and realized that I’ve done so many things in the last 10 years, and I actually haven’t kept any records of my doings and experiences. So, it’s time for me to start a blog (better late than never)
I’m not the best at keeping these things up to date, but I’ll do my best on this one…
So, just some basic stuff about me:
I am Gerard Korsten, not the South African singer nor his son, the famous conductor. I’m probably family of them, but as far as I know it’s not close in any way shape or form.
I am South African and a nerd – so I’m into computers, programming, building websites and setting up servers and stuff.
Hmmm, I think that’s enough of an intro about me. Hope you find some of the stuff here useful.