Here is a brilliant article about distributed source control systems in general with an emphasis on Git and a clear look at the pros and cons of different approaches.

Set Up

Before starting make sure this is setup.

$HOME/.hgrc
[ui]
username = Chris X Edwards <cxe-hg@xed.ch>
editor = /usr/bin/vim

Starting A Project With Some Files To Track

$ cd coolware # Be in the directory you want to manage.
$ hg init     # Creates .hg for this directory.
$ hg status   # Show all non-ignored files.
$ hg add      # Add all 'unknown' files.
$ hg add coolfile1 coolfile2  # Or add specific files.
$ hg ci -u xed -m "Initial coolness" # 1st commit.
$ hg parents  # Show currently checked out revision.
$ hg status -A # Show status of all tracked files.

Complete Example Of General Use

$ mkdir coolware
$ cd coolware
$ echo "/* The coolware main program */" > coolware.c
$ hg init
$ hg add coolware.c
$ hg ci -u xed -m "Initial coolness." coolware.c
$ cd ..
$ hg clone coolware coolware-dev
$ cd coolware-dev
$ echo "/* A very cool program. */" >> coolware.c
$ hg ci -u xed -m "Writing cool software." coolware.c
$ cd ../coolware
$ hg pull ../coolware-dev/
pulling from ../coolware-dev/
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
$ cat coolware.c
/* The coolware main program */
$ hg up
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cat coolware.c
/* The coolware main program */
/* A very cool program. */

Cloning A Repository

If a project lives somewhere and you want it somewhere else, this is a possible technique for doing that:

hg clone ssh://xed@xalab.example.edu/../sysadmin/safe/chemmgr/

Note the weird ssh URL format, the host is separated from the path by the path separator (/). Also note that the path is relative to the home directory. In this example, I’m showing how to lift a repository out of someone else’s home directory as an example of how to get around this overly helpful feature.

Here is how to specify absolute paths.

hg clone ssh://user@server//home/projects/alpha/

If you need to use a non-standard port. Here SSH listens to 2020.

hg clone ssh://xed@70.59.33.247:2020/pathfromhome/myproject/

If you started the project behind a firewall and want to establish it on your main well-connected server, do something like this from inside the project’s local directory (which will have been set up for hg with hg init etc):

hg clone . ssh://xed@remote.xed.ch//home/xed/project/goodtimes_project/
ssh xed@remote.xed.ch
cd ~/project/goodtimes_project
hg up

Updating Changes From Elsewhere

Say you made some changes on a work machine called w and then you came home to a machine called h that you wanted the changes to also be on. First you have to pull the work repository (or you could have pushed before you left).

hg pull ssh://xed@w/path/aproject

But that’s not all. Nothing will seem different. The h repository will now be aware that changes have been made, but to actually apply them to the local repository, you need to update that with:

hg up

Cloning And Updating A Repository To Another Location

Although these decentralized version control systems have a lot of flexibility, the normal thing is that you have a central storage location and several people work on the same code in several different locations. That is what was just previously described.

But what if you’re working on the code mostly in one location but want to use the source control to distribute it — and of course changes — to multiple hosts. For example, if I have a repo of my unix configuration files, I may want to push that out to various hosts when I’m setting them up. Since that will be constantly being improved in arbitrary places (any host) the system needs to be flexible. This can be done in the following way. Start by creating the remote copy of the repo like this.

cd ..  # From inside repo skelx
hg clone skelx ssh://testuser@192.168.1.250//home/testuser/X/skelx

It can then be updated from within the repo to the remote location like this.

hg push ssh://testuser@192.168.1.250//home/testuser/skelx

Obviously there are topology problems if I make different changes to this repo from two remote systems and never coordinate.

Once the remote repo is in action, you can merge any changes made there with a pull.

hg pull ssh://$HOST:$PORT//home/testuser/X/skelx

This prevents the need to log in to the secure central machine from a potentially insecure situation.

What Version Is This

You may want to know what version you’re up to.

hg log

You may want to even slip this into source code somehow. I’ve used this to generate a that information the way I wanted it.

hg log -l1 --template '{date|shortdate} - Version: {rev}'

More details about how you can format such things here.

What Files Changed

Sometimes you see evidence of changes when you pull in a repository that someone else (or you elsewhere) is working on.

adding file changes
added 2 changesets with 4 changes to 3 files
new changesets d8d47651e7a3:1d1218fa8546
...
3 files updated, 0 files merged, 0 files removed, 0 files unresolved

It is clear that some files changed, but which ones? To find out, use this command.

hg status --change .

Reorganizing Things

The worst thing about all of the version control systems is that they are philosophically self-defeating in the sense that if you knew the final organizational structure of all your project’s files, you probably would also know the final exact syntax of the entire project. But the whole point of a version control system is that it manages your lack of this foresight. However, renaming and moving files into completely new directory trees can be a pain.

Simple Renaming

For a simple mistaken filename, this works fine.

hg rename xt2png.c xy2png.c

