git-svn: do our best to ensure that our ref and rev_db are consistent
authorEric Wong <normalperson@yhbt.net>
Wed, 31 Jan 2007 21:54:23 +0000 (13:54 -0800)
committerEric Wong <normalperson@yhbt.net>
Fri, 23 Feb 2007 08:57:11 +0000 (00:57 -0800)
Defer any signals that cause termination while they are
updating; and put the update-ref call as close to the rename()
as possible.  Also, make things extra-safe (but slower) for
people using --no-metadata since they can't rely on .rev_db
being rebuilt if it's clobbered (well, I'm calling update-ref
with the -m flag for reflogs, we don't yet have a way to rebuild
.rev_db from reflogs.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
git-svn.perl

index 1fd65526d186e017640543af240dea0ccb2b421a..2206f1b250125eb9f9d27b87c505b66bb7af531b 100755 (executable)
@@ -631,6 +631,7 @@ use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
+use File::Copy qw/copy/;
 use IPC::Open3;
 
 my $_repack_nr;
@@ -645,6 +646,9 @@ BEGIN {
                                        svn:entry:committed-date/;
 }
 
+my %LOCKFILES;
+END { unlink keys %LOCKFILES if %LOCKFILES }
+
 sub fetch_all {
        my ($repo_id, $url, $fetch) = @_;
        my @gs;
@@ -1030,8 +1034,7 @@ sub do_git_commit {
                die "Failed to commit, invalid sha1: $commit\n";
        }
 
-       command_noisy('update-ref',$self->refname, $commit);
-       $self->rev_db_set($log_entry->{revision}, $commit);
+       $self->rev_db_set($log_entry->{revision}, $commit, 1);
 
        $self->{last_rev} = $log_entry->{revision};
        $self->{last_commit} = $commit;
@@ -1353,11 +1356,28 @@ sub rebuild {
 # to a revision: (41 * rev) is the byte offset.
 # A record of 40 0s denotes an empty revision.
 # And yes, it's still pretty fast (faster than Tie::File).
+# These files are disposable unless --no-metadata is set
 
 sub rev_db_set {
-       my ($self, $rev, $commit) = @_;
+       my ($self, $rev, $commit, $update_ref) = @_;
        length $commit == 40 or croak "arg3 must be a full SHA1 hexsum\n";
-       open my $fh, '+<', $self->{db_path} or croak $!;
+       my ($db, $db_lock) = ($self->{db_path}, "$self->{db_path}.lock");
+       my $sig;
+       if ($update_ref) {
+               $SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
+                           $SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };
+       }
+       $LOCKFILES{$db_lock} = 1;
+       if ($_no_metadata) {
+               copy($db, $db_lock) or die "rev_db_set(@_): ",
+                                          "Failed to copy: ",
+                                          "$db => $db_lock ($!)\n";
+       } else {
+               rename $db, $db_lock or die "rev_db_set(@_): ",
+                                           "Failed to rename: ",
+                                           "$db => $db_lock ($!)\n";
+       }
+       open my $fh, '+<', $db_lock or croak $!;
        my $offset = $rev * 41;
        # assume that append is the common case:
        seek $fh, 0, 2 or croak $!;
@@ -1370,6 +1390,18 @@ sub rev_db_set {
        seek $fh, $offset, 0 or croak $!;
        print $fh $commit,"\n" or croak $!;
        close $fh or croak $!;
+       if ($update_ref) {
+               command_noisy('update-ref', '-m', "r$rev",
+                             $self->refname, $commit);
+       }
+       rename $db_lock, $db or die "rev_db_set(@_): ", "Failed to rename: ",
+                                   "$db_lock => $db ($!)\n";
+       delete $LOCKFILES{$db_lock};
+       if ($update_ref) {
+               $SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
+                           $SIG{USR1} = $SIG{USR2} = 'DEFAULT';
+               kill $sig, $$ if defined $sig;
+       }
 }
 
 sub rev_db_get {