aragost Trifork: Mercurial Kick Start Exercises


Interacting with Subversion

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.

Contents

Installation

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.

Cloning Subversion

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:

clone.png

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.

Pushing to Subversion

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.

Pulling from 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:

pull.png

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:

pushed.png

Summary

The hgsubversion extension lets you use Mercurial as a Subversion client. This immediately gives you the following advantages compared to the normal Subversion client:

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.