Vendor Branches: Mercurial

As I explained previously (Vendor Branches: The Solution), Mercurial along with its Queues extension is the cornerstone of the way we manage our customisations to OfBiz. In this article I will describe how we achieve this.

The basic idea is that our main repository should contain the standard vendor code, plus our files that were added under hot-deploy. Any changes that affect vendor files are not included in our main repository. Instead there is a sub-repository which stores patches. Mercurial’s Mq extension manages these patches, applying and removing them on demand. Our patches are also under version control.

Although I am going to use command line examples below, there are GUI-based programs that do the same things. Hopefully you can convert the command line examples into the right options in your preferred software.

To enable this, we initialised a Mercurial repository along with a queue. Version control was enabled for the queue. The commands to do this vary depending on the version of Mercurial you are using, so I’m not going to give those commands here. Read the documentation for the version you are using and don’t just blindly follow what someone has written.

We exported the current version of OfBiz trunk from apache.org, and copied the files into the new repository. Then from the top directory in the new repository:


hg add
hg commit
hg tag REV_xxxxxx

The xxxxxx was replaced with the vendor’s revision number.

Any files we create under the hot-deploy folder are dealt with in the usual way:


hg add
hg commit

Changes to the vendor code are treated differently.


hg add
hg qnew -m'descriptive comment' patch-name

We now have our changes stored in a patch named patch-name, and the patch is applied to our copy of vendor’s source. If we then make more changes, we need to decide whether they should be included in the same patch. If the answer is yes:


hg add
hg qrefresh

If we haven’t added any new files, there’s no need for the hg add above.

If however they should be in their own separate patch:


hg add
hg qnew -m'Another descriptive comment' another-patch-name

So now we have the standard vendor code, with two patches applied. As yet, the patches have not been recorded in their version control system, which by default lives in the folder .hg/patches under our main repository. To do this, we need to first remove (“pop”) the patches from the source.


hg qpop -a
cd .hg/patches
hg add
hg commit
cd ../..

Notice that the commit instruction is committing the new patches to the patch repository. The -a option to the qpop means to pop all patches. Now that we’ve done that, we might want to apply our patches to the vendor code again:


hg qpush -a

Once again, the -a means all patches. You can see the names of the currently applied patches using:


hg qapplied

Perhaps at this point we realise we made a mistake, and that first patch called patch-name does not involve any vendor code, and hence should not have been a patch. We fix this by making it the current patch using qpop, checking it is indeed the current patch using qapplied, and then telling Mercurial to move it from a patch to the proper repository using qfinish. We then go to the patches directory and commit the changes, so the patch repository records that it now has only one patch.


hg qpop
hg qapplied
hg qfinish -a
cd .hg/patches
hg commit

Those commands cover most of the day-to-day necessities. You’ll find yourself qpushing and qpopping all the time, as you can’t do a normal commit unless all patches have first been qpopped. Nor can you push to another repository while patches are applied.

If you can’t commit while patches are applied, but you need patches applied while developing and testing your code, what do you do? To answer that, let’s assume there are two patches called first-patch and second-patch that are applied to your code while you are adding a new feature under the hot-deploy folder. Once you are happy with that new feature, you need to commit it to the main repository. It doesn’t contain any changes to vendor code, so you don’t want it as a patch.

First create it as patch (we’ll call it my-patch), then change the order of your patches, test all is still okay, then commit the new patch.


hg qnew -m'My new feature' my-patch
hg qpop -a
cd .hg/patches

Now edit the series file in the patches folder. Initially it should look like

first-patch
second-patch
my-patch

Change it to look like

my-patch
first-patch
second-patch

and save it. Now we need to check the patches all still apply successfully (qpush -a), and if they do we make sure my-patch is the only one applied (qpop -a, qpush), that the patch has the comment and files we want (-v tip), and commit it to the main repository (qfinish -a). You might want to use hg qapplied along the way to make sure which patches are applied at various stages.


cd ../..
hg qpush -a
hg qpop -a
hg qpush
hg -v tip
hg qfinish -a

There’s just one more important day-to-day task to know about. Sometimes we want to copy a file from the vendor code into our hot-deploy section, so we can safely make minor changes. However we’d like future changes to the original vendor file to be applied to our copy as well.


hg copy path/to/vendor/file.ext path/to/our/version/ourfile.ext

By using Mercurial’s copy, changes made by the vendor to path/to/vendor/file.ext will also be applied to our path/to/our/version/ourfile.ext.

All that remains is for me to explain how we actually update our code with a new vendor version. I’ll leave that for my next article.

Next: Vendor Branches: Mercurial Part 2
Previous: Vendor Branches: The Solution