Python Rocks! and other rants
Weblog of Kent S Johnson

2009-03-25 08:20:14

Introduction to Mercurial

Mercurial is a popular open source, distributed revision-control system written in Python with a little C. Mercurial is fast, robust, friendly and easy to use. Many extensions add functionality. Mercurial itself is a command-line utility but add-ons supply GUIs and integration with other packages.

Installation

Binary packages are available for many operating systems.

On Windows, the TortoiseHg installer includes many useful GUI utilities and integrates with Windows Explorer.

I recommend disabling the TortoiseHg shell extensions (they seem a bit flaky) and installing hg view according to the instructions here.

Setup

In your home directory, create a file called Mercurial.ini. (Note: the docs refer to both Mercurial.ini and .hgrc. You can use either name.) Add your username to it with two lines similar to these:

[ui]
username = Kent Johnson <kjohnson@mydomain.com>

Configure a program for visual diffs

The default visual diff program is kdiff3. If you would rather use a different program, add two lines to your Mercurial.ini such as these (configuring Beyond Compare):

[extdiff]
cmd.vdiff = C:\Program Files\Beyond Compare 2\BC2.exe

Now the command hg vdiff will launch Beyond Compare.

Using gpyfm for merge

The default visual merge program is kdiff3. If you would rather use gpyfm (trust me, you would), add these three lines to your Mercurial.ini (assuming the TortoiseHg install):

[merge-tools]
gpyfm.checkconflicts = True
gpyfm.checkchanged = True

Now merging will launch gpyfm.

Learning Mercurial

Mercurial is extensively documented. Many useful resources are linked from the Mercurial home page. Some starting points:

Distributed revision control with Mercurial is a comprehensive on-line book.

You can also type hg help, hg help <command> or hg help <extension> to get help from the command line.

Distributed version control

If you have used a centralized version control system such as CVS, SVN or a commercial VCS, using a distributed VCS takes a bit of an adjustment in your thinking. The most important thing to understand is that you will always be working with a local repository. When you check in or check out a file, revert, diff, view history, etc., it all happens against the local repository.

For private projects, the local repository may be the only one for the project; with Mercurial, it is very easy to put a local directory under version control.

If you are also working with a central repository, your local repository is a full peer of the central repository. In Mercurial, a "master" repository is just a working designation, not a technical one. When working with an upstream repository, you will generally be working with three copies of the source code - the upstream repository, your local repository, and your working copy. To get a change from your working copy to the upstream repository requires two steps. First, check in to the local repository; second, push the local repository to the upstream repository.

Creating a repository

You can create a local repository from scratch, or as a clone of an existing repository.

Cloning an existing repository

To clone an existing repository, use

hg clone <repository_url>``

This creates a subdirectory of the current directory containing both a clone of the upstream repository and a working copy. For example, I created a repository at bitbucket.org containing sample programs I have written for python-tutor. To clone this repository, use the command

hg clone https://kent37@bitbucket.org/kent37/python-tutor-samples/

This creates a directory named python-tutor-samples containing a Mercurial repository (in the .hg directory) and a working copy of the files from the repository. It also configures the default repository for hg pull and hg push commands (see below).

You can clone a local repository, making a new local copy. This is a common way to branch a repository. Files in the clone working copy symlink to the upstream repository so the clone is fast and cheap.

Create a repository from an existing directory

hg init

creates a new, empty repository in the current directory. No files have been committed yet.

hg status

shows the files in the working copy, with a ? indicating that they are not in the repository.

You probably have files in the working copy that you don't want to include the repository. Typically these are build products such as object files and executables. Create a file named .hgignore at the top level of the working copy (not in the .hg directory) and edit it to exclude these files. Lines in .hgignore are either glob patterns or regular expression patterns matching files to exclude. For example:

