Using Mercurial (hg)


This document is a slighly more reader-friendly version of the notes I took while learning to use Mercurial.  The purpose of this document is to provide a simple guide to the immediate use cases for hg.  The assumption is that you're using Linux, OS X, or similar.

toc

The .hgrc file

The .hgignore file

Your project repository
Working with Mercurial
Viewing the project history in your web browser, download via Mercurial's web server

Getting a Do-Over

Switching to an earlier working revision


The .hgrc file

Creating a .hgrc file in your home directory will allow for hg to source information from that file, thereby saving you the hassle of providing the info yourself when you run hg or having hg guess what the answers should be.  You can create the .hgrc file using vi, and populate it with something like:

[ui]
username = Charles Lockhart <lockhart@ifa.hawaii.edu>

This is taken from my .hgrc file.  Obviously your file should have your name and email address.


The .hgignore file

The .hgignore file goes in your project directory (right next to the .hg directory).  It's a listing of what file types shouldn't be included.  Here's the contents of the .hgignore file I made:

syntax: glob
*.class
*~
*.o

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

The .hgignore file gets added to the repository, so after you create the file, run hg add and hg commit.


Your Project Repository

Your project will have a repository for hg to track files.  There are two ways for this to get set up.

CVS Note: unlike CVS/SVN, hg doesn't have a primary repository.  Instead, it's distributed, and essentially you have the master copy.  Each project member also has their own master copy.  As changes are made, you push and pull changes to and from each other.  However, de facto there is typically one person in charge of "the release" for a project, and they will typically end up having the master copy.  Pushes and pulls are most likely going to go through that master copy.  So while the nature of the program is for everything to be flat, in actual usage it's more likely to be similar to CVS/SVN.

Creating a New Repository

The assumption is that you have a bunch of files and folders that contain code and are in a main folder for the project.  To convert the project folder to an hg repostiory.  Here's how:
  1. cd into the project directory.
  2. run the command hg init  This creates the .hg directory and the initial setup files used by hg.  At this point, there are no files added to the hg project. 
  3. run the command hg add  This adds all files that aren't currently in the hg project file list to it's file list.  Because we just created the list (via hg init), this adds everything.
  4. run the command hg commit  This commits all changes to the project.  With hg add we included the files, now we secure the file contents.
And that's it.  Repository created, files populated.

Getting an Existing Repository (Cloning a repository)

The assumption is that somewhere out there is an hg project that you want a copy of for your own development purposes.  In Mercurial this is called cloning, and we use the clone command.  Here will be shown two ways of doing this, one for cloning a local repository, the other for cloning a remote repository by riding over ssh.

Cloning a Local Repository

Cloning a local repository is simple.  Run the command:

    hg clone original_repository_directory_name new_repository_directory_name

As long as the original repository directory exists and has a workable .hg folder in it, a new clone of that original directory will be created that is up to date with the repository files.

NOTE: the files in the original repository directory may not be up to date with the actual repository files.  Every project directory is a working directory.

Cloning a Remote Repository

Coning a remote repository over ssh is also fairly simple.  Run the command:

    hg clone ssh://username@machinename/path/to/repository

Unless you have ssh keys set up, you will be prompted for username's password.  Enter the password, and if everything is set up correctly on the remote machine (path to hg is correct, path to repository is correct, etc.), you will end up with a clone of the repository on th e local machine.

It should be noted that the path to repository is relative to the users home directory.  So

    hg clone ssh://lockhart@stefan.ifa.hawaii.edu/src

will clone the src directory located in user lockhart's home directory.  To specify an absolute path, double slahes are needed, as in this example:

    hg clone ssh://lockhart@stefan.ifa.hawaii.edu//tmp/project

Errors usually come from one of two places, either the path is wrong, or for new installs, the hg binary isn't in the PATH.  

Here's an example for using hg & ssh to clone when the server has a different port setup:

hg -v clone ssh://sgir@stefan.ifa.hawaii.edu:2266/src/idl/noise/



Working With Mercurial

The essentials of any project are adding content, backing up that content, and merging in other people's content.  All of this is easily done in Mercurial.

Let's assume there are three users, Charles, Tony and Bob.   Tony creates the project and initializes it.  Charles and Bob use the clone command to get copies.  Now everyone starts out on an even playing field.  All three users keep the project directory in their ~/src directory, and the name is our_project.

The initial files in our_project are:  Makefile, test.c, test.h

Changing a file, simple

Tony makes a change to test.c.  He then commits that change by running:

    hg commit
NOTE: hg commit pops you into a vi session that makes you write in some change notes. If for some insane reason you don't know vi, use [hg commit -m "my message about the code here"], without the brackets on either side.
which commits all file modifications to the repository.  Now, at this point, Tony has made a change, but the other users, Charles and Bob, don't know that.  Charles can see what changes Tony has made by running:

  hg incoming

which will produce a list of changes (list entry items include the user who made the change, the date the change was made, and the summary (written by the editor at the time the change is committed)).  At this point they can decide to update their own repository.

To update a repository that has been cloned from someone elses, run the command:

    hg pull

Charles and Bob decide to update their repositories, so they run hg pull.  The file content in their reporitories is updated.  However, their working files are not!  The changes that Tony made are not shown in their actual files.

To update the content from the repository, assuming Charles and Bob haven't made and committed any changes themselves, they need to also run:

    hg update

This will re-synchronize their working directory with the repository.

