aragost Trifork: Mercurial Kick Start Exercises


Basic Mercurial

We will begin with the basics of Mercurial. We believe the best way to learn is to do things yourself, so we encourage you to follow along and execute the commands you see “Alice” and “Bob” execute. You can tell who is executing the command by the color of their terminal: Alice has a pale peach colored terminal, Bob has a pale turquoise color, and Carla (introduced later) has a lavender color.

There are also some exercises later that cover the same things that Alice and Bob did.

Contents

Installation & Configuration

Mercurial is available for most platforms, including Windows, Mac OS X, and GNU/Linux:

Windows:

Please only install TortoiseHg, which gives you a complete installation with Mercurial and a set of graphical tools for examining and manipulating the repository.

After installation, you will have a right-click menu in Windows Explorer that gives you access to the graphical tools. After logging out and in again, you will also have a hg and a thg program available in a Command Prompt. You can use the thg program to start the graphical TortoiseHg tools.

Tip

While the examples will use a few Unix commands, you can easily translate them back to the equivalent commands for the Command Prompt:

ls          -> dir
cat         -> type
echo "abc"  -> echo abc
Linux:

Please install Mercurial using your package manager. If you cannot find it there, then you can install from source: grab the latest stable release, unpack it somewhere and run make local inside. Now symlink the hg script to a directory in your PATH and you are done.

To install TortoiseHg, you need the PyQt bindings. They are available in most distributions. Then follow these instructions. That gives you the thg program.

Mac OS X:
We recommend installing recommend MacHg to get a good, fast, and native client for Mercurial. MacHg comes bundled with its own Mercurial so you won’t have to worry about that. If you already use TortoiseHg on Windows, then you’ll be happy to know that you can now install it on Mac OS X too.

We will be using the command line in our examples but will sometime show the repository state in the TortoiseHg Workbench.

After installing Mercurial, try running hg version:

alice$ hg version
Mercurial Distributed SCM (version 2.2)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2012 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

It is of course fine if you see a version number greater than 2.2. Mercurial 1.4 is also okay for our purposes, but we always recommend upgrading to the newest version available for your platform. You can verify your installation with:

alice$ hg debuginstall 
Checking encoding (UTF-8)...
Checking installed modules (/usr/share/pyshared/mercurial)...
Checking templates (/usr/lib/python2.7/dist-packages/mercurial)...
Checking commit editor...
Checking username...
 no username supplied (see "hg help config")
 (specify a username in your configuration file)
1 problems detected, please check your install!

The username is normally missing immediately after installation. You set it by putting the name in a configuration file. On Unix-like systems this is $HOME/.hgrc and on Windows the file is called %HOME%\Mercurial.ini (see hg help config for all locations or use thg userconfig if you prefer). Create the file with this content:

[ui]
username = Firstname Lastname <example@example.net>

but with your own name. We will use Alice <alice@example.net> first and her terminals look like the two you saw above. Running hg debuginstall should no longer report any problems.

Basic Commands

We will now make a swift tour through the basic commands. These are all similar to older systems such as Subversion or even CVS. If you are using TortoiseHg under Windows, then you might want to also refer to their quick start guide.

Creating a Repository

Create a repository with the hg init command:

alice$ ls
alice$ hg init example
alice$ ls
example

The hg init command created a new directory which is the new repository. We could also have made the example directory ourselves and issued a blank hg init inside that directory to turn it into a repository.

Entering example we can look around:

alice$ cd example
alice$ ls -a
.
..
.hg

As you can see, the new repository is empty except for a .hg directory in the repository root. Mercurial stores the history and some other administrative files inside this directory.

Creating a Changeset

Let us create a file and ask Mercurial about the status of the repository:

alice$ echo "Hello World" > hello.txt
alice$ hg status
? hello.txt

The reply tells us that hello.txt is unknown to Mercurial, i.e., it is not yet tracked. We can add the file:

alice$ hg add hello.txt
alice$ hg status
A hello.txt

which changes the status to “A” for “added”. The file is not yet stored in the repository history — we’ll do this by making a commit:

alice$ hg commit -m "First version of hello"

No output indicates success — Mercurial won’t interrupt you unless necessary.

