<?xml version='1.0' encoding='utf-8'?>
<?xml-stylesheet href="/style/browser.xsl" type="text/xsl"?>
<essay xmlns="http://docbook.org/ns/docbook"
       xmlns:xlink="http://www.w3.org/1999/xlink"
       xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
       xmlns:dc='http://purl.org/dc/elements/1.1/'
       xmlns:dcterms="http://purl.org/dc/terms/"
       xmlns:gal='http://norman.walsh.name/rdf/gallery#'
       xmlns:foaf="http://xmlns.com/foaf/0.1/"
       xml:lang="en"
       version='5.0'>
<info>
<title>Mercurial heartburn</title>
<volumenum>10</volumenum>
<issuenum>81</issuenum>
<pubdate>2007-08-09T12:55:32-04:00</pubdate>
<date>$Date$</date>
<author><personname>
<firstname>Norman</firstname><surname>Walsh</surname>
</personname></author>
<copyright><year>2007</year><holder>Norman Walsh</holder></copyright>
<abstract>
<para>A tale of woe with bad parts, good parts, and a moral.</para>
</abstract>
</info>

<para xml:id='p1'>I've been <link
xlink:href="http://norman.walsh.name/2007/07/19/mercurial">using
Mercurial</link> for a few weeks now. I had no trouble switching; it's
similar enough to the other version control systems I've used. And
even though I'm usually connected to the net, I'm loving fast, local
commits. So much so that I have six or eight repositories up and
running now: my home directory, one for each of the web sites I
maintain, one for papers I've written, another for presentations,
basically most of (eventually, all of) the stuff that isn't in some
other version control system.</para>

<section xml:id="bad">
<title>The bad part</title>

<para xml:id='p2'>Then a few days ago, I happened to notice something odd (alas,
I don't recall what). Verifying local and remote repositories revealed
that my home directory repository was totally borked on the server (it showed
1 revision where locally I had 23) and the repository for this weblog
showed a bunch of errors, both locally and remotely.</para>

<para xml:id='p3'>The very worst part is that there had been no error messages
to indicate that anything had gone wrong. In fact, running an
<command>hg push</command> in my home directory <emphasis>appeared
to complete successfully</emphasis>.</para>

<para xml:id='p4'>It's hard to imagine a worse sort of error than that in a version
control system.</para>

<para xml:id='p5'>Much digging (with tremendous assistance from
<personname><firstname>Matt</firstname><surname>Mackall</surname>
</personname> at
<link xlink:href="http://selenic.com/">selenic</link>) revealed the
problem.</para>

<para xml:id='p6'>In a nutshell, my hosting company, where the server I'm pushing
to is located, terminates with extreme prejudice any process that uses
too much memory or too much CPU. My attempt to push the commit of a
50Mb image caused the Mercurial process on the server to chew up
hundreds of megabytes of memory attempting to compute diffs, and when
it crossed some threshold, it got <symbol>SIGKILL</symbol>d.</para>

<para xml:id='p7'>I think the client should have noticed this and not appeared to
complete successfully, but I'm not sure how realistic that is. I'm
willing to stipulate that <command>kill -9</command>ing one half of
system isn't something you'd expect to happen.</para>

<para xml:id='p8'>In practice, I didn't realize I'd attempted to put a 50Mb image
into the system and I don't have any practical reason to do so.</para>

<para xml:id='p9'>Matt maintains that a similar circumstance caused the errors in
my other repository. I'm not really convinced, but since there doesn't
appear to be any way to tell, I'm not pushing the point.</para>

<para xml:id='p10'>So now I've got two repositories with errors deep in their
version history. (The second of 23 transactions in one case, the 1366th
of 1420 in the other.)</para>
</section>

<section xml:id="good">
<title>The good part</title>

<para xml:id='p11'>Luckily, Mercurial has features that allowed me to deal with these
issues. They're called “queues”. I don't really grok them, but Matt helped
me use them to fix the problems.</para>

<para xml:id='p12'>In each case, I turned all of the commits that had occurred
after the point where the error occurred into a queue of patches. Then
I rolled back to the error version, fixed the errors, refreshed the
patch for that version, reapplied all the patches, and finally deleted
the queue.</para>

<para xml:id='p13'>With fixed local repositories in hand, I was able to migrate
them back to the server and all seems to be well.</para>
</section>

<section xml:id="moral">
<title>The moral</title>

<para xml:id='p14'>You might think the moral is, don't use Mercurial, but I'm not
willing to go that far. A lot of big projects are (or are going to be)
using it. I'm confident that the bugs will get worked out. The queuing
functionality seems to provide the ability to work around problems.
No, the moral is <emphasis>an unverified backup is a worthless
backup</emphasis>. Backing up your data (and really, that's what I'm using
Mercurial for here) but not checking that the backup is both readable
and correct is accepting a risk proportional to the importance of your
data. And you're backing it up because it's important, right?</para>

<para xml:id='p15'>Your gun, your bullet, your foot.</para>

<para xml:id='p16'>No more blindly pushing changes and assuming they work for
me.</para>

<programlisting><![CDATA[#!/usr/bin/perl -- # -*- Perl -*-
#
# rdf:
# dc:title HGPush
# dc:date 2007-08-09T10:35:04-04:00
# cvs:date $Date$
# dc:creator http://norman.walsh.name/knows/who/norman-walsh
# dc:rights Copyright &#169; 2007 Norman Walsh.
# cc:license http://creativecommons.org/licenses/by/3.0/
# cc:license http://www.fsf.org/licensing/licenses/info/GPLv2.html
# dc:description Pushes Mercurial commits verifying both repositories
#
# Bugs:
#  - only supports the default server path
#  - only supports server paths of the form
#    ssh://user@host/path/to/repository
#  - should check the return code of the local verify and abort
#    if it fails

open (HG, ".hg/hgrc") || die "No .hg directory?\n";

my $host = undef;
my $section = "";
my $key = "";
while (<HG>) {
    chop;
    next if /^\#/ || /^\s*$/;
    $section = $1 if /^\[(.*?)\]/;
    $key = $1 if /^([^\[\s=]+)/;
    if ($section eq 'paths' && $key eq 'default') {
	($host = $_) =~ s/^.+\s//;
    }
}

die "No default path?\n" if !defined($host);

die "Only prepared for ssh paths; found \"$host\"\n"
    unless $host =~ /^ssh:\/\/(.*?)\/(.*)$/;

my $rsh = $1;
my $path = $2;

print "Verifying local repository\n";
system("hg verify");
print "Pushing local changes to $host\n";
system("hg push");
print "Verifying remote repository\n";
system("rsh $rsh \"(cd $path; hg verify)\"");]]></programlisting>

<para xml:id='p17'>Share and enjoy.</para>
</section>
</essay>
