Mercurial doesn’t get a lot of love nowadays, but as I’ve been using it pretty much every day for about eight or nine years, I thought it might be time to remind The Internet that it still, very much is, a thing.
In this post, I’m using a throwaway, demo project and I’d advise anyone reading to do the same, until you’re comfortable with Mercurial concepts.
hg help
The most important command.
Mercurial Distributed SCM
list of commands:
Repository creation:
clone make a copy of an existing repository
init create a new repository in the given directory
qclone clone main and patch repository at same time
Remote repository management:
incoming show new changesets found in source
outgoing show changesets not found in the destination
paths show aliases for remote repositories
pull pull changes from the specified source
push push changes to the specified destination
serve start stand-alone webserver
Change creation:
absorb incorporate corrections into the stack of draft changesets
commit commit the specified files or all outstanding changes
:
hg status
I’ve already added a few files for the purposes of this demo. We’ll run hg st to see what needs to be done.
You can run hg st to see the status of your reposistory
[simon@computer 09:45:37] ~/tmp/hg-demo
$ hg st
abort: no repository found in '/home/simon/tmp/hg-demo' (.hg not found)
In this case, Mercurial helpfully tells us that we don’t have a repository in this directory (yet). Let’s create one
hg init
[simon@computer 10:00:23] ~/tmp/hg-demo
$ hg init
If we forget that we’ve created a repo and try to create one again, Mercurial won’t allow that
[simon@computer 10:01:10] default ~/tmp/hg-demo
$ hg init
abort: repository . already exists
We’ll run hg st again to see where we are
[simon@computer 10:01:08] default ~/tmp/hg-demo
$ hg st
? bottle-sqlite-master/.gitignore
? bottle-sqlite-master/.travis.yml
? bottle-sqlite-master/LICENSE
? bottle-sqlite-master/README.rst
? bottle-sqlite-master/bottle_sqlite.py
? bottle-sqlite-master/setup.py
? bottle-sqlite-master/test.py
? bottle-sqlite-master/tox.ini
The ? simply means that the listed files / directories are currently unknown to Mercurial. We need to add them first
hg add
We can either add them individually, or add them all as a batch. I’m lazy so I’ll let Mercurial figure it out
$ hg add
adding bottle-sqlite-master/.gitignore
adding bottle-sqlite-master/.travis.yml
adding bottle-sqlite-master/LICENSE
adding bottle-sqlite-master/README.rst
adding bottle-sqlite-master/bottle_sqlite.py
adding bottle-sqlite-master/setup.py
adding bottle-sqlite-master/test.py
adding bottle-sqlite-master/tox.ini
Running hg st again lets us know that the files have been added, but not committed (or checked in, as I prefer) to our repo
[simon@computer 10:04:53] default ~/tmp/hg-demo
$ hg st
A bottle-sqlite-master/.gitignore
A bottle-sqlite-master/.travis.yml
A bottle-sqlite-master/LICENSE
A bottle-sqlite-master/README.rst
A bottle-sqlite-master/bottle_sqlite.py
A bottle-sqlite-master/setup.py
A bottle-sqlite-master/test.py
A bottle-sqlite-master/tox.ini
hg ci
Running hg ci will open up our default editor and let us write a commit message
GNU nano 9.0 /tmp/hg-editor-rw5uklht.commit.hg.txt * I
Add bottle-sqlite files
HG: Enter commit message. Lines beginning with 'HG:' are removed.
HG: Leave message empty to abort commit.
HG: --
HG: user: Simon Harrison <simon@simonh.uk>
HG: branch 'default'
HG: added bottle-sqlite-master/.gitignore
HG: added bottle-sqlite-master/.travis.yml
HG: added bottle-sqlite-master/LICENSE
HG: added bottle-sqlite-master/README.rst
HG: added bottle-sqlite-master/bottle_sqlite.py
HG: added bottle-sqlite-master/setup.py
HG: added bottle-sqlite-master/test.py
HG: added bottle-sqlite-master/tox.ini
If we run hg ci again when nothing has changed we get
[simon@computer 10:19:55] default ~/tmp/hg-demo
$ hg ci
nothing changed
We can avoid the editor by passing -m to Mercurial
[simon@computer 10:19:55] default ~/tmp/hg-demo
$ hg ci -m "I changed something, somewhere"
hg log
At any time, we can see what chages we’ve committed to our repo
[simon@computer 10:07:45] default ~/tmp/hg-demo
$ hg log
changeset: 0:3f082894d145
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:07:32 2026 +0100
summary: Add bottle-sqlite files
As you can see, I’ve got one changeset. They say it’s always good to have a README, so let’s add one of them
[simon@computer 10:22:46] default ~/tmp/hg-demo
$ hg st
? README
We’ll add our new file specifically this time
[simon@computer 10:26:03] default ~/tmp/hg-demo
$ hg add README
[simon@computer 10:26:51] default ~/tmp/hg-demo
$ hg st
A README
And check it in with a short log message
[simon@computer 10:28:07] default ~/tmp/hg-demo
$ hg ci -m "Add README"
hg log -p
If we want to know what the actual changes were in a commit, we can pass the -p for patch
[simon@computer 10:28:31] default ~/tmp/hg-demo
$ hg log -p -r tip
changeset: 1:f45ecf76aaf0
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:28:17 2026 +0100
summary: Add README
diff -r 3f082894d145 -r f45ecf76aaf0 README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README Mon May 25 10:28:17 2026 +0100
@@ -0,0 +1,1 @@
+This is just a test repo for a blog post.
hg clean
What if you’ve added a file (or files) to your working directory, but realise you don’t want them in there, actually? Look at this. A random Perl script is in our project!
[simon@computer 10:43:24] default ~/tmp/hg-demo
$ hg st
? first.pl
How did that get in here? Of course, we could just delete using the shell or a file manager, but Mercurial has a useful command clean to get rid of such things
[simon@computer 10:43:31] default ~/tmp/hg-demo
$ hg clean
permanently delete 1 unknown files? (yN) y
[simon@computer 10:43:53] default ~/tmp/hg-demo
Let’s check it really has gone
$ ll
total 20K
drwxrwxr-x 4 simon simon 4.0K 2026-05-25 10:43 .
drwxrwxrwx 8 simon simon 4.0K 2026-05-25 09:58 ..
drwxrwxr-x 2 simon simon 4.0K 2020-10-03 16:27 bottle-sqlite-master
drwxrwxr-x 5 simon simon 4.0K 2026-05-25 10:56 .hg
-rw-rw-r-- 1 simon simon 42 2026-05-25 10:21 README
This can come in very useful. I quite often pip install to a folder in my working directory. Except this time, I forget to install to lib/pip
[simon@computer 11:04:00] default ~/tmp/hg-demo
$ pip3 install -t . openpyxl
Collecting openpyxl
Using cached openpyxl-3.1.5-py2.py3-none-any.whl.metadata (2.5 kB)
Collecting et-xmlfile (from openpyxl)
Using cached et_xmlfile-2.0.0-py3-none-any.whl.metadata (2.7 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached et_xmlfile-2.0.0-py3-none-any.whl (18 kB)
Installing collected packages: et-xmlfile, openpyxl
Successfully installed et-xmlfile-2.0.0 openpyxl-3.1.5
Running hg st shows that I made a boo boo.
? et_xmlfile-2.0.0.dist-info/AUTHORS.txt
? et_xmlfile-2.0.0.dist-info/INSTALLER
? et_xmlfile-2.0.0.dist-info/LICENCE.python
? et_xmlfile-2.0.0.dist-info/LICENCE.rst
? et_xmlfile-2.0.0.dist-info/METADATA
? et_xmlfile-2.0.0.dist-info/RECORD
? et_xmlfile-2.0.0.dist-info/WHEEL
? et_xmlfile-2.0.0.dist-info/top_level.txt
? et_xmlfile/__init__.py
? et_xmlfile/__pycache__/__init__.cpython-312.pyc
? et_xmlfile/__pycache__/incremental_tree.cpython-312.pyc
? et_xmlfile/__pycache__/xmlfile.cpython-312.pyc
? et_xmlfile/incremental_tree.py
? et_xmlfile/xmlfile.py
? openpyxl-3.1.5.dist-info/INSTALLER
? openpyxl-3.1.5.dist-info/LICENCE.rst
? openpyxl-3.1.5.dist-info/METADATA
? openpyxl-3.1.5.dist-info/RECORD
? openpyxl-3.1.5.dist-info/REQUESTED
? openpyxl-3.1.5.dist-info/WHEEL
? openpyxl-3.1.5.dist-info/top_level.txt
? openpyxl/__init__.py
? openpyxl/__pycache__/__init__.cpython-312.pyc
:
But no matter, now we know about hg clean
[simon@computer 11:07:19] default ~/tmp/hg-demo
$ hg clean
permanently delete 401 unknown files? (yN) y
[simon@computer 11:07:28] default ~/tmp/hg-demo
And everything is back as it should be
$ ll
total 20K
drwxrwxr-x 4 simon simon 4.0K 2026-05-25 11:07 .
drwxrwxrwx 8 simon simon 4.0K 2026-05-25 09:58 ..
drwxrwxr-x 2 simon simon 4.0K 2020-10-03 16:27 bottle-sqlite-master
drwxrwxr-x 5 simon simon 4.0K 2026-05-25 11:07 .hg
-rw-rw-r-- 1 simon simon 42 2026-05-25 10:21 README
Running hg log again shows that our repo has not been effected in any way
[simon@computer 11:07:31] default ~/tmp/hg-demo
$ hg log
changeset: 1:f45ecf76aaf0
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:28:17 2026 +0100
summary: Add README
changeset: 0:3f082894d145
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:07:32 2026 +0100
summary: Add bottle-sqlite files
hg absorb
At this point, I’d like to introduce you to a Mercurial extension called “absorb”. I started using it only a few months ago, but let me tell you – it’s amazing!1
Very often, particularly when working on a new project, you’ll be making lots of changes to the same files, trying to get them working correctly. And therefore, you’ll be making lots of commits which just really add noise to your log. You might have:
- add section to README
- add another section to README
- add yet another section to README
- fix typo in README
- etc.
We’ve already got at least four commits that aren’t very informative. Absorb will insert new commits into old commits thereby giving you a nice clean log, with only one commit. In the example below, I’ve made a note to myself to mention the absorb extension. I then instruct Mercurial to commit into the exising log entry
[simon@computer 11:17:02] default ~/tmp/hg-demo
$ hg st
M README
[simon@computer 11:17:04] default ~/tmp/hg-demo
$ hg absorb
showing changes for README
@@ -1,0 +1,3 @@
f45ecf7 +
f45ecf7 +Note: remember to write about hg absorb
f45ecf7 +
1 changesets affected
f45ecf7 Add README
apply changes (y/N)? y
saved backup bundle to /home/simon/tmp/hg-demo/.hg/strip-backup/f45ecf76aaf0-07614989-absorb.hg
1 of 1 chunk(s) applied
Now I don’t normally use the word “neat”, but I’ve got to say, that’s neat.
hg export
The “export” command will show the patch information for a changeset. Not specifying one will show the latest changeset called tip
[simon@computer 11:22:09] default ~/tmp/hg-demo
$ hg export
# HG changeset patch
# User Simon Harrison <simon@simonh.uk>
# Date 1779701297 -3600
# Mon May 25 10:28:17 2026 +0100
# Node ID 9ac81759eac4cb041cb910172a50754d47a539d9
# Parent 3f082894d145de9bea6d87745be40831683d659d
Add README
diff -r 3f082894d145 -r 9ac81759eac4 README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README Mon May 25 10:28:17 2026 +0100
@@ -0,0 +1,4 @@
+This is just a test repo for a blog post.
+
+Note: remember to write about hg absorb
+
~/.hgrc
Mercurial will check to see if you have a .hgrc file in your home directory. Mine is below. The only thing you need to know about right now is the [extensions] section with:
absorb =
Which will enable the absorb extension
[simon@computer 11:22:14] default ~/tmp/hg-demo
$ cat ~/.hgrc
# $Id: .hgrc,v 1.2 2024/09/30 14:59:53 simon Exp simon $
# example user config (see 'hg help config' for more info)
[ui]
# name and email, e.g.
# username = Jane Doe <jdoe@example.com>
username = Simon Harrison <web@simonh.uk>
editor = nano -/
# We recommend enabling tweakdefaults to get slight improvements to
# the UI over time. Make sure to set HGPLAIN in the environment when
# writing scripts!
tweakdefaults = True
merge=internal:merge
# uncomment to disable command output pagination
# (see 'hg help pager' for details)
# paginate = never
[extensions]
convert =
patchbomb =
absorb =
mq =
[alias]
slog = log --stat -r
br = branch
bs = co stable
bd = co default
md = merge default
ms = merge stable
glog = log -G
ds = diff -r stable:default
dd = diff -r default:stable
[diff]
nobinary = True
git = False
Let’s add openpyxl to our project (and in the right place)
[simon@computer 12:28:15] default ~/tmp/hg-demo
$ pip3 install -t lib/pip openpyxl
Collecting openpyxl
Using cached openpyxl-3.1.5-py2.py3-none-any.whl.metadata (2.5 kB)
Collecting et-xmlfile (from openpyxl)
Using cached et_xmlfile-2.0.0-py3-none-any.whl.metadata (2.7 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached et_xmlfile-2.0.0-py3-none-any.whl (18 kB)
Installing collected packages: et-xmlfile, openpyxl
Successfully installed et-xmlfile-2.0.0 openpyxl-3.1.5
hg update
At any time, we can jump around our changesets or revisions. Let’s go back to the very beginning of our repo
[simon@computer 12:32:34] default ~/tmp/hg-demo
$ hg up -r0
0 files updated, 0 files merged, 402 files removed, 0 files unresolved
Mercurial has thrown away everything our working directory that didn’t exist at revision 0.
Sometimes we want to give a name to a commit, mainly so we can refer to it easily and / or checkout that revision. We do that with tags.
hg tag
[simon@computer 12:40:47] default ~/tmp/hg-demo
$ hg tag -r0 'zero'
This has two effects. One, it creates the named tag we specifed, and two, it creates a commit
[simon@computer 12:43:19] default ~/tmp/hg-demo
$ hg log
changeset: 3:612f6977a3b8
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 12:41:55 2026 +0100
summary: Added tag zero for changeset 3f082894d145
changeset: 2:c74897799d7f
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 12:30:41 2026 +0100
summary: Add openpyxl to lib/pip
changeset: 1:9ac81759eac4
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:28:17 2026 +0100
summary: Add README
changeset: 0:3f082894d145
tag: zero
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:07:32 2026 +0100
summary: Add bottle-sqlite files
Let’s have a look at the commit
[simon@computer 12:45:09] default ~/tmp/hg-demo
$ hg exp
# HG changeset patch
# User Simon Harrison <simon@simonh.uk>
# Date 1779709315 -3600
# Mon May 25 12:41:55 2026 +0100
# Node ID 612f6977a3b82405ec0c78c4e0d7087340061450
# Parent c74897799d7f5b33da8d3d65f9f1a665edfbdbee
Added tag zero for changeset 3f082894d145
diff -r c74897799d7f -r 612f6977a3b8 .hgtags
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgtags Mon May 25 12:41:55 2026 +0100
@@ -0,0 +1,1 @@
+3f082894d145de9bea6d87745be40831683d659d zero
hg identify
If we insist on bouncing around our changesets, it would be useful to quickly find out where we are. “Identify” or “id” is what we’ll need to run
[simon@computer 12:52:55] default ~/tmp/hg-demo
$ hg id
612f6977a3b8 tip
[simon@computer 12:52:58] default ~/tmp/hg-demo
$ hg up zero
0 files updated, 0 files merged, 403 files removed, 0 files unresolved
[simon@computer 12:53:13] default ~/tmp/hg-demo
$ hg id
3f082894d145 zero
hg log -G
We can also use hg log -G to see where we are. The @ is where we currenly are
[simon@computer 13:11:55] default ~/tmp/hg-demo
$ hg log -G
o changeset: 3:612f6977a3b8
| tag: tip
| user: Simon Harrison <simon@simonh.uk>
| date: Mon May 25 12:41:55 2026 +0100
| summary: Added tag zero for changeset 3f082894d145
|
o changeset: 2:c74897799d7f
| user: Simon Harrison <simon@simonh.uk>
| date: Mon May 25 12:30:41 2026 +0100
| summary: Add openpyxl to lib/pip
|
o changeset: 1:9ac81759eac4
| user: Simon Harrison <simon@simonh.uk>
| date: Mon May 25 10:28:17 2026 +0100
| summary: Add README
|
@ changeset: 0:3f082894d145
tag: zero
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:07:32 2026 +0100
summary: Add bottle-sqlite files
That’s pretty helpful. Let’s update to our latest change
[simon@computer 13:11:57] default ~/tmp/hg-demo
$ hg up
403 files updated, 0 files merged, 0 files removed, 0 files unresolved
[simon@computer 13:13:51] default ~/tmp/hg-demo
$ hg log -G
@ changeset: 3:612f6977a3b8
| tag: tip
| user: Simon Harrison <simon@simonh.uk>
| date: Mon May 25 12:41:55 2026 +0100
| summary: Added tag zero for changeset 3f082894d145
|
o changeset: 2:c74897799d7f
| user: Simon Harrison <simon@simonh.uk>
| date: Mon May 25 12:30:41 2026 +0100
| summary: Add openpyxl to lib/pip
|
o changeset: 1:9ac81759eac4
| user: Simon Harrison <simon@simonh.uk>
| date: Mon May 25 10:28:17 2026 +0100
| summary: Add README
|
o changeset: 0:3f082894d145
tag: zero
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:07:32 2026 +0100
summary: Add bottle-sqlite files
hg id should agree with hg log -G
[simon@computer 13:14:26] default ~/tmp/hg-demo
$ hg id
612f6977a3b8 tip
hg diff
When we make changes to files already tracked by Mercurial, we can easily see what’s changed by running hg diff
[simon@computer 13:20:20] default ~/tmp/hg-demo
$ hg di
diff -r 612f6977a3b8 README
--- a/README Mon May 25 12:41:55 2026 +0100
+++ b/README Mon May 25 13:20:22 2026 +0100
@@ -1,4 +1,3 @@
This is just a test repo for a blog post.
-Note: remember to write about hg absorb
-
+Remember to mention about hg diff
As you can see I’ve replaced a line in README. Let’s absorb it.
[simon@computer 13:22:09] default ~/tmp/hg-demo
$ hg absorb
showing changes for README
@@ -2,2 +2,1 @@
9ac8175 -Note: remember to write about hg absorb
9ac8175 -
9ac8175 +Remember to mention about hg diff
1 changesets affected
9ac8175 Add README
apply changes (y/N)? y
saved backup bundle to /home/simon/tmp/hg-demo/.hg/strip-backup/9ac81759eac4-5291bbfc-absorb.hg
1 of 1 chunk(s) applied
Can you see why I like absorb so much now???
hg backout
Sometimes, you made a commit and you want to “undo” it. You wish you hadn’t made the changes at all. hg backout is like ctrl-z for your repo. Maybe not, as you can backout any commits throughout the history of your repo.
I’ve made a small change to the README and I’ll check then commit it
[simon@computer 13:49:19] default ~/tmp/hg-demo
$ hg di
diff -r 5245de0c1b13 README
--- a/README Mon May 25 12:41:55 2026 +0100
+++ b/README Mon May 25 13:49:21 2026 +0100
@@ -1,3 +1,5 @@
This is just a test repo for a blog post.
Remember to mention about hg diff
+
+Today is very hot. Very hot indeed.
[simon@computer 13:52:59] default ~/tmp/hg-demo
$ hg ci -m "Small change to README"
But then, I realise that people probably don’t care that it’s hot today. What was I thinking?!
[simon@computer 13:53:46] default ~/tmp/hg-demo
$ hg backout -r4
reverting README
changeset 5:8da10bffb88e backs out changeset 4:fb966da0722c
And of course, running hg log records what we did
changeset: 5:8da10bffb88e
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 13:55:16 2026 +0100
summary: Backed out changeset fb966da0722c
changeset: 4:fb966da0722c
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 13:52:53 2026 +0100
summary: Small change to the README
changeset: 3:5245de0c1b13
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 12:41:55 2026 +0100
summary: Added tag zero for changeset 3f082894d145
changeset: 2:80ba0b02fc09
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 12:30:41 2026 +0100
summary: Add openpyxl to lib/pip
changeset: 1:59aa4d653c3f
user: Simon Harrison <simon@simonh.uk>
:
hg strip
USE WITH CAUTION.
Perhaps this would be better named as “obliterate” as that’s what it does. Strip will remove everything after a particular commit. Let’s say you have ten commits. if you run hg strip -r5 then everything from r5 onwards is deleted! In the example below, I’ve chosen to strip the latest commit and as you see, -r4 is no more.
[simon@computer 13:41:38] default ~/tmp/hg-demo
$ hg strip -r4
saved backup bundle to /home/simon/tmp/hg-demo/.hg/strip-backup/2804913c9e0d-63a65735-backup.hg
[simon@computer 13:41:49] default ~/tmp/hg-demo
$ hg log
changeset: 3:5245de0c1b13
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 12:41:55 2026 +0100
summary: Added tag zero for changeset 3f082894d145
changeset: 2:80ba0b02fc09
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 12:30:41 2026 +0100
summary: Add openpyxl to lib/pip
changeset: 1:59aa4d653c3f
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:28:17 2026 +0100
summary: Add README
changeset: 0:3f082894d145
tag: zero
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 10:07:32 2026 +0100
summary: Add bottle-sqlite files
hg branch
Branching let’s you veer off from the default branch for various reasons. One might be to create a stable branch, which you merge bug fixes from your main / default branch. Another might be to work on some experimental feature (which may need to be scrapped). Either way, a branch separates your code into silo’s which are effectively independent until merged back together.
In Mercurial, you create a new branch like so
[simon@computer 13:29:15] default ~/tmp/hg-demo
$ hg br "test-branch"
marked working directory as branch test-branch
(branches are permanent and global, did you want a bookmark?)
Once you’ve done that, you’ll need to commit for the branch to actually be created. I have created and committed the test-branch and added a HELLO_WORLD file
changeset: 8:148dfc191c11
branch: test-branch
tag: tip
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:23:57 2026 +0100
summary: Add HELLO_WORLD to the test-branch
changeset: 7:e0d495cff8b9
branch: test-branch
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:19:01 2026 +0100
summary: Create test-branch
changeset: 6:a54d2f2f5c03
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:09:46 2026 +0100
summary: Added the INSTALL file
changeset: 5:8da10bffb88e
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 13:55:16 2026 +0100
summary: Backed out changeset fb966da0722c
:
Mercurial reports what branch we’re on (if we are not on the default one). Now we can switch between branches using update
[simon@computer 14:30:42] test-branch ~/tmp/hg-demo
$ hg up default
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
Running ll will show that the HELLO_WORLD file is invisible to the default branch as we created it in the test-branch
[simon@computer 14:30:47] default ~/tmp/hg-demo
$ ll
total 32K
drwxrwxr-x 5 simon simon 4.0K 2026-05-25 14:30 .
drwxrwxrwx 8 simon simon 4.0K 2026-05-25 14:11 ..
drwxrwxr-x 2 simon simon 4.0K 2020-10-03 16:27 bottle-sqlite-master
drwxrwxr-x 6 simon simon 4.0K 2026-05-25 14:30 .hg
-rw-rw-r-- 1 simon simon 46 2026-05-25 13:13 .hgtags
-rw-rw-r-- 1 simon simon 46 2026-05-25 14:09 INSTALL
drwxrwxr-x 3 simon simon 4.0K 2026-05-25 13:13 lib
-rw-rw-r-- 1 simon simon 77 2026-05-25 13:53 README
Updating to the test-branch brings HELLO_WORLD back
[simon@computer 14:32:10] default ~/tmp/hg-demo
$ hg up test-branch
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[simon@computer 14:32:34] test-branch ~/tmp/hg-demo
$ ll
total 36K
drwxrwxr-x 5 simon simon 4.0K 2026-05-25 14:32 .
drwxrwxrwx 8 simon simon 4.0K 2026-05-25 14:11 ..
drwxrwxr-x 2 simon simon 4.0K 2020-10-03 16:27 bottle-sqlite-master
-rw-rw-r-- 1 simon simon 14 2026-05-25 14:32 HELLO_wORLD
drwxrwxr-x 6 simon simon 4.0K 2026-05-25 14:32 .hg
-rw-rw-r-- 1 simon simon 46 2026-05-25 13:13 .hgtags
-rw-rw-r-- 1 simon simon 46 2026-05-25 14:09 INSTALL
drwxrwxr-x 3 simon simon 4.0K 2026-05-25 13:13 lib
-rw-rw-r-- 1 simon simon 77 2026-05-25 13:53 README
So if at this point, we’re happy with our changes on test-branch, we can merge those changes back into the default branch. We’ll switch to our default branch first, then do the merge
[simon@computer 14:35:24] test-branch ~/tmp/hg-demo
$ hg up default
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
[simon@computer 14:35:30] default ~/tmp/hg-demo
$ hg merge test-branch
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
Mercurial helpfully reminds us that we need to commit. Running hg st gives us the following slightly frightening message
[simon@computer 14:35:37] default ~/tmp/hg-demo
$ hg st
M HELLO_wORLD
# The repository is in an unfinished *merge* state.
# To continue: hg commit
# To abort: hg merge --abort
But it also tells us what we need to do. We’ll commit our merge using the usual hg ci and check the log
changeset: 9:252812a28287
tag: tip
parent: 6:a54d2f2f5c03
parent: 8:148dfc191c11
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:38:42 2026 +0100
summary: Merge test-branch
changeset: 8:148dfc191c11
branch: test-branch
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:23:57 2026 +0100
summary: Add HELLO_WORLD to the test-branch
changeset: 7:e0d495cff8b9
branch: test-branch
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:19:01 2026 +0100
summary: Create test-branch
changeset: 6:a54d2f2f5c03
user: Simon Harrison <simon@simonh.uk>
date: Mon May 25 14:09:46 2026 +0100
:
hg shelve
Sometimes, you’ll be working on something and then all of a sudden, you need to be some place else in your repository. Perhaps to fix a bug, investigate something etc. hg shelve lets you put away all your uncommitted changes in a safe place, and then bring them back later.
[simon@jupiter 15:14:08] default ~/tmp/hg-demo
$ hg di
diff -r 252812a28287 HELLO_wORLD
--- a/HELLO_wORLD Mon May 25 14:38:42 2026 +0100
+++ b/HELLO_wORLD Mon May 25 15:14:10 2026 +0100
@@ -1,2 +1,3 @@
Hello world!
-
+I've added this line
+and this one
diff -r 252812a28287 README
--- a/README Mon May 25 14:38:42 2026 +0100
+++ b/README Mon May 25 15:14:10 2026 +0100
@@ -1,3 +1,5 @@
This is just a test repo for a blog post.
Remember to mention about hg diff
+
+Howdly doodly
I want to put those very important (unfinished) changes away as I’m not ready to commit them yet. I mean, I could commit them, but the work is not finished. Let’s shelve them instead
[simon@jupiter 15:18:07] default ~/tmp/hg-demo
$ hg shelve
shelved as default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
[simon@jupiter 15:18:09] default ~/tmp/hg-demo
$ hg st
[simon@jupiter 15:18:12] default ~/tmp/hg-demo
$ hg shelve -l
default (8s ago) changes to: Merge test-branch
We can see what we’ve shelved easily
[simon@jupiter 15:20:29] default ~/tmp/hg-demo
$ hg shelve -l
default (2m ago) changes to: Merge test-branch
And when we’re ready, we can get those changes back in our working directory
[simon@jupiter 15:20:32] default ~/tmp/hg-demo
$ hg unshelve
unshelving change 'default'
[simon@jupiter 15:21:12] default ~/tmp/hg-demo
$ hg st
M HELLO_wORLD
M README
To be Continued…