Using hg pull / hg update, users can download and update their repositories quite easily.

What if users want to update someone else's repository?  What if Charles makes a change, and wants to update Tony's repository with the change?

Charles makes a change to test.h.  He then commits that change using hg commit.  He can view any changes that would be uploaded by running:

    hg outgoing

To upload these changes to the repository he originally cloned from, he runs:

    hg push

which updates the original repository, but not the working files held there.

Multiple users changing files, not as simple, but almost

Both Tony and Charles make and commit changes to their repositories.  Charles then goes and pulls Tony's version, and fins out that they both made changes.  To see what changes they both made that need to be merged, Charles can run:

    hg heads

which will print out all changes that are temporarily being held.  To go ahead and have Mercurial merge the files together automatically, Charles then runs:

    hg merge

Running hg merge gets Mercurial to combine the files into a single set having all changes.  However, the results of the merge are changes to the working files, not the files in the repository.  Charles must them commit those changes to have them be in the repository.  This allows Charles to review the results of the merge, and make changes if necessary.  Charles then runs hg commit and hg push to commit his changes and to upload them to Tony, who can then run hg update to update his working files.

Now, what happens if Charles makes some changes, commits them, then uploads them to Tony.  And then Tony commits a change he's made.  Simple, two branches have been created, which can be seen by running hg heads.  To unite the two branches, Tony can then run hg merge.

Managing merge conflicts

Mercurial's merge tool will at times have problems, and will return indicating conflicts that must then be fixed. The conflicts will be highlighted within the file by merge markers (<<<<<<< ======= >>>>>>>) to indicate the local code, the other code, and a seperator in between.

At that point the most straight forward thing is to go through and merge those conflicting sections by hand. Many guides refer to using a GUI based tool, we don't bother. We edit the file, selecting which section we want (all of the conflicting sections must be fixed), then save the file. We then run hg resove -m filename to mark the file as having been fixed up, then check in the changes.

Comparing working files with what's in the repository

The easiest way to see what's different between your working files and the repository is to run:

    hg diff

which prints out the accumulation of all changes.  Once changes are committed, using hg commit, hg diff will return nothing.

Now, hg diff simply indicates if you've made any changes since the last time you've run commit.  It does not show you if there have been changes that have pushed to your repository by someone else using hg push.  To check, run:

    hg log

which shows the change history of your repository.


Adding a new file, files, directory or directories

This is very simple.  If new files are added, or new directories are added, simply run:

    hg add

and

    hg commit

which creates entries in the repository for the files/directories, and then commits their contents.

Removing files or directories

This is also very simple.  To remove a file from the repository, run:

    hg rm path_to_file/filename

This removes the file from the repository AND deletes the file from the directory.  The file will no longer exist.  If the directory is then empty, the directory will be removed/deleted as well.

Copying files or directories

Again, simple.  To copy a file or directory in the repository, run:

    hg cp file_or_directory_name new_file_or_directory_name

Note, you must then run hg commit to have these new copies included in the repository.

Moving files or directories

Again, simple.  To move a file or directory in the repository, run:

    hg mv file_or_directory_name new_file_or_directory_name

Note, you must rhen run hg commit to have these changes included in the repository.  The hg mv command is essentially hg cp and hg rm.



Viewing the project history in your web browser

One neat little feature of Mercurial is that you can view the change log in a fairly user friendly way by using it's built in web server.  Run:

    hg serve

And Mercurial will start up a web server (default port is 8000, you can assign a port using the -p PORTNUM option).  Open up your browser, browse to http://localhost:8000 and you can see the projects history and track changes in a fairly nice way.

Mercurial can use this server to serve the project.  Here is an example of the command you could use to clone from the web server:

    hg clone http://localhost:8000/ myproject

This will download the project being served to the directory myproject.  To push changes back to the server you need to set up an SSL certificate.


Getting a Do-Over

You can use the

    hg rollback

command to undo the very last change you made to your local project.



Switching to an earlier version to work on something


To get a working set of files from a previous revision, you can run:

    hg update -C REVISION

For example, if you run
   
    hg log

and the output is something like:

changeset:   7:2497fceb1c9d
tag:         tip
parent:      10:526b195e810d
parent:      15:9f75160e0484
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Thu Apr 30 08:16:27 2009 -1000
summary:     merge

changeset:   6:d3d77c23b5ab
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:36:04 2009 -1000
summary:     Added done message

changeset:   5:1e148e4558c6
parent:      3:57b9cd5b4d13
parent:      4:d48bee4e80e4
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:29:56 2009 -1000
summary:     merged Charles and Tony's changes

changeset:   4:d48bee4e80e4
parent:      1:780ef1879d22
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:23:59 2009 -1000
summary:     changed authors

changeset:   3:57b9cd5b4d13
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:24:34 2009 -1000
summary:     changed hello there message

changeset:   2:3636dad0a768
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:21:08 2009 -1000
summary:     Added a struct to test.h

changeset:   1:780ef1879d22
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:06:29 2009 -1000
summary:     changed return value for main().

changeset:   0:83117260c893
user:        Charles Lockhart <lockhart@ifa.hawaii.edu>
date:        Wed Apr 29 14:05:36 2009 -1000
summary:     initial entry

If you want to go back to revision 5 (changeset:   5:1e148e4558c6), you would run:

    hg update -C 5

If you're working with a public server, where the short revision numbers don't match, you would run:

    hg update -C 1e148e4558c6