Inspecting History

You can see the newly created changeset with hg log:

alice$ hg log
changeset:   0:d312da7770f4
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     First version of hello

Success, you have made a commit with Mercurial! Let us modify the file and ask Mercurial to show us how the files in working copy differ from the last revision:

alice$ echo "Goodbye!" > hello.txt
alice$ hg diff
diff -r d312da7770f4 hello.txt
--- a/hello.txt Wed Mar 10 20:10:05 2010 +0000
+++ b/hello.txt Mon Mar 10 20:10:10 2010 +0000
@@ -1,1 +1,1 @@
-Hello World
+Goodbye!

We can change our mind and revert the file back to how it looked before:

alice$ hg revert hello.txt
alice$ cat hello.txt
Hello World

Let us make another change to our file:

alice$ echo >> hello.txt
alice$ echo "by Alice." >> hello.txt
alice$ hg commit -m "Added author." 

We can now ask Mercurial to annotate the file, that is, to show when each line was last modified:

alice$ hg annotate hello.txt
0: Hello World
1:
1: by Alice.

Quite right, Mercurial tells us that the first line is from revision 0, and that the two final lines are from revision 1. TortoiseHg offers a very nice interactive annotate tool, see thg annotate.

Annotating files is an invaluable help when fixing bugs: after finding the bug, you can use hg annotate to find out when the offending line was introduced in your file. Then use hg log to retrieve the associated commit message, which can hopefully explain what the committer was trying to do when he changed the line:

alice$ hg log -r 1
changeset:   1:2e982bdc137f
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:15:00 2010 +0000
summary:     Added author.

The two changesets in our repository are in sequence. This is best illustrated with the help of the standard graphlog extension. To keep the core of Mercurial slim, a lot of functionality is delegated to extensions. Many of the best extensions are shipped with Mercurial and they are easy to enable. To enable graphlog, Alice simply adds:

[extensions]
graphlog =

to her configuration file. She can then run hg help graphlog to learn more about the extension. This extension is particularly simple, it just gives Alice a new command:

alice$ hg glog
@  changeset:   1:2e982bdc137f
|  tag:         tip
|  user:        Alice <alice@example.net>
|  date:        Wed Mar 10 20:15:00 2010 +0000
|  summary:     Added author.
|
o  changeset:   0:d312da7770f4
   user:        Alice <alice@example.net>
   date:        Wed Mar 10 20:10:05 2010 +0000
   summary:     First version of hello

This shows how the changeset 2e982bdc137f is a child of d312da7770f4. The same can be seen in thg log:

alice-log.png

The files in the working copy are always synchronized to a particular changeset. In thg log, this is shown with a circled bullet in the changeset graph. In the output of hg glog, the working directory parent revision is highlighted with a @-sign.

Time Travel

You can change the working copy parent revision with the hg update command. This is useful to go back and examine old versions of your code, for instance if you need to test an old version of your software with a new operating system. Here Alice will just go back to revision 0 to look at her old file. She first uses the hg parents command to ascertain where she is in the graph:

alice$ hg parents
changeset:   1:2e982bdc137f
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:15:00 2010 +0000
summary:     Added author.

and she then makes the jump:

alice$ hg update 0
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ hg parents
changeset:   0:d312da7770f4
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     First version of hello

Her file is now back at the old version:

alice$ cat hello.txt
Hello World

but the newer version is not forgotten. Mercurial can easily retrieve it for her:

alice$ hg cat -r 1 hello.txt
Hello World

by Alice.

The hg cat command is useful to quickly see old versions of files instead of using hg update to change the entire working directory to an old version. Going back to the tip revision is easy, just do hg update without an argument:

alice$ hg update
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ cat hello.txt
Hello World

by Alice.

