The hgsubversion extension turns Mercurial into a Subversion client. This gives you offline commits and all the other nice features of Mercurial while letting you push changesets back to Subversion.
The extension requires the Python Subversion bindings. The bindings are bundled with TortoiseHg on Windows. On Linux you want to search for a package called python-subversion in your package manager.
Install the extension itself by running:
$ hg clone https://hgsubversion.googlecode.com/hg/ hgsubversion
Now put:
[extensions] hgsubversion = path/to/hgsubversion
into your configuration file to load the extension. If you have installed the extension using your package manager, then it has most likely been put in your PYTHONPATH and so you just need to add:
[extensions] hgsubversion =
Use hg help hgsubversion to double-check that the extension is enabled.
We have prepared a Subversion repository with a small Hello World program. There is a repository for each group, named hello1, hello2, …, hello5. Please clone the one corresponding to your group number:
alice$ hg clone http://mercurial.aragost.com/svn/helloX hello-hg [r1] mg: Created initial layout. [r2] mg: First known version by Brian Kernighan, 1974. [r3] mg: Include stdio.h. [r4] mg: Output newline. [r5] mg: Version 1.0. [r6] mg: Capitalized! [r7] mg: Comments are good. [r8] mg: Added README. [r9] mg: Added Makefile. [r10] mg: Ignore compiled program. pulled 9 revisions updating to branch default 4 files updated, 0 files merged, 0 files removed, 0 files unresolved alice$ cd hello-hg
Note
If a http(s):// URL doesn’t work for you, then try with svn+http(s):// instead. That should tell Mercurial in unambiguous terms that you’re cloning from Subversion.
She inspects the repository with thg log to see that the tags were defined properly:
The final Subversion revision sets the svn:ignore property to hello in order to ignore the compiled binary. In Mercurial, ignore patterns are controlled in the top-level .hgignore file. This file can be generated with the command:
alice$ hg svn genignore
Normally, one would commit the .hgignore file in order to share it with everybody who uses the repository. However, since Subversion is still the master in this setup, you might not want to push the .hgignore file back there. For that reason, the generated .hgignore file ignores itself. You can regenerate it from time to time with hg svn genignore --force.
Alice can now edit the files and commit them with Mercurial:
alice$ echo "/* The End. */" >> hello.c alice$ hg diff diff -r e3314036fa9f hello.c --- a/hello.c Fri Mar 12 10:20:45 2010 +0000 +++ b/hello.c Fri Mar 12 21:20:10 2010 +0000 @@ -7,3 +7,4 @@ printf("Hello, World!\n"); return 0; } +/* The End. */ alice$ hg commit -m "Added footer."
The change is not yet on the Subversion server — like normal, hg push is needed. Pushing the changeset involves rewriting it. This is because the Subversion server is the master and when you commit to a Subversion server, it will determine the username and timestamp that goes into the revision. On the Mercurial side, the changeset is rewritten to reflect the updated meta data:
alice$ hg outgoing comparing with http://mercurial.aragost.com/svn/helloX changeset: 9:2dc661f0cdc8 tag: tip user: Alice <alice@example.net> date: Fri Mar 12 21:20:15 2010 +0000 summary: Added footer. alice$ hg push pushing to http://mercurial.aragost.com/svn/helloX searching for changes [r11] alice: Added footer. pulled 1 revisions saved backup bundle to $HOME/hello-hg/.hg/strip-backup/2dc661f0cdc8-backup.hg
You can see the updated changeset like this:
alice$ hg tip changeset: 9:a545b3d1c21d tag: tip user: alice@66d5687c-2042-4ccc-9f0a-7e74790c38a1 date: Fri Mar 12 21:25:00 2010 +0000 summary: Added footer.
Notice how the author and timestamp changed because the Subversion server updated the meta data. Because changesets are rewritten, you should not make further Mercurial clones of your original clone. If you do so, you will have to manually strip the old versions of changesets that are pushed to Subversion.
Alice makes a couple of more local changes in her Mercurial clone:
alice$ hg outgoing comparing with http://mercurial.aragost.com/svn/helloX changeset: 10:9a6e43d86b16 user: Alice <alice@example.net> date: Fri Mar 12 21:26:00 2010 +0000 summary: Express greater joy! changeset: 11:f3bd093438f1 tag: tip user: Alice <alice@example.net> date: Fri Mar 12 21:27:00 2010 +0000 summary: Added simple MIT license header.
Meanwhile, the Subversion repository has been changed:
alice$ hg incoming incoming changes from http://mercurial.aragost.com/svn/helloX revision: 12 user: alice date: 2010-03-12T21:26:30.0Z message: List email address for bug reports. alice$ hg pull pulling from http://mercurial.aragost.com/svn/helloX [r12] alice: List email address for bug reports. pulled 1 revisions (run 'hg update' to get a working copy)
The new revision causes a fork in the history:
When Alice push, her two changesets (7d780c6154fb and 12445668eefc) will automatically be rebased on top of the revision she just pulled from Subversion (2099057558d2):
alice$ hg push pushing to http://mercurial.aragost.com/svn/helloX searching for changes [r13] alice: Express greater joy! pulled 1 revisions saved backup bundle to $HOME/hello-hg/.hg/strip-backup/xxxxxxxxxxxx-backup.hg [r14] alice: Added simple MIT license header. pulled 1 revisions saved backup bundle to $HOME/hello-hg/.hg/strip-backup/xxxxxxxxxxxx-backup.hg
Again, this is necessary since Subversion is a strictly linear system. The new history looks like this:
The hgsubversion extension lets you use Mercurial as a Subversion client. This immediately gives you the following advantages compared to the normal Subversion client:
Full history on client. This enables the fast bug-hunting techniques offered by Mercurial, be it simply searching the history for the introduction of a particular string with hg grep or hg annotate, or be it the more advanced binary search done with hg bisect.
People simply begin working differently with their tools when they become sufficiently fast. Mercurial is designed to make local access fast.
Local (offline) commits. Version control is all about tracking the development of the code. In particular, version control should let people experiment with new ideas and features. To do this, people need to commit often — maybe every five minutes when they think they have come a little closer to a working solution.
Subversion forces people to publish these revisions immediately. People will therefore tend to hold off on committing, especially to the trunk, out of fear of breaking the build. If people work on a branch, all these commits will pollute the history and make reviewing hard.
Mercurial lets people commit as often as they feel necessary. The changes can later be refined into fewer, and more meaningful commits that are easy to review and which avoids a lot of false starts. These refined commits are then pushed to the Subversion server.
Distributed development. You saw above how changesets are rebased when pushed to Subversion. This makes it tedious to collaborate with others if the changesets on which you are working are continuously changed under your feet.
However, it is possible to work in a distributed fashion on a new feature. It merely requires that the collaborators wait with pushing to Subversion until they are done. So you can make as many clones as you want and push/pull between them as you like. When the feature is done, you integrate it in Subversion and ask the rest of your team to ditch their local clones and pull the rebased changesets from the Subversion server.
The hgsubversion extension can also be used as a way to convert to Mercurial. Mercurial already ships with a convert extension, but hgsubversion is known to convert some repositories better and more accurately.
The main problem in converting from Subversion (or really, any legacy system) to Mercurial is the lack of rigor in the old systems. Subversion has no tags and no branches, it only has a (very) strong convention that says that a copy from trunk/ to branches/foo indicates the creation of a branch named “foo”. But nothing prevents someone from copying half of trunk/ to branches/foo, or from making a commit that modifies files in both trunk/ and branches/foo simultaneously.
This kind of “cruft” cannot always be represented in a meaningful way in Mercurial, but hgsubversion still does a good job at interpreting the developers’ intentions.