This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.


Mercurial: Why So Unhelpful?


I've been using Mercurial for a little over a year now, but I'm still kind of a newbie because: <ol> I don't use it every day I use it only for private projects, so there aren't many merge issues </ol> For development, I have two repositories: One for the web site content itself and another for the <a href="{root}software/webcore/">earthli WebCore</a>, the backend for the web site. For each of these projects, I have the following repositories: <ul> Local repository Server repository (development) Server repository (production) </ul> I usually make changes to the local version, then upload to the "dev" repsitory and test them on the server before pushing them to production. Sometimes, though, I have to make a quick fix on the production server itself when a user is blocked by a bug. Most of these fixes are one-liners in a single file and I try to push them back to "dev" immediately. Since I work alone, I'm not so accustomed to downloading from the server repository before working locally again. Couple all of this with the fact that I do actual web site development on quite sporadically and you have a recipe for files left lying around in various states: <ul> Uncommitted in either "prod" or "dev" Committed in "prod" but not pushed to "dev" Committed in "dev" but not pushed to "prod" (actually, that's ok) </ul> Uncommitted or unpushed files from the local repository are absolutely OK, though I still sometimes forget that I need to push to the server in addition to committing in order to actually see my changes. In fact, a full check-in looks like this: <shell> ...test local local:$ hg commit local:$ hg push local:$ ssh earthli earthli:# cd earthli/dev earthli:# hg update ...test dev earthli:# hg push earthli:# cd earthli/prod earthli:# hg update ...test prod </shell> This is actually all well and good and works just great as long as I <i>always</i> work in this order: Local => dev => prod. It's when I make one of the aforementioned changes directly to "prod" that I end up having to merge changes. Mercurial was totally designed to handle merging. I'm also quite familiar with the concept. But, with my local development on OS X and the servers running Debian, there is a paucity of visual support for merging. After years of administering OS X and Debian boxes, I'm also quite comfortable with the command line and am at least an advanced newbie at <c>vi</c>. Those are all not problems. What I don't like to do is resolve merge conflicts on the command line or using a text editor and hand-editing conflict markers. Fortunately, that happens exceedingly rarely, as I haven't managed to make conflicting changes in different repositories too often. But I chose both my server and development environments and will have to live with command-line support only for now. I just wish that command-line support was a bit more helpful. Here's a recent example to illustrate my frustration. I was trying to push changes from "dev" to "prod" and was told that the push would create multiple heads at the target. I totally grokked that: It meant that there were changes in "prod" that were not in "dev", so pushing from "dev" to "prod" would necessarily make another line of changes that would have to be merged in "prod" in order to have a single "tip". That wouldn't have been the end of the world, but I appreciated Mercurial informing me of the situation as well as offering that using the <c>-f</c> flag would allow me to force the push, if I so desired. I did not, however, so desire, because I <i>never</i> want to merge in prod. I always want to make all my mistakes in "dev", get everything working as expected and <i>then</i> push to "prod". The only commands I want to use from "prod" are: <ol> <c>hg update</c> to copy the latest changes from the repository to the local hard disk (from which the web server reads) <c>hg commit</c> to commit the rare emergency bug-fix to the "prod" repository. </ol> Ok, so instead I'll <c>hg pull</c> from the "prod" repository to the "dev" one, making two heads there. <shell> earthli:# hg pull ../prod pulling from ../prod searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) earthli:# </shell> So far, so good. That's exactly what I expected to happen. Let's check the heads, as Mercurial has so nicely suggested: <shell> earthli:# hg heads changeset: 16:9197ac29588a tag: tip parent: 14:cad4ff88973d user: date: Sun Jan 24 20:18:54 2010 +0100 summary: Removed a bunch of pages from the search site changeset: 15:f7c0e21d2e18 user: date: Fri Apr 02 18:05:06 2010 +0200 summary: - Fixed crash on dieting form earthli:# </shell> Again, those are two most recent changes I made in each branch. Mercurial also helpfully suggested I use <c>hg merge</c> to merge the changes. Let's try that. <shell> earthli:# hg merge abort: outstanding uncommitted changes earthli:# </shell> Well, I <i>think</i> I know what that means: The repository for the web site includes configuration files, one of which specifies local paths. Naturally, that file differs for the "prod" and "dev" deployments. In the local copy of "dev", I had updated that file, but hadn't checked in the changes so that they would never get transferred to "prod" by accident. I can only assume that Mercurial is complaining about that file, but let's check the status to be sure. <shell> earthli:# hg status M lib/plugins/com.earthli.webcore.init.php M site/robots.txt earthli:# </shell> Bingo! <c>lib/plugins/com.earthli.webcore.init.php</c> is the configuration file and <c>site/robots.txt</c> is the change I made to "prod" that needs to be merged in. But what's Mercurial complaining about? That it can't merge in the changes to <c>site/robots.txt</c> while there are outstanding changes to other, completely unrelated files? That seems to be the case. Why in the name of all that is holy should that be the case? Perhaps I've misunderstood something? At any rate, I only know the <c>hg status</c> command, but it only shows me that two files are modified and not that they belong to two different heads. The conflict is not evident here, which is a shame. A newbie merging mode would be nice that would hold my hand a bit more and tell me what to do here. The first thing I tried was to commit only the file that came in with the <c>pull</c> operation, <c>robots.txt</c>. <shell> # hg commit site/robots.txt abort: cannot partially commit a merge (do not specify files or patterns) </shell> Aaaarrrgggggh. Now what? I feel like a total moron. Mercurial is scolding me for trying to <iq>partially commit a merge</iq> even though, as far as I can tell, that single file is the only file that was merged. I guess it makes sense that I can't commit that file because it hasn't technically been merged yet. But do I have to commit or revert all local changes before I can do any merging? That would seem to be the case. It may technically <i>not</i> be the case, but Mercurial is not being very forthcoming on other options. In the words of Homer Simpson: "urge to kill rising."<fn> The status of my repository is that two files have been modified (as shown above). However, one of them has a special status: that it is unmerged. Out of curiosity, I tried to commit and was greeted by the following commit message in good old <c>vi</c>: <shell> HG: Enter commit message. Lines beginning with 'HG:' are removed. HG: -- HG: user: HG: branch merge HG: branch 'default' HG: changed lib/plugins/com.earthli.webcore.init.php </shell> As I suspected, the file I actually want to commit is not commitable at this time, and the one I definitely don't want to commit can be committed. Now that's a fine how-do-you-do. Then, I had a killer idea: Most version control systems have a way of ignoring files or file patterns. The two other systems with which I'm familiar both use an "ignore" command or some file with the word "ignore" in it. I checked the Mercurial help for the word "ignore" and found nothing. I searched online and found a bunch of stuff about an <c>.hgignore</c> file, which I didn't get to work correctly and other tips that said to use an <c>hgignore</c> file (without the leading dot) and that didn't work either. I'll probably look into this again later, because I really need to ignore those configuration files. So, here I am with a problem that boils down to copying a single file from one directory to another. The version control system I'm using is offering to check in a file I want it to ignore and not allowing me to merge the file I need until I take care of the file it refuses to ignore. What I ended up doing was reverting the changes to the configuration file, executing the merge, then pushing the changes from "dev" back to "prod" and, finally, restoring the altered configuration file under "dev", where it will bite me in the ass the next time, probably in six months, when I can go through this all again. Part of the problem, as I see it, is that non-trivial---heck, even trivial---merging and diffing and resolving are <i>goddamned hard</i> using only command-line tools. Doing this kind of stuff without more graphical input is an accident waiting to happen. Naturally, there will be geniuses who claim this not to be the case, but that doesn't help the rest of us---the bottom-feeders: Someone should be trying to help us get our work done, too. The other problem is that Mercurial assumes that you know exactly what your files are doing at all times without any extra help. In the example above, why does the call to <c>hg status</c> not show me that my file is unmerged? It clearly knows this. Ah, well. I imagine I'll get used to it at some point or some guru will point out what I'm doing wrong. I just feel that this is some of the polish that's missing from some of these super-tools: They get so much of the technical stuff working really well, then hide it behind an inscrutable, unhelpful interface. I've done it myself too many times: Built the basic framework and left making an actually intuitively useful application on top of it as an exercise for the next programmer. It really wouldn't be that hard to build in a mode that showed---maybe even with ASCII art---what was going on and what the poor newb's options were. One could argue that a command-line interface for a DVCS is, <i>by definition</i>, a hardcore programmer's tool, but why do we hardcore programmers always have to suffer? Why do we get tools that force us to use every bit of brain power we have just to accomplish what should be very simple tasks? Don't we ever get a break? Or should we just be grateful that there are tools like Mercurial at all? Maybe that's it. <hr> <ft>My frustration at this point is evident for all the world to see in <a href="" source="" author="">the changelog</a>.</ft>