syntax: glob
*.orig
*.rej
*~
*.o
tests/*.err

syntax: regexp
.*\#.*\#$

When hg status shows just the files that you want in the repository, type

hg add

to add them to the repository and

hg commit -m "Commit message"

to actually commit the files.

Basic commands

The commands you will use the most are

  • hg status - show the status of the working copy (hg st)
  • hg add - add new files to the local repository
  • hg commit - commit changes to the local repository (hg ci)

If you have configured vdiff, qct and hgk, these commands open useful GUI views:

  • hg vdiff - visual diff of uncommitted changes
  • hg qct - opens the qct commit tool
  • hg view - opens the hgk repository viewer

Commands can be abbreviated to the shortest unique name, such as hg st. Many commands have aliases, such as hg ci. hg help <command> lists the aliases for a command.

Working with an upstream repository

Often you will be working with an upstream repository, either to collaborate with others or to publish your repository.

Syncing with an upstream repository

These examples assume you have a default path set for the upstream repository (default parameter in the [paths] section of Mercurial.ini). If you created your local repository by cloning the upstream repository, the default path is set automatically. Otherwise, supply the repository url with each command.

hg incoming shows changesets in the upstream repository that are not in your local repository.

hg pull -u pulls changes from the upstream repository to your local repository and updates your working copy. You probably want your changes to be checked in locally before you do this.

hg outgoing shows changesets in your local repository that are not in the upstream repository. Note this shows committed changesets, not working copy changes.

hg push updates the upstream repository with local changesets. Your local repository must be up-to-date with the remote for this to work.

Merging

If you make changes in a local repository, and other changes are pushed to an upstream repository, when you pull from the upstream your local repository will branch. Resolving the branch requires two steps. First, hg merge to merge the two heads in your working copy. Verify that the merge is correct, then check in the merge changeset. See this explanation complete with pictures.

More settings

Mercurial may be customized with settings in Mercurial.ini or .hgrc. Settings may apply to a single repository, a single user or all users on a system, depending on where the settings file is located. For complete details see the .hgrc docs.

Default arguments

You can define default arguments for any Mercurial command by adding a [defaults] section to Mercurial.ini. For example, to make the hg log command default to showing only five entries, add this section:

[defaults]
log -l 5

Path aliases

You can create aliases for paths to other repositories. In particular you can define a default repository for pull and push. Note that if you created a repository by cloning another, the defaults are already set up for you. The default configuration looks like this:

[paths]
default = <repository url>

Extensions

As initially installed, Mercurial exposes a no-frills set of basic commands. Many included extensions are disabled by default, and many add-on extensions are available. When you feel comfortable with basic Mercurial operations, or miss some feature, it is worth taking some time to explore the available extensions.

Extensions are enabled by adding an entry to the [extensions] section of Mercurial.ini. Many extensions also have configuration sections of their own.

Aliases

You can create aliases for Mercurial commands that you use frequently. First, enable aliases by adding the line

hgext.alias =

in the [extensions] section of your Mercurial.ini. Then define your aliases in an [alias] section. For example, to make hg qon be an alias for hg qapplied, add these lines to Mercurial.ini:

[alias]
qon = qapplied

The Record extension

The record extension provides the hg record command. This command lets you choose which parts of the changes in a working directory you'd like to commit, at the granularity of patch hunks. It also adds a hg qrecord command if you are using Mercurial queues.

Mercurial queues

The Mercurial queues extension gives you more control over your local changesets by allowing you to push and pop changesets in your local repository. Mq adds complexity and power to your repository. For example, you can avoid merge changesets before a push by keeping all your changes in a queue. Before a pull, hg qpop all your changesets. This restores your local repository to the state at the last pull. Now hg pull -u to sync with the upstream repository, then hg qpush to restore your changes. You have applied the upstream revisions ''before'' your changes, so no merge is needed before a push. (Of course if there are conflicts you still have to merge the individual files.)

For more information see the Mq tutorial and documentation.

I find the below sequence of commands gives a useful summary of the current state of a repository. It shows incoming and outgoing changesets, the state of the patch queue, and local uncommitted changes. Put it in a batch file; I call it fullstat:

echo off
echo Incoming:
hg in
echo
echo Outgoing:
hg out
echo
echo Queue on:
hg qapplied
echo
echo Queue off:
hg qunapplied
echo
echo Status:
hg status

Another useful command converts all qpushed patches into permanent changesets. The command is

hg qdelete -r qbase:qtip

I create a Mercurial alias hg qdall for this command by adding these lines to Mercurial.ini:

[extensions]
alias =

[alias]
qdall = qdelete -r qbase:qtip

(Mercurial 1.1 includes a qfinish command which is similar.)

 
© Kent S Johnson Creative Commons License

Comments about life, the universe and Python, from the imagination of Kent S Johnson.

kentsjohnson.com

Weblog home

All By Date

All By Category

Essays

XML-Image

BlogRoll