The basics

I think it unlikely that someone possessing the taste and discernment to be reading CVu would not be familiar with at least one version control system. So, while I want to give you a flavour of what it's like to use, I'm not going to hang about. If you'd like a proper introduction, or you don't follow something, I thoroughly recommend you consult the Mercurial book.

To start using Mercurial to keep track of a project.

$ hg init
$

This creates the repository root in the current directory.

Like CVS5with its CVS directory and Subversion6with its .svn directory, Mercurial keeps its private data in a directory. Mercifully there is only one of these, in the top level of your project. And rather than holding details of where the actual repository is to be found, the .hg directory holds the entire repository.

Next you need to specify the files you want Mercurial to track.

$ echo "There was a gibbon one morning" > pome.txt
$ hg add pome.txt
$

As you might expect, this marks the files as to be added. And as you might also expect, you need to commit to record the added files in the repository. The commit comment can be supplied on the command line; if you don't supply a comment, you'll be dropped into an editor to provide one.

There is a suggested format for these messages -- a one line summary followed by any more required detail on following lines. By default Mercurial will only display the first line of commit messages when listing changes. In these examples I'll stick to terse messages, and I'll enter them from the command line.

$ hg commit -m "My Pome" -u "Jim Hague <jim.hague@acm.org>"
$

Mercurial records the user making the change as part of the change information. It is usual to give your name and email address as I've done here. You can imagine, though, that constantly having to repeat this is a bit tedious, so you can set a default user name in a configuration file. Mercurial keeps global, user and repository configurations, and it can go in any of those.

As with Subversion, after further edits you see how your working copy differs from the repository.

$ hg status
M pome.txt
$ hg diff
diff -r 33596ef855c1 pome.txt
--- a/pome.txt  Wed Apr 23 22:36:33 2008 +0100
+++ b/pome.txt  Wed Apr 23 22:48:01 2008 +0100
@@ -1,1 +1,2 @@ There was a gibbon one morning
 There was a gibbon one morning
+said "I think I will fly to the moon".
$ hg commit -m "A great second line"
$

And look through a log of changes.

$ hg log
changeset:   1:3d65e7a57890
tag:         tip
user:        Jim Hague <jim.hague@acm.org>
date:        Wed Apr 23 22:49:10 2008 +0100
summary:     A great second line

changeset:   0:33596ef855c1
user:        Jim Hague <jim.hague@acm.org>
date:        Wed Apr 23 22:36:33 2008 +0100
summary:     My Pome

$

There are some items here that need an explanation.

The changeset identifier is in fact two identifiers separated by a colon. The first is the sequence number of the changeset in the repository, and is directly comparable to the change number in a Subversion repository. The second is a globally unique identifier for that change. As the change is copied from one repository to another (this is a distributed system, remember, even if we haven't come to that bit yet), its sequence number in any particular repository will change, but the global identifier will always remain the same.

tip is a Mercurial term. It means simply the most recent change.

Want to rename a file?

$ hg mv pome.txt poem.txt
$ hg status
A poem.txt
R pome.txt
$ hg commit -m "Rename my file"
$
(The command to rename a file is actually hg rename, but Mercurial saves Unix-trained fingers from typing embarrassment.)

At this point you may be wondering about directories. hg mkdir perhaps? Well, no. Mercurial only tracks files. To be sure, the directory a file occupies is tracked, but effectively only as a component of the file name. This has the slightly unexpected result that you can't record an empty directory in your repository.7

Given this, and the status output above that suggests strongly that Mercurial treats a rename as a copy followed by a delete, you may be worried that Mercurial won't cope at all well with rearranging your repository. Relax. Mercurial does store the details of the rename as part of the changeset, and copes very well with rearrangements8.

Want to rewind the working copy to a previous revision?

$ hg update -r 1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
$

hg update updates the working files. In this case I'm specifying that I want to go back to local changeset 1. I could also have typed -r 3d65e7a57890, or even -r 3d; when specifying the global change identifier you only need to type enough digits to make it unique.

This is all very well, but it's not exactly distributed, is it?



Footnotes

... CVS5
http://www.nongnu.org/cvs/
... Subversion6
http://subversion.tigris.org/
... repository.7
I tripped over this converting a work Subversion repository. One possibility is to create a placeholder file in the directory. In the event I created the directory (which receives build products) as part of the build instead.
... rearrangements8
The Mercurial designers justify not dealing with directories as first class objects by pointing out that provided you can correctly move files about in the tree, the other reasons for tracking directories are uncommon and do not in their opinion justify the considerable added complexity. So far I've found no reason to doubt that judgement.
Jim Hague 2009-05-22