We use Mercurial at work. It’s nothing particularly new to me; I’ve been working with it ever since SDL, a major open-source graphics library that I’ve been using and contributing to for a long time now, changed over to a HG repository several years ago. You’d think that I’d be used to its quirks and glitches, all the little things it manages to somehow not get right, by now. But it keeps finding new and innovative ways to suck.
Yesterday at work, I was halfway done with an issue when a very minor but very urgent issue came up: I had checked in something that was breaking the build. No big deal, right? Just fix it, check in your changes, and get back to work. Well, yeah, that’s how it would go if you were using a sane version control system like SVN. But we use Mercurial at work.
The “just fix it” part is the same, but then I went to check in my changes. In SVN, this is incredibly simple: select the changes to check in, write a commit message, click OK, and you’re good. But Mercurial is a DVCS. It was designed for much larger, more complicated projects than most people will ever work on, stuff like operating systems, that are so huge that distributed repositories are a necessity. (I heard it said once that if every developer on the Windows team were to only ever break the build once per year, they would literally never have a working build if everyone checked into the same repo.) But we don’t have a team anywhere near that huge. And yet, we use Mercurial at work, and it likes to pretend we need all the stuff that enormous teams need. So checking in code is a two-step process as a best-case scenario. First you commit it to your local repository, then you push your local repository changes to the central repo.
This turned out not to be the best of times, because someone had checked in something since the last time I pulled changes. In a sane VCS like SVN, this doesn’t matter all that much; you just grab the update, SVN integrates everything seamlessly unless there’s a merge conflict–which is rare in a well-managed project regardless of which VCS you’re using–and then you commit. For a couple years now, TortoiseSVN has even been able to automate that workflow for you. But we use Mercurial at work. So you pull the changes to your local repo, and then you have to “update” to that revision, even though that really should be a no-brainer. Except that I have a commit in my local repo, and even though they’re on the same branch, Mercurial freaks out and turns it into a new branch of the branch I’m on.
That’s not a problem, right? Just merge the commits. Except Mercurial doesn’t know of any such thing as “merge commits.” No, it knows how to “merge with local”, meaning it pulls the new commits on the new branch into your local working copy and then commits the changes from your local working copy into the local repository. This sounds like a pointless semantic difference, and there really shouldn’t be any difference at all, except… I was in the middle of working on another issue, so I have uncommitted changes. For some moronic reason, if I try to merge, HG will commit those to my local repo too, and then you’re stuck in a huge painful mess of trying to back your changes out, because HG doesn’t know how to merge two commits and only merge that. And yet we use it at work.
No big deal, though. Any experienced DVCS victim can tell you how to take care of that scenario: just shelve your uncommitted changes before you merge. Nothing I haven’t done dozens of times before. So I dutifully stick my uncommitted changes on a shelf named “Mercurial is stupid”, which is the one I use for performing this stupid operation that would not be necessary if DVCS wasn’t fundamentally broken. (Yes, DVCS in general. Git is even worse about these things!) I merge the commits, push my changes, and everything is peachy… right up until I go to unshelve my code and get back to work.
You see, one of the changes I had made involved renaming one of the units in the project. Since Delphi requires the unit name declared at the top of the unit to match the filename, this means both changing the code and renaming a file. I had dutifully renamed the file using the special Mercurial operation for it in the context menu, so that it can track the changes correctly. But when I go to unshelve, the operation fails. It says “NewName.pas” cannot be found, and therefore it can’t apply the changes to the unit name declaration at the top of the file. (But it very helpfully restored the deletion of the old file, which meant I had to go back and revert the change and do it all over again!) So much for being able to track renames properly! What’s the point of even having an official Mercurial file rename command in the context menu if it doesn’t actually know that the file has been renamed? Even my manager, who actually likes Mercurial, had wasn’t sure if there was any easy solution. He had never seen this before and he was all confused when I showed this to him, because he didn’t know it can screw up in this particular way.
Every time I think I’ve seen it all, DVCSs keep finding new and innovative ways to suck. The entire DVCS concept is essentially a fractal of bad design, and the deeper into it you go, the more problems you find.
And yet, we use Mercurial at work. I guess I’m stuck with it. But please, if you happen to be a manager setting up a new project… just use SVN and save everyone a ton of headaches!