Wander Lairson Costa

Mercurial for git lovers

• VCS

So far I have been a heavy git user, but since I joined Mozilla I have given Mercurial a try. I must say that migrating from Subversion to git was far less painful than from git to Mercurial. I think there are a few reasons for that:

Most of the day to day commands in Mercurial are quite similar to git, like clone and commit. Some others have very different names, like git revert, which in Mercurial is called backout. hg revert is used to discard non-committed changes in your repository.

I prepared a list of some Mercurial extensions that will make your life easier when coming from git. These extensions will make you feel more comfortable while moving from git to Mercurial.


git feature Mercurial extension
color Color
git rebase Rebase
Interactive rebase Histedit
git stash Shelve
git clean Purge
git add -i Record
gitk Hgk
__git_ps1 Prompt
git cherry-pick Transplant
git send-email PatchBomb
git am Mbox
Topic branches Bookmarks
Paged log Pager


This link has a lot more information about differences between git and Mercurial, including Mercurial counterparts for several git actions.

Mercurial comes with an important feature that there is no equivalent in git at all, called phases. I mention it here because you may have problems with it if you (like me) do a lot of rebase and history editing. Every commit you push or pull to/from a remote repository is considered public, which makes it immutable. This happens all the time to me because I often push commits to Mozilla Try and reviewboard. Thus, I generally face errors like this:

    hg rebase -d bookmark0
    abort: can't rebase immutable changeset 35c25b97fca2
    (see hg help phases for details)

That’s because I have pushed this commit and Mercurial made it public (aka immutable). The solution for this is make it a draft commit again:

    hg phase --draft --force bookmark3

I find this feature very annoying and I think it would be better implemented as an extension. It sounds Java telling me what I can and can’t do. You can disable making commits public after a push by adding these entries in your hgrc file:

    [phases]
    publish = False

Special note on Bookmarks

Since Mercurial version 1.8, Bookmarks are now part of Mercurial core. They are often advertised as git branches on Mercurial. They are not! The most difficult part for me was to understand that Mercurial has no branch support like git. Period. Bookmarks are just a hack that tries to mimic topic branches, but, technically, a bookmark is not a branch, it is more like a tag. I am not going to explain how Bookmarks work, you can learn more on it here.

The most irritating thing regarding Bookmarks is that changing the history in one bookmark can affect other bookmarks as well. In some cases, when you create multiple heads descending from a bookmark, you cannot edit its history at all. Let’s try an example. Imagine you fix a bug and submit it for review. While you wait for feedback, you create more bookmarks to work on other product features. You eventually end up with a tree like this:

    @ bookmark 3
    |
    | o bookmark 2
    |/
    |
    | o bookmark 1
    |/
    |
    o bookmark 0 (bug 137463)

The @ indicates the commit representing the current directory.

After a while you receive feedback for bug 137463 and you are requested some changes. You then move back to bookmark 0:

    o bookmark 3
    |
    | o bookmark 2
    |/
    |
    | o bookmark 1
    |/
    |
    @ bookmark 0 (bug 137463)

And start to apply the requested changes, making a new commit:

    o bookmark 3
    |
    | o bookmark 2
    |/
    |
    | o bookmark 1
    |/
    |
    | @ bookmark 0 (bug 137463)
    |/
    |
    o commit 1 (original bug 137453 commit)

bookmark 0 moved to the new commit. What you want now is to squash commit 1 and bookmark 0, but you can’t, because you will mess up bookmarks 1-3. One solution would be to rebase bookmarks 1-3 on top of bookmark 0:

    o bookmark 3
    |
    | o bookmark 2
    |/
    |
    | o bookmark 1
    |/
    |
    @ bookmark 0 (bug 137463)
    |
    o commit 1

You can now histedit from commit 1 and squash it with bookmark 0, right? Wrong! This happens because the structure you see is just an illusion from branching point of view. In git, when you modify a branch, it doesn’t affect descendant branches. In Mercurial this is not true simply because all the commits are in the same branch.

Conclusion

If your git workflow relies heavily on git branching capabilities and history editing, you will have some trouble to adapt yourself to Mercurial (like me).

If I could request just one feature to Mercurial developers, that would be lightweight branches like git. That would give Mercurial a big boost.

There are other problems I had with bookmarks regarding remote repositories, but I will save that for a future post.

comments powered by Disqus