Over the last few years a number of new version control systems have started to appear all designed to replace CVS which until recently has been the de-facto standard version control system for nearly all open source software. The reason for this is mostly that when CVS was designed disk-space was expensive and networks were fast. Today the reverse is true and the new systems are designed to minimise network traffic. In addition the set known as Distributed Version Control Systems provide a way to de-centralize software development. It becomes no longer necessary to maintain a central repository with a core group of developers with commit rights. Instead each developer can maintain their own clone of the repository with all history and useful features can be merged among interested developers.
Among the set of available systems Git gets a lot of noise as its being used by the Linux developers. However it is heavily unix-centric at this time. Mercurial however does have good windows support. Its very easy to install and use and there is even a TortoiseHg plugin for explorer - although this needs some work yet as it interfered with my TortoiseCVS icons.
An attractive feature of Mercurial for me is the integrated patch management plugin 'Mercurial Queues' or mq. I typically have a number of on-going patches in my Tcl and Tk working directories. What happens is that I begin doing some job on a clean current CVS checkout. Then someone on #tcl raises something that is worth looking at. So I archive the current set of work as a patch using cvs diff > jobname-data.patch and apply the reverse patch to get a clean copy again and then deal with the problem. Later I can go back, reapply the patch and continue.
The mq patch control looks like being ideal for this. Further the ease and speed of creating repository clones will likely obviate this working practice. Its likely that instead of archiving via a patch, I will in future simply make a clone from a local copy and get to work.
Importing from CVS
Mercurial has a built-in conversion extension that can handle CVS. It needs a local copy of the CVS repository to be really effective and it needs cvsps to turn the CVS history into patchsets. cvsps isn't really working on Windows so I ran the conversion on a Solaris box. This turned out to be very easy. Sourceforge permit repository mirroring using rsync so I can easily maintain a running mirror of the CVS repository. The Mercurial convert tool also tracks the patchsets that have already been handled. So repeatedly running the conversion tool will serve to update a Mercurial repository from the CVS mirror. This is great as it allows me to create a live Mercurial mirror of the Tcl and Tk CVS repositories. You can in fact see them: Tcl Mercurial mirror, Tk Mercurial mirror and also a TclUDP Mercurial mirror.
The necessary commands to create each mirror can be run using cron and are just:
rsync -av 'rsync://tcl.cvs.sourceforge.net/cvsroot/tcl/*' /opt/mirrors/tcl cd /opt/hgrepos cvs -d:local:/opt/mirrors/tcl co -d tcl.cvs tcl hg convert --datesort tcl.cvs tcl
The above yields a Mercurial mirror in /opt/hgrepos/tcl. It does take a while on my UltraSparc but a modern machine can probably process it pretty fast. Now that the intial import is done - each update only takes a few seconds and the Mercurial mirror stays current.
I have not really looked into this but I believe it is possible to reduce the size of the mirror by importing only patchsets for the main development trunk, or only those since a certain date. I just imported the whole CVS repository so we have everything from 1998 onwards. At that it's not too huge - a clone of Tk is about 70MB and Tcl is a little over 100MB.
Using the Mercurial mirror
So now I have a mirror, back to some real work. I'll test mq using the TIP #213 work. Back on the windows box I need to clone the repository and create a mq patch.
hg clone http://www.patthoyts.tk/repo/tk tk.tip213 ht qinit -c hg qnew -l tip213.description tip213 patch -p0 < ..\tk\tip213-20080513.patch hg add library\fontdlg.tcl tests\fontdlg.test hg qrefresh
Lets look at some of the output:
C:\opt\tcl\src\tk.tip213<patch -p0 < ..\tk\tip213.patch patching file `generic/tkInt.h' patching file `generic/tkUtil.c' patching file `generic/tkWindow.c' patching file `library/console.tcl' patching file `library/fontdlg.tcl' patching file `library/msgs/de.msg' patching file `library/msgs/en.msg' patching file `library/tclIndex' patching file `library/tk.tcl' patching file `tests/fontdlg.test' patching file `tests/winDialog.test' patching file `win/tkWinDialog.c' C:\opt\tcl\src\tk.tip213<hg status M generic\tkInt.h M generic\tkUtil.c M generic\tkWindow.c M library\console.tcl M library\msgs\de.msg M library\msgs\en.msg M library\tclIndex M library\tk.tcl M tests\winDialog.test M win\tkWinDialog.c ? library\fontdlg.tcl ? tests\fontdlg.test C:\opt\tcl\src\tk.tip213<hg add library\fontdlg.tcl tests\fontdlg.test C:\opt\tcl\src\tk.tip213<hg qtop tip213 C:\opt\tcl\src\tk.tip213<hg qrefresh C:\opt\tcl\src\tk.tip213<hg status
Line ending problems
The above seems great. I've now got my patch managed by mq. To update the patch I can just use hg qpop, hg pull -u, hg qpush tip213.
Or do I.
It turns out that Mercurial ignores line endings. Thats OK - the web informs us that we can use the win32text extension to manage the CRLF issues - and indeed this works fine. Unfortunately mq completely fails to make use of this. If we do a hg qpop followed by a hg qpush of the same patch it will fail to apply. Looking at the rejects all of them have unix line-endings. The completely evil hack that I currently perform to work around this is to open the patch in repo/.hg/patches/ and convert if from unix to dos line-endings using emacs just before I do the hg qpush.
I assume that this will be addressed shortly because without this being fixed the mq extension is basically useless for any Windows developer.
Other than the mq line-ending problem described above I have found Mercurial very easy to use and to setup. It works well on both large and small projects. Of course its written in Python, so maybe I should look around some more. fossil might be more appropriate for a Tcl/Tk developer but I'll probably have to write my own CVS import for that - and it doesnt have anything like mq. More actual development use will show how useful that turns out to be.
Pat Thoyts. 24 May 2008.