2010

2009

2007

2006

2005

A sample Mercurial bookmark workflow

Entry published apr 01 2009

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
← Previous: Common workflows in different DVCS's  //  Next: Use Mercurial with a Git Repository

comments

Steve Losh, 1 year, 5 months ago:

I never understood the benefit of bookmarks. Am I missing something? What I usually hear is “bookmarks are better than named branches because named branches can’t be destroyed.”

However, when you hg book -d a bookmark the bookmark is destroyed but all of the changesets are still in the repository! Sure, the list of bookmarks is a bit cleaner, but the changelog is still just as cluttered as before.

You can use hg strip to remove the changesets if you like (provided you haven’t merged/pushed) but that works exactly the same with named branches and bookmarks. If you strip all the changesets of a named branch out of a repo you won’t see the branch name in hg branches.

Now that named branches can be explicitly closed with hg commit --close-branch is there a reason to use bookmarks?

Matt Ronge, 1 year, 5 months ago:

Steve,

You’re right in that named branches are nearly the same as bookmarks, however even after you close a named branch, it is listed with hg branch with (closed) next to it. That gets really annoying really fast if you happen to create lots of short lived branches.

Brett, 1 year, 5 months ago:

Good writeup. I have never felt the need for bookmarks since I don’t have the absolute path issues you mention and can usually clone repositories left and right. Nevertheless I always enjoy reading about obscure features in case I ever have the need for them.

0000vk, 1 year, 4 months ago:

”’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.”’

that’s what guards are for. the opposite problem is worse. that is backporting changes to multiple branches is a bigger problem. queues deal with this better.

dsp, 1 year, 1 month ago:

Thank you for the tutorial. Nice that people help other people to understand bookmarks.