Note that apparently on some platforms there can be issues with simply changing case. As described here you may need to do something like this.

hg rename xy2png.c tmp
hg rename tmp XY2png.c

This sequence of events should preserve history.

Changing Directory Layout

Let’s say I had a program called foo that was being tracked. It was a minor thing designed to be a proof of concept. Now we want to rewrite foo from scratch with all the right things done from the beginning. We don’t want to lose our earlier work, but we want to move existing foo work into a directory called foo-prototype to refer to it. Here is what can work.

mkdir foo-prototype
hg mv foo.c foo-prototype/foo.c
touch foo.c  # Starting the new one.
hg add foo.c

What Files Are Being Tracked

Sometimes I lose track of which files are being tracked. Check with:

hg manifest

Where Did This Stuff Come From?

Sometimes I have something on a fancy server somewhere and I pull it in and that’s all well and good. Mercurial makes pushes automatic so it’s easy to forget where that originally was being stored. If I need to check it out into a different place how can I figure out where it came from in a place I’m currently using it?

$ hg paths
default = ssh://xed@myisp.com/hg/mycoolproject

What Went Wrong?

Sometimes something is done, often to fix a bug, and you wind up making something else not work. You want to go back to the place you "fixed" and revisit that but you can’t remember where it was or what you did. First thing to do is to check the commit logs. I like to reverse them so the newest is last.

hg log | tac

This produces a lot of stuff, but if you can find the commit that was around the problem, look for it’s "changeset" id which should look something like: 87:c472394a44cc. The 87 is important here. To see the changes that were made on this commit:

hg diff -r 87

To see just what files were worked on use:

hg log --template '{files}\n' -r 87

Oops - Revert To Previous

The best summary I have seen is here.

Basically hg revert --all will undo whatever mess you’ve just made, but you can also use --rev to specify a particular revision number.

A good sequence of actions is something like this.

hg log somecode.py # Useful for seeing what's been going on with the file.
hg annotate somecode.py # Lists currently tracked code with rev # that caused it.
hg diff somecode.py # Show only additions/deletions between previous version and current.
hg revert --rev 2 somecode.py # Resets current code back to change set #2.
vi somecode.py # Try some better strategy with the file.
hg ci somecode.py # Check it back in heading in a new direction.

What if you just want to take a look at a previous version to get an idea what you might have messed up?

hg cat sourcecode.cxx -r 117 > sourcecode-117.cxx

Push Creates New Remote Head

Sometimes you do a hg push and you get this mysterious message telling you something is wrong.

abort: push creates new remote head c473eca09ada!
(merge or see 'hg help push' for details about pushing new heads)

This probably means that you and the remote team did some changes at the same time. The answer is to merge them.

hg pull
hg merge
hg commit -m "= merge"
hg push

And it should be fine. Obviously if your merge conflicts, that needs to be sorted out.

Magic Web Server

Despite the mania for Github, Mercurial is really easy to get on the web and it always has been. The good thing is that you do not need to entrust management of your code to a proprietary company. You can easily host your own repositories. To do so, just be in the tracked directory and do this.

hg serve

That’s it. Note that this automatically starts its own web server on the spot. It is not daemonized but a process that displays all connection attempts. It can also be put into a cgi-bin directory but that’s a different topic. Now point a web browser to your host like this.

http://host.xed.ch:8000/

And you can browse away at the change history and the tracked files. The default permissions are read only. To get the whole project, just do something like this from the command line of the receiving system.

hg clone http://host.xed.ch:8000 project

Using One SSH Hosting Account For Multiple Contributors

Let’s say you have a small group working on a project. They would all like a place which is on 24/7 which can be the main coordinating repository for the group. By renting a $5/month SSH host (or use AWS for free if you’re brave) you can give people personalized access to the repository using SSH keys. Basically the way it works is that all the participants generate an SSH key pair. The public keys are rounded up and put into the hosting service account’s ~/.ssh/authorized_keys file. Instead of giving everyone complete access you can limit the other participants to just being able to work with mercurial. You do this by using the command= directive on the SSH key. Here is an example from the hosting service account’s authorized_keys file.

hosting.service.com:/home/mainaccount/.ssh/authorized_keys
command="hg -R hg/ourproject serve --stdio",no-port-forwarding,\
no-X11-forwarding,no-agent-forwarding \
ssh-rsa AAAAB3NzaC...full/public/key...zYFiidCx amigo@amigoshost

This is all a one line entry, i.e. replace the \ and returns with nothing. Note that the main repo lives in ourproject in the directory ~/hg.

To use this key from a client somewhere, you’ll need the corresponding private key and the password to decrypt it. Then you must tell mercurial that you don’t want ordinary SSH, you want special key SSH. Like this.

hg clone --ssh "ssh -i /home/amigo/.ssh/ourproject_rsa" \
ssh://mainaccount@hosting.service.com/hg/ourproject

If there are problems check my SSH notes especially the part about "Specifying Commands".