Exercises

  1. Use hg init to create a repository called kick-start.

  2. Enter kick-start and create a couple of files. Schedule them for addition with hg add. You can use hg revert to cancel a pending addition if you change your mind.

  3. Use hg commit to commit the files. An editor will be started if you do not specify a commit message on the command line.

  4. Make a couple of more commits.

  5. Use hg update to update the working directory to an older revision. Notice how the output of hg parents changes to match and compare this with hg tip, which stays fixed then you use hg update.

  6. Try making a commit when the working parent is an old revision. You will be told that you have created a new “head” — a head is a changeset without any children. Use hg heads to see the repository heads and look at the graph in thg log.

    Notice how the newly created changeset has the changeset listed previously in hg parents as its parent changeset. This is an invariant in Mercurial: the working copy parent revision becomes the parent revision of the next changeset.

  7. Multiple heads represent multiple divergent lines of development. You will normally merge these lines using hg merge. If your changes do not overlap, then the merge will be easy. Otherwise you will have to resolve the conflicts — more about this later.

    Notice how hg parents print two changesets after you issued hg merge. Again, this means that the next commit will have two parents. You can see the parents in hg glog or thg log after you commit.

Working with Others

Right now Alice’s repository is in her home directory. Her co-worker, Bob, also needs to work on the project. We will show his commands with a light blue background color.

Making a Clone

In these examples, we will let Alice and Bob share a local filesystem. This could very well be a company-wide network filesystem. Changes can also be shared over SSH and HTTP connections — people even use emails or USB sticks to move around changesets. They then typically use a compressed binary format produced by the hg bundle command. This is not necessary here, instead Bob uses hg clone with a relative path to Alice’s repository:

bob$ hg clone ../alice/example
destination directory: example
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Bob now has his own fully independent copy of Alice’s repository. He can make such a clone as long as he has read access to Alice’s files. Bob can make his own changesets in his clone, without affecting Alice. He makes a couple of changes:

bob$ cd example
bob$ echo "Goodbye!" > goodbye.txt
bob$ echo >> goodbye.txt
bob$ echo "by Bob." >> goodbye.txt
bob$ hg add
adding goodbye.txt
bob$ hg commit -m "My goodbye file." 
bob$ echo >> hello.txt
bob$ echo "not by Bob." >> hello.txt
bob$ hg commit -m "Not my file." 

The repository now has four changesets:

bob-log.png

Comparing Clones

The clone knows where it originates from and Bob can ask if there are any changesets in Alice’s clone that he does not already have:

bob$ hg incoming
comparing with /home/alice/example
searching for changes
no changes found

He can also ask Mercurial what changeset he has that Alice lacks:

bob$ hg outgoing
comparing with /home/alice/example
searching for changes
changeset:   2:659410363fe0
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:00:00 2010 +0000
summary:     My goodbye file.

changeset:   3:51b2976b01e8
tag:         tip
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:03:00 2010 +0000
summary:     Not my file.

This hopefully makes sense: Bob created two new changesets in his own clone, so Alice does not have them yet.

Moving Changesets

Bob does not have write access to the files in Alice’s home directory, so he cannot write his changesets to her clone. But he lets her know that he has made a clone and asks her to retrieve the changesets. She does this with the hg pull command. Before using the command, she enters the path to Bob’s clone in the .hg/hgrc file. This a repository-local configuration file. She enters:

[paths]
default = /home/bob/example

to make Bob’s clone the default target when doing hg pull and other commands that involve a remote repository. She can see what will be pulled with hg incoming:

alice$ hg incoming
comparing with /home/bob/example
searching for changes
changeset:   2:659410363fe0
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:00:00 2010 +0000
summary:     My goodbye file.

changeset:   3:51b2976b01e8
tag:         tip
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:03:00 2010 +0000
summary:     Not my file.

Notice how this is symmetric to when Bob executed hg outgoing in his clone. She decides to go ahead and pull Bob’s changes:

alice$ hg pull
pulling from /home/bob/example
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
(run 'hg update' to get a working copy)

The final message is a hint to Alice to her know that her working copy parent revision has not changed:

alice-pull.png

The parent revision is unchanged since Alice may have been working on a change of her own, and so she might not be interested in updating right away. In this case she does update:

alice$ hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

You will almost always want to run hg update after hg pull and for this reason, there is a shortcut: hg pull -u will automatically update after pulling in new changes.

Divergent Lines of Development

Alice reworks the hello.txt file a bit:

