Mercurial is still not ready for prime-time

Although Python has selected Mercurial as the distributed source control system of record, Mercurial still lacks many of the basic features needed to equip novice or amateur users with a usable system. I'm reporting my experience here to capture the issues I encountered.

A few months ago, I started working on Python issue 1578269. I have a symlink implementation for windows in I suggested porting that implementation into core Python so that os.symlink would work in Windows as well as Unix-based OSs.

As I started developing the patch, I soon found that a Subversion checkout and periodic patch generation was inadequate for maintaining a record and undo capability. I needed the ability to tentatively commit to an implementation, try a potential new approach, and possibly roll-back or commit the new change. This capability is difficult to accomplish with Subversion (without commit access).

The suggested remedy was to use Mercurial. So I cloned the Python Mercurial repository and applied my sequence of patches to the working copy and committed each to my local repo. I found I was then able to easily perform the same operations with rollback, commit, etc, that I was used to with a subversion repository, without impacting other projects.

The first problem came when I tried to apply the patches. It turns out that while Subversion supports native line ending translation, Mercurial does not (or didn't at the time). The prescribed solution was to just use Unix LF endings, so I had to convert my patches before applying them.

The real problems came months later, however, when I wanted to merge changes from the parent repository. I had tried in the past performing a proper merge, but I found that technique clunky. In retrospect, perhaps that would have been the correct action.

In any case, I also found there's a rebase operation. This was reported to do exactly what I wanted: update my local changes to appear as if they were against the latest changes in the parent. I found that the rebase option isn't enabled by default. I had to edit my mercurial.ini file (in the user's home directory, WTF) to enable the rebase capability. After adding rebase, I pulled the latest revisions from the parent repo and rebased the repo.

As expected, there were conflicts... except that when the conflicts were opened in TortoiseMerge, no red lines were displayed. In other words, it appeared as if Hg or Tortoise had successfully reconciled all changes. I was shown the same conflict resolution window many times, but I dismissed it each time, as it had no conflicting changes.

Perhaps I should have been taking a different action, because I found that the rebased code had destroyed my revision history. Many of the revisions I had made to one file had been lost. Indeed, TortoiseHg now showed some "revisions" that had the proper comment, but apparently no change.

Furthermore, the rebased repo appeared now to have reverted some changes that were made in the parent repo. I never felt like I had been given adequate ability to review these potentially disasterous changes, but they were already written. There was no going back.

Fortunately, I still had the majority of the changes documented through the bug tracker. In particular, patch 6 was made using Mercurial. So I did some searching to find out how to apply a patch to an Hg repository. What I found was an article describing the ten steps to apply a patch.

Unfortunately, applying this patch to the repo, even with it stripped to the revision from which it was made, failed to apply with lots of "HUNK #n FAILED at nnn" messages.

To reiterate, using Hg, I was unable to apply a patch created by Hg to a repo against which that patch was created. I tried analyzing the problem, and the patch appears fine. The line numbers appear to reference the proper lines and the reference content appears to match.

I suspect the problem again is due to platform linebreaks. I've confirmed that both the source and the patch use LF linebreaks. I'm able to apply the rejected patches to the source manually if I pass the --binary option. i.e.

patch --binary -i

Although the resulting output appears to have the native line endings (CR/LF), so I have to manually correct that.

In any case, there are several shortcomings of Hg/TortoiseHg that prevent it from being a user-friendly DSCM:

  • Robust line-ending support (should behave reasonably across platforms)

  • Merge support (TortoiseHg should have merge support; command-line should not require a 10-step process)

  • Rebase support (Rebase should be robust, should warn the user if it's about to destroy revision history, and should more clearly communicate conflicts).

Given that Mercurial was selected for its Windows platform support, I cringe to think what my experience might have been in Git.
Written on September 11, 2009