Bookmarks were, for me, the missing piece in Mercurial. Before bookmarks,there wasn’t an easy way of working on multiple changes simultaneously, something I do alot. Usually, I fix a bug or add a feature and put it up for
review. While the code is under review I start working on something
else. The existing options don’t nicely solve this problem:
Mercurial branches can’t be destroyed. Once you create a
branch, it’s stuck and global. I don’t want to clutter everyone with a bunch of branches.
I can’t easily clone another copy of the repository.
My emacs config is set to a specific directory. and we have environment variables that
make it a pain.
Mercurial Queues come the closest to solving the problem, but are
also a no go. Lets say, I
create a new patch in the queue, and then create a second one. Due to
how queues work, patch one is the parent of patch two. This is a
problem because it doesn’t play nicely with software like Review
Board.
There is also the shelf extension, but I believe you can only shelve one thing at a time. I don’t remember why else I didn’t use this, but I know I didn’t like it.
Mercurial bookmarks come to the rescue and let you create a bookmark for
each change. (Initially I couldn’t get bookmarks to work, so I hope I will save others from similar trouble. I
can’t remember what I was doing wrong but props to Augie
Fackler for showing me how to properly
use bookmarks.) So here’s a sample workflow.
Let’s say we have to fix a bug. We start by making a bookmark:
hg book bugX
Typing hg book will show us our current active bookmarks:
* bugX 0:2473879fcde9
Now, code, code, … code and commit:
hg commit -m "Bug fix #X"
We dump that change for review:
hg out -p > ~/bug_x.patch
While a coworker makes sure I didn’t break anything with a code review, I’d
like to get started on a different change. So let’s go back to the parent changeset, since we don’t want to build our changes on top of the fix, we create another bookmark.
hg up 0 # rev number is 0 just in this example
hg book cool_feature
And now hg book will show two bookmarks
* cool_feature 0:2473879fcde9
bugX 1:94b0bd2a03ae
More coding … and during the review my coworker found a flaw in my bug fix, I want to get that pushed out right away, so let’s checkpoint, update the bug fix and come back to this later.
hg commit -m "cool_feature checkpoint. Doesn't fully work"
hg up -C bugX
Now we are back at bugX, so I can do the necessary changes, commit that, and create a new patch. This time I need to specify which bookmark to use for creating the patch, since we have two.
hg up -C bugX
# Make our changes
hg commit -m "Updated bug fix #X"
hg out -r bugX -p > ~/bug_x.patch
Now we can switch back to our work on that cool new feature:
hg up -C cool_feature
If we look at the log, we can see the two heads we’ve created:
@ changeset: 3:750736b7f1a7
| tag: tip
| tag: bugX
| parent: 1:94b0bd2a03ae
| user: Matt Ronge <mronge@xxxxx.com>
| date: Wed Apr 01 22:13:10 2009 -0500
| summary: Cleaned up bug #X
|
| o changeset: 2:597b5f702706
| | tag: cool_feature
| | parent: 0:2473879fcde9
| | user: Matt Ronge <mronge@xxxxx.com>
| | date: Wed Apr 01 22:10:38 2009 -0500
| | summary: cool_feature checkpoint. Doesn't fully work
| |
o | changeset: 1:94b0bd2a03ae
|/ user: Matt Ronge <mronge@xxxxx.com>
| date: Wed Apr 01 20:06:45 2009 -0500
| summary: Bug fix #X
|
o changeset: 0:2473879fcde9
user: Matt Ronge <mronge@xxxxx.com>
date: Wed Apr 01 20:03:54 2009 -0500
summary: Commit 1
As you can see, bugX and the cool feature share the same parent, but are otherwise two independent branches of development. Let’s finish up work on the feature..and we’ve gotten permission to commit the bug and feature. So let;s merge them and push the result out.
hg commit -m "Finished feature"
hg merge bugX # merge bugX into the current line of development of cool_feature
hg commit -m "Merged"
hg push
And now we are done. That was a bit pedantic to go through the entire workflow, but my hope is that putting in each step will help others get acquainted with bookmarks. It’s also interesting to see the final graph:
@ changeset: 5:9bd7038876ba
|\ tag: cool_feature
| | tag: tip
| | tag: bugX
| | parent: 4:f80dcce7e0da
| | parent: 3:750736b7f1a7
| | user: Matt Ronge <mronge@xxxxx.com>
| | date: Wed Apr 01 22:24:09 2009 -0500
| | summary: Merged
| |
| o changeset: 4:f80dcce7e0da
| | parent: 2:597b5f702706
| | user: Matt Ronge <mronge@xxxxx.com>
| | date: Wed Apr 01 22:20:41 2009 -0500
| | summary: Finished feature
| |
o | changeset: 3:750736b7f1a7
| | parent: 1:94b0bd2a03ae
| | user: Matt Ronge <mronge@xxxxx.com>
| | date: Wed Apr 01 22:13:10 2009 -0500
| | summary: Cleaned up bug #X
| |
| o changeset: 2:597b5f702706
| | parent: 0:2473879fcde9
| | user: Matt Ronge <mronge@xxxxx.com>
| | date: Wed Apr 01 22:10:38 2009 -0500
| | summary: cool_feature checkpoint. Doesn't fully work
| |
o | changeset: 1:94b0bd2a03ae
|/ user: Matt Ronge <mronge@xxxxx.com>
| date: Wed Apr 01 20:06:45 2009 -0500
| summary: Bug fix #X
|
o changeset: 0:2473879fcde9
user: Matt Ronge <mronge@xxxxx.com>
date: Wed Apr 01 20:03:54 2009 -0500
summary: Commit 1