alice$ hg diff
diff -r 51b2976b01e8 hello.txt
--- a/hello.txt Thu Mar 11 10:03:00 2010 +0000
+++ b/hello.txt Thu Mar 11 11:00:00 2010 +0000
@@ -1,4 +1,4 @@
-Hello World
+Hello, Wonderful World!

 by Alice.
alice$ hg commit -m "Happy, happy!" 

At the same time, Bob is working. He creates a new file:

bob$ echo "Welcome!" > welcome.txt
bob$ hg add
adding welcome.txt
bob$ hg commit -m "Welcome file." 

Both Alice and Bob have now made a changeset that is a child of 51b2976b01e8, but neither of them knows about this since they have not yet pulled or pushed the changes anywhere. Alice decides to pull from Bob:

alice$ hg pull
pulling from /home/bob/example
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

What happened? As mentioned above, a “head” changeset is a changeset without any children. Since Alice and Bob have made different changes based on 51b2976b01e8, there are now two changesets in the repository without any children. These heads are divergent lines of development, as can be seen in thg log:

alice-heads.png

The two lines of development should be reconciled, and the hg merge command is the way to do that:

alice$ hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

There were no conflicts since Alice and Bob edited different files. After hg merge, the working copy has two parents — this is how you can tell that a merge is in progress if you have left your computer and forgotten about it. The merge is not yet committed since you might need to edit the files to make your code compile. Just because there were no overlapping edits (no conflicts), there might still be some semantic conflicts which Mercurial cannot detect. It is therefore a good idea to run the test suite after a merge. In our case, Alice goes ahead with the commit:

alice$ hg commit -m "Merge with Bob." 

The two heads are now bound together in the changeset graph:

alice-merged.png

The merge changeset contains changes from both of its parent revisions. You should think of a merge changeset as your way of saying to the world: this is how you should combine changeset X with changeset Y. When others pull the merge changeset from your repository, it will go into the right place in their changeset graph. They will therefore not have to do the same merge. We can see this if we let Bob pull from Alice:

bob$ hg pull
pulling from /home/alice/example
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
bob-pull.png

Notice how the changesets appear in a different order in Bob’s repository compared to Alice’s repository — the “Happy, happy!” changeset has switched order with the “Welcome file.” changeset. Changesets always retain their ID, the longer hexadecimal number, but the revision number can change. The revision number simply depends on the order in which changesets was added to a repository, and when two people pull from each other, they will most likely end up with different revision numbers for the same changesets. You should therefore always refer to a changeset by its ID when talking to others. But you can still use the revision numbers with commands like hg update that work inside your local repository.

Exercises

  1. Create a repository and make a couple of commits inside it.

  2. Make two clones of your repository. Notice how the .hg/hgrc file records the location of the original repository. The hg paths command will show you the paths defined.

  3. Add another path to .hg/hgrc, such as:

    bob = /home/bob/test
    

    You will now be able to execute hg pull bob, hg outgoing bob, etc. It is a good idea to add shortcuts for people you collaborate with often.

  4. Experiment with pulling and pushing changes. Note how the working directory is not changed even though history is added to the repository. The working copy parent revision is only changed with hg update.

Mercurial supports disconnected collaboration. This works by exchanging changesets in what is called bundles. A bundle is a compressed, binary representation of a number of changesets.

  1. Create a new changeset in one of your repositories. Use hg bundle --base X out.bundle to create a bundle in out.bundle that contains all changesets following X. The idea is that you record somewhere the last known shared changeset between you and the target, and use that as the base.

  2. Go to the target repository and use hg unbundle to import the changesets from the bundle. In this example you will most likely have both repositories on your own machine, but in reality the two repositories could be on machines that are never online at the same time. In that case, bundles offer a way for you to move changesets over, say, email.

You can also publish your repository via HTTP:

  1. Execute hg serve in a repository. You can now access the repository in a browser with http://localhost:8000. Try to start a server for both Alice’s and Bob’s repository.

  2. Try to pull from a repository published with hg serve.

Note hg serve is intended for ad-hoc publishing. For permanent publishing you can for example use Apache with the hgweb.cgi script supplied with Mercurial.

Summary

You have seen most of the basic commands in Mercurial and you have seen how the history graph works. The important commands are:

And very importantly: