{"thread":{"id":"11206","subject":"[PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","startedAt":"2007-12-09T07:27:40Z","lastAt":"2007-12-13T16:30:03Z","messageCount":8,"participants":["Eric Wong","Harvey Harrison","Sam Vilain"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"62461","messageId":"1197185262-16765-1-git-send-email-normalperson@yhbt.net","threadId":"11206","inReplyTo":null,"subject":"[PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-12-09T07:27:40Z","receivedAt":"2007-12-09T07:27:40Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is very lightly tested, but describes the format I described in:\n\n  http://article.gmane.org/gmane.comp.version-control.git/67126\n\n  (more in the commit messages)\n\nI'll be out of town the next few days and I'm not sure how much I'll be\nable to follow up on it while I'm gone.  Please test, especially if\nyou're dealing with a repository where large .rev_db files are a\nproblem.\n\nJunio: not intended for master just yet, but if you hear nothing but\ngood things about it, feel free :)\n\n---\n git-svn.perl               |  323 ++++++++++++++++++++++++++++++--------------\n t/lib-git-svn.sh           |   26 ++++\n t/t9107-git-svn-migrate.sh |   16 ++-\n 3 files changed, 258 insertions(+), 107 deletions(-)\n\n-- \nEric Wong\n"},{"id":"62462","messageId":"1197185262-16765-2-git-send-email-normalperson@yhbt.net","threadId":"11206","inReplyTo":"1197185262-16765-1-git-send-email-normalperson@yhbt.net","subject":"[PATCH 1/2] git-svn: replace .rev_db with a more space-efficient .rev_map format","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-12-09T07:27:41Z","receivedAt":"2007-12-09T07:27:41Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Migrations are done automatically on an as-needed basis when new\nrevisions are to be fetched.  Stale remote branches do not get\nmigrated, yet.\n\nHowever, unless you set noMetadata or useSvkProps it's safe to\njust do:\n\n  find $GIT_DIR/svn -name '.rev_db*' -print0 | xargs rm -f\n\nto purge all the old .rev_db files.\n\nThe new format is a one-way migration and is NOT compatible with\nold versions of git-svn.\n\nThis is the replacement for the rev_db format, which was too big\nand inefficient for large repositories with a lot of sparse history\n(mainly tags).\n\nThe format is this:\n\n  - 24 bytes for every record,\n    * 4 bytes for the integer representing an SVN revision number\n    * 20 bytes representing the sha1 of a git commit\n\n  - No empty padding records like the old format\n\n  - new records are written append-only since SVN revision numbers\n    increase monotonically\n\n  - lookups on SVN revision number are done via a binary search\n\n  - Piping the file to xxd(1) -c24 is a good way of dumping it for\n    viewing or editing, should the need ever arise.\n\nAs with .rev_db, these files are disposable unless noMetadata or\nuseSvmProps is set.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n git-svn.perl               |  286 ++++++++++++++++++++++++++++----------------\n t/lib-git-svn.sh           |   26 ++++\n t/t9107-git-svn-migrate.sh |   16 ++-\n 3 files changed, 218 insertions(+), 110 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 9f884eb..b45c7e3 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -529,7 +529,7 @@ sub cmd_find_rev {\n \t\t\t    \"$head history\\n\";\n \t\t}\n \t\tmy $desired_revision = substr($revision_or_hash, 1);\n-\t\t$result = $gs->rev_db_get($desired_revision);\n+\t\t$result = $gs->rev_map_get($desired_revision);\n \t} else {\n \t\tmy (undef, $rev, undef) = cmt_metadata($revision_or_hash);\n \t\t$result = $rev;\n@@ -1128,12 +1128,12 @@ sub working_head_info {\n \t\tif (defined $url && defined $rev) {\n \t\t\tnext if $max{$url} and $max{$url} < $rev;\n \t\t\tif (my $gs = Git::SVN->find_by_url($url)) {\n-\t\t\t\tmy $c = $gs->rev_db_get($rev);\n+\t\t\t\tmy $c = $gs->rev_map_get($rev);\n \t\t\t\tif ($c && $c eq $hash) {\n \t\t\t\t\tclose $fh; # break the pipe\n \t\t\t\t\treturn ($url, $rev, $uuid, $gs);\n \t\t\t\t} else {\n-\t\t\t\t\t$max{$url} ||= $gs->rev_db_max;\n+\t\t\t\t\t$max{$url} ||= $gs->rev_map_max;\n \t\t\t\t}\n \t\t\t}\n \t\t}\n@@ -1234,6 +1234,8 @@ sub md5sum {\n package Git::SVN;\n use strict;\n use warnings;\n+use Fcntl qw/:DEFAULT :seek/;\n+use constant rev_map_fmt => 'NH40';\n use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent\n             $_repack $_repack_flags $_use_svm_props $_head\n             $_use_svnsync_props $no_reuse_existing $_minimize_url\n@@ -1362,7 +1364,7 @@ sub fetch_all {\n \tif ($fetch) {\n \t\tforeach my $p (sort keys %$fetch) {\n \t\t\tmy $gs = Git::SVN->new($fetch->{$p}, $repo_id, $p);\n-\t\t\tmy $lr = $gs->rev_db_max;\n+\t\t\tmy $lr = $gs->rev_map_max;\n \t\t\tif (defined $lr) {\n \t\t\t\t$base = $lr if ($lr < $base);\n \t\t\t}\n@@ -1897,38 +1899,20 @@ sub last_rev_commit {\n \t\t\treturn ($rev, $c);\n \t\t}\n \t}\n-\tmy $db_path = $self->db_path;\n-\tunless (-e $db_path) {\n+\tmy $map_path = $self->map_path;\n+\tunless (-e $map_path) {\n \t\t($self->{last_rev}, $self->{last_commit}) = (undef, undef);\n \t\treturn (undef, undef);\n \t}\n-\tmy $offset = -41; # from tail\n-\tmy $rl;\n-\topen my $fh, '<', $db_path or croak \"$db_path not readable: $!\\n\";\n-\tsysseek($fh, $offset, 2); # don't care for errors\n-\tsysread($fh, $rl, 41) == 41 or return (undef, undef);\n-\tchomp $rl;\n-\twhile (('0' x40) eq $rl && sysseek($fh, 0, 1) != 0) {\n-\t\t$offset -= 41;\n-\t\tsysseek($fh, $offset, 2); # don't care for errors\n-\t\tsysread($fh, $rl, 41) == 41 or return (undef, undef);\n-\t\tchomp $rl;\n-\t}\n-\tif ($c && $c ne $rl) {\n-\t\tdie \"$db_path and \", $self->refname,\n-\t\t    \" inconsistent!:\\n$c != $rl\\n\";\n-\t}\n-\tmy $rev = sysseek($fh, 0, 1) or croak $!;\n-\t$rev =  ($rev - 41) / 41;\n-\tclose $fh or croak $!;\n-\t($self->{last_rev}, $self->{last_commit}) = ($rev, $c);\n-\treturn ($rev, $c);\n+\tmy ($rev, $commit) = $self->rev_map_max;\n+\t($self->{last_rev}, $self->{last_commit}) = ($rev, $commit);\n+\treturn ($rev, $commit);\n }\n \n sub get_fetch_range {\n \tmy ($self, $min, $max) = @_;\n \t$max ||= $self->ra->get_latest_revnum;\n-\t$min ||= $self->rev_db_max;\n+\t$min ||= $self->rev_map_max;\n \t(++$min, $max);\n }\n \n@@ -2073,7 +2057,7 @@ sub do_git_commit {\n \t\t    \" was r$lr, but we are about to fetch: \",\n \t\t    \"r$log_entry->{revision}!\\n\";\n \t}\n-\tif (my $c = $self->rev_db_get($log_entry->{revision})) {\n+\tif (my $c = $self->rev_map_get($log_entry->{revision})) {\n \t\tcroak \"$log_entry->{revision} = $c already exists! \",\n \t\t      \"Why are we refetching it?\\n\";\n \t}\n@@ -2116,14 +2100,14 @@ sub do_git_commit {\n \t\tdie \"Failed to commit, invalid sha1: $commit\\n\";\n \t}\n \n-\t$self->rev_db_set($log_entry->{revision}, $commit, 1);\n+\t$self->rev_map_set($log_entry->{revision}, $commit, 1);\n \n \t$self->{last_rev} = $log_entry->{revision};\n \t$self->{last_commit} = $commit;\n \tprint \"r$log_entry->{revision}\";\n \tif (defined $log_entry->{svm_revision}) {\n \t\t print \" (\\@$log_entry->{svm_revision})\";\n-\t\t $self->rev_db_set($log_entry->{svm_revision}, $commit,\n+\t\t $self->rev_map_set($log_entry->{svm_revision}, $commit,\n \t\t                   0, $self->svm_uuid);\n \t}\n \tprint \" = $commit ($self->{ref_id})\\n\";\n@@ -2465,25 +2449,44 @@ sub set_tree {\n \t}\n }\n \n+sub rebuild_from_rev_db {\n+\tmy ($self, $path) = @_;\n+\tmy $r = -1;\n+\topen my $fh, '<', $path or croak \"open: $!\";\n+\twhile (<$fh>) {\n+\t\tlength($_) == 41 or croak \"inconsistent size in ($_) != 41\";\n+\t\tchomp($_);\n+\t\t++$r;\n+\t\tnext if $_ eq ('0' x 40);\n+\t\t$self->rev_map_set($r, $_);\n+\t\tprint \"r$r = $_\\n\";\n+\t}\n+\tclose $fh or croak \"close: $!\";\n+\tunlink $path or croak \"unlink: $!\";\n+}\n+\n sub rebuild {\n \tmy ($self) = @_;\n-\tmy $db_path = $self->db_path;\n-\treturn if (-e $db_path && ! -z $db_path);\n+\tmy $map_path = $self->map_path;\n+\treturn if (-e $map_path && ! -z $map_path);\n \treturn unless ::verify_ref($self->refname.'^0');\n-\tif (-f $self->{db_root}) {\n-\t\trename $self->{db_root}, $db_path or die\n-\t\t     \"rename $self->{db_root} => $db_path failed: $!\\n\";\n-\t\tmy ($dir, $base) = ($db_path =~ m#^(.*?)/?([^/]+)$#);\n-\t\tsymlink $base, $self->{db_root} or die\n-\t\t     \"symlink $base => $self->{db_root} failed: $!\\n\";\n+\tif ($self->use_svm_props || $self->no_metadata) {\n+\t\tmy $rev_db = $self->rev_db_path;\n+\t\t$self->rebuild_from_rev_db($rev_db);\n+\t\tif ($self->use_svm_props) {\n+\t\t\tmy $svm_rev_db = $self->rev_db_path($self->svm_uuid);\n+\t\t\t$self->rebuild_from_rev_db($svm_rev_db);\n+\t\t}\n+\t\t$self->unlink_rev_db_symlink;\n \t\treturn;\n \t}\n-\tprint \"Rebuilding $db_path ...\\n\";\n-\tmy ($log, $ctx) = command_output_pipe(\"log\", '--no-color', $self->refname);\n-\tmy $latest;\n+\tprint \"Rebuilding $map_path ...\\n\";\n+\tmy ($log, $ctx) =\n+\t    command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,\n+\t                        $self->refname, '--');\n \tmy $full_url = $self->full_url;\n \tremove_username($full_url);\n-\tmy $svn_uuid;\n+\tmy $svn_uuid = $self->ra_uuid;\n \tmy $c;\n \twhile (<$log>) {\n \t\tif ( m{^commit ($::sha1)$} ) {\n@@ -2499,46 +2502,64 @@ sub rebuild {\n \n \t\t# if we merged or otherwise started elsewhere, this is\n \t\t# how we break out of it\n-\t\tif ((defined $svn_uuid && ($uuid ne $svn_uuid)) ||\n+\t\tif (($uuid ne $svn_uuid) ||\n \t\t    ($full_url && $url && ($url ne $full_url))) {\n \t\t\tnext;\n \t\t}\n-\t\t$latest ||= $rev;\n-\t\t$svn_uuid ||= $uuid;\n \n-\t\t$self->rev_db_set($rev, $c);\n+\t\t$self->rev_map_set($rev, $c);\n \t\tprint \"r$rev = $c\\n\";\n \t}\n \tcommand_close_pipe($log, $ctx);\n-\tprint \"Done rebuilding $db_path\\n\";\n+\tprint \"Done rebuilding $map_path\\n\";\n+\tmy $rev_db_path = $self->rev_db_path;\n+\tif (-f $self->rev_db_path) {\n+\t\tunlink $self->rev_db_path or croak \"unlink: $!\";\n+\t}\n+\t$self->unlink_rev_db_symlink;\n }\n \n-# rev_db:\n+# rev_map:\n # Tie::File seems to be prone to offset errors if revisions get sparse,\n # it's not that fast, either.  Tie::File is also not in Perl 5.6.  So\n # one of my favorite modules is out :<  Next up would be one of the DBM\n-# modules, but I'm not sure which is most portable...  So I'll just\n-# go with something that's plain-text, but still capable of\n-# being randomly accessed.  So here's my ultra-simple fixed-width\n-# database.  All records are 40 characters + \"\\n\", so it's easy to seek\n-# to a revision: (41 * rev) is the byte offset.\n-# A record of 40 0s denotes an empty revision.\n-# And yes, it's still pretty fast (faster than Tie::File).\n+# modules, but I'm not sure which is most portable...\n+#\n+# This is the replacement for the rev_db format, which was too big\n+# and inefficient for large repositories with a lot of sparse history\n+# (mainly tags)\n+#\n+# The format is this:\n+#   - 24 bytes for every record,\n+#     * 4 bytes for the integer representing an SVN revision number\n+#     * 20 bytes representing the sha1 of a git commit\n+#   - No empty padding records like the old format\n+#   - new records are written append-only since SVN revision numbers\n+#     increase monotonically\n+#   - lookups on SVN revision number are done via a binary search\n+#   - Piping the file to xxd(1) -c24 is a good way of dumping it for\n+#     viewing or editing, should the need ever arise.\n+#\n # These files are disposable unless noMetadata or useSvmProps is set\n \n-sub _rev_db_set {\n+sub _rev_map_set {\n \tmy ($fh, $rev, $commit) = @_;\n-\tmy $offset = $rev * 41;\n-\t# assume that append is the common case:\n-\tseek $fh, 0, 2 or croak $!;\n-\tmy $pos = tell $fh;\n-\tif ($pos < $offset) {\n-\t\tfor (1 .. (($offset - $pos) / 41)) {\n-\t\t\tprint $fh (('0' x 40),\"\\n\") or croak $!;\n+\n+\tmy $size = (stat($fh))[7];\n+\t($size % 24) == 0 or croak \"inconsistent size: $size\";\n+\n+\tif ($size > 0) {\n+\t\tsysseek($fh, -24, SEEK_END) or croak \"seek: $!\";\n+\t\tmy $read = sysread($fh, my $buf, 24) or croak \"read: $!\";\n+\t\t$read == 24 or croak \"read only $read bytes (!= 24)\";\n+\t\tmy ($last_rev, $last_commit) = unpack(rev_map_fmt, $buf);\n+\t\tif ($last_rev >= $rev) {\n+\t\t\tcroak \"last_rev is higher!: $last_rev >= $rev\";\n \t\t}\n \t}\n-\tseek $fh, $offset, 0 or croak $!;\n-\tprint $fh $commit,\"\\n\" or croak $!;\n+\tsysseek($fh, 0, SEEK_END) or croak \"seek: $!\";\n+\tsyswrite($fh, pack(rev_map_fmt, $rev, $commit), 24) == 24 or\n+\t  croak \"write: $!\";\n }\n \n sub mkfile {\n@@ -2551,10 +2572,10 @@ sub mkfile {\n \t}\n }\n \n-sub rev_db_set {\n+sub rev_map_set {\n \tmy ($self, $rev, $commit, $update_ref, $uuid) = @_;\n \tlength $commit == 40 or die \"arg3 must be a full SHA1 hexsum\\n\";\n-\tmy $db = $self->db_path($uuid);\n+\tmy $db = $self->map_path($uuid);\n \tmy $db_lock = \"$db.lock\";\n \tmy $sig;\n \tif ($update_ref) {\n@@ -2569,16 +2590,18 @@ sub rev_db_set {\n \t# and we can't afford to lose it because rebuild() won't work\n \tif ($self->use_svm_props || $self->no_metadata) {\n \t\t$sync = 1;\n-\t\tcopy($db, $db_lock) or die \"rev_db_set(@_): \",\n+\t\tcopy($db, $db_lock) or die \"rev_map_set(@_): \",\n \t\t\t\t\t   \"Failed to copy: \",\n \t\t\t\t\t   \"$db => $db_lock ($!)\\n\";\n \t} else {\n-\t\trename $db, $db_lock or die \"rev_db_set(@_): \",\n+\t\trename $db, $db_lock or die \"rev_map_set(@_): \",\n \t\t\t\t\t    \"Failed to rename: \",\n \t\t\t\t\t    \"$db => $db_lock ($!)\\n\";\n \t}\n-\topen my $fh, '+<', $db_lock or die \"Couldn't open $db_lock: $!\\n\";\n-\t_rev_db_set($fh, $rev, $commit);\n+\n+\tsysopen(my $fh, $db_lock, O_RDWR | O_APPEND | O_CREAT)\n+\t     or croak \"Couldn't open $db_lock: $!\\n\";\n+\t_rev_map_set($fh, $rev, $commit);\n \tif ($sync) {\n \t\t$fh->flush or die \"Couldn't flush $db_lock: $!\\n\";\n \t\t$fh->sync or die \"Couldn't sync $db_lock: $!\\n\";\n@@ -2589,7 +2612,7 @@ sub rev_db_set {\n \t\tcommand_noisy('update-ref', '-m', \"r$rev\",\n \t\t              $self->refname, $commit);\n \t}\n-\trename $db_lock, $db or die \"rev_db_set(@_): \", \"Failed to rename: \",\n+\trename $db_lock, $db or die \"rev_map_set(@_): \", \"Failed to rename: \",\n \t                            \"$db_lock => $db ($!)\\n\";\n \tdelete $LOCKFILES{$db_lock};\n \tif ($update_ref) {\n@@ -2599,29 +2622,61 @@ sub rev_db_set {\n \t}\n }\n \n-sub rev_db_max {\n+sub rev_map_max {\n \tmy ($self) = @_;\n \t$self->rebuild;\n-\tmy $db_path = $self->db_path;\n-\tmy @stat = stat $db_path or return 0;\n-\t($stat[7] % 41) == 0 or die \"$db_path inconsistent size: $stat[7]\\n\";\n-\tmy $max = $stat[7] / 41;\n-\t(($max > 0) ? $max - 1 : 0);\n+\tmy $map_path = $self->map_path;\n+\tstat $map_path or return wantarray ? (0, undef) : 0;\n+\tsysopen(my $fh, $map_path, O_RDONLY) or croak \"open: $!\";\n+\tmy $size = (stat($fh))[7];\n+\t($size % 24) == 0 or croak \"inconsistent size: $size\";\n+\n+\tif ($size == 0) {\n+\t\tclose $fh or croak \"close: $!\";\n+\t\treturn wantarray ? (0, undef) : 0;\n+\t}\n+\n+\tsysseek($fh, -24, SEEK_END);\n+\tsysread($fh, my $buf, 24) == 24 or croak \"read: $!\";\n+\tclose $fh or croak \"close: $!\";\n+\tmy ($r, $c) = unpack(rev_map_fmt, $buf);\n+\twantarray ? ($r, $c) : $r;\n }\n \n-sub rev_db_get {\n+sub rev_map_get {\n \tmy ($self, $rev, $uuid) = @_;\n-\tmy $ret;\n-\tmy $offset = $rev * 41;\n-\tmy $db_path = $self->db_path($uuid);\n-\treturn undef unless -e $db_path;\n-\topen my $fh, '<', $db_path or croak $!;\n-\tif (sysseek($fh, $offset, 0) == $offset) {\n-\t\tmy $read = sysread($fh, $ret, 40);\n-\t\t$ret = undef if ($read != 40 || $ret eq ('0'x40));\n+\tmy $map_path = $self->map_path($uuid);\n+\treturn undef unless -e $map_path;\n+\n+\tsysopen(my $fh, $map_path, O_RDONLY) or croak \"open: $!\";\n+\tmy $size = (stat($fh))[7];\n+\t($size % 24) == 0 or croak \"inconsistent size: $size\";\n+\n+\tif ($size == 0) {\n+\t\tclose $fh or croak \"close: $fh\";\n+\t\treturn undef;\n \t}\n-\tclose $fh or croak $!;\n-\t$ret;\n+\n+\tmy ($l, $u) = (0, $size - 24);\n+\tmy ($r, $c, $buf);\n+\n+\twhile ($l <= $u) {\n+\t\tmy $i = int(($l/24 + $u/24) / 2) * 24;\n+\t\tsysseek($fh, $i, SEEK_SET) or croak \"seek: $!\";\n+\t\tsysread($fh, my $buf, 24) == 24 or croak \"read: $!\";\n+\t\tmy ($r, $c) = unpack('NH40', $buf);\n+\n+\t\tif ($r < $rev) {\n+\t\t\t$l = $i + 24;\n+\t\t} elsif ($r > $rev) {\n+\t\t\t$u = $i - 24;\n+\t\t} else { # $r == $rev\n+\t\t\tclose($fh) or croak \"close: $!\";\n+\t\t\treturn $c;\n+\t\t}\n+\t}\n+\tclose($fh) or croak \"close: $!\";\n+\tundef;\n }\n \n # Finds the first svn revision that exists on (if $eq_ok is true) or\n@@ -2633,7 +2688,7 @@ sub find_rev_before {\n \t--$rev unless $eq_ok;\n \t$min_rev ||= 1;\n \twhile ($rev >= $min_rev) {\n-\t\tif (my $c = $self->rev_db_get($rev)) {\n+\t\tif (my $c = $self->rev_map_get($rev)) {\n \t\t\treturn ($rev, $c);\n \t\t}\n \t\t--$rev;\n@@ -2648,9 +2703,9 @@ sub find_rev_before {\n sub find_rev_after {\n \tmy ($self, $rev, $eq_ok, $max_rev) = @_;\n \t++$rev unless $eq_ok;\n-\t$max_rev ||= $self->rev_db_max();\n+\t$max_rev ||= $self->rev_map_max;\n \twhile ($rev <= $max_rev) {\n-\t\tif (my $c = $self->rev_db_get($rev)) {\n+\t\tif (my $c = $self->rev_map_get($rev)) {\n \t\t\treturn ($rev, $c);\n \t\t}\n \t\t++$rev;\n@@ -2673,13 +2728,32 @@ sub _new {\n \tbless {\n \t\tref_id => $ref_id, dir => $dir, index => \"$dir/index\",\n \t        path => $path, config => \"$ENV{GIT_DIR}/svn/config\",\n-\t        db_root => \"$dir/.rev_db\", repo_id => $repo_id }, $class;\n+\t        map_root => \"$dir/.rev_map\", repo_id => $repo_id }, $class;\n }\n \n-sub db_path {\n+# for read-only access of old .rev_db formats\n+sub unlink_rev_db_symlink {\n+\tmy ($self) = @_;\n+\tmy $link = $self->rev_db_path;\n+\t$link =~ s/\\.[\\w-]+$// or croak \"missing UUID at the end of $link\";\n+\tif (-l $link) {\n+\t\tunlink $link or croak \"unlink: $link failed!\";\n+\t}\n+}\n+\n+sub rev_db_path {\n+\tmy ($self, $uuid) = @_;\n+\tmy $db_path = $self->map_path($uuid);\n+\t$db_path =~ s{/\\.rev_map\\.}{/\\.rev_db\\.}\n+\t    or croak \"map_path: $db_path does not contain '/.rev_map.' !\";\n+\t$db_path;\n+}\n+\n+# the new replacement for .rev_db\n+sub map_path {\n \tmy ($self, $uuid) = @_;\n \t$uuid ||= $self->ra_uuid;\n-\t\"$self->{db_root}.$uuid\";\n+\t\"$self->{map_root}.$uuid\";\n }\n \n sub uri_encode {\n@@ -3763,7 +3837,7 @@ sub gs_fetch_loop_common {\n \n \t\t\tforeach my $gs ($self->match_globs(\\%exists, $paths,\n \t\t\t                                   $globs, $r)) {\n-\t\t\t\tif ($gs->rev_db_max >= $r) {\n+\t\t\t\tif ($gs->rev_map_max >= $r) {\n \t\t\t\t\tnext;\n \t\t\t\t}\n \t\t\t\tnext unless $gs->match_paths($paths, $r);\n@@ -3789,12 +3863,6 @@ sub gs_fetch_loop_common {\n \t\t\t\t$ra_invalid = undef;\n \t\t\t}\n \t\t}\n-\t\t# pre-fill the .rev_db since it'll eventually get filled in\n-\t\t# with '0' x40 if something new gets committed\n-\t\tforeach my $gs (@$gsv) {\n-\t\t\tnext if defined $gs->rev_db_get($max);\n-\t\t\t$gs->rev_db_set($max, 0 x40);\n-\t\t}\n \t\tforeach my $g (@$globs) {\n \t\t\tmy $k = \"svn-remote.$g->{remote}.$g->{t}-maxRev\";\n \t\t\tGit::SVN::tmp_config($k, $max);\n@@ -4030,7 +4098,7 @@ sub git_svn_log_cmd {\n \tpush @cmd, @log_opts;\n \tif (defined $r_max && $r_max == $r_min) {\n \t\tpush @cmd, '--max-count=1';\n-\t\tif (my $c = $gs->rev_db_get($r_max)) {\n+\t\tif (my $c = $gs->rev_map_get($r_max)) {\n \t\t\tpush @cmd, $c;\n \t\t}\n \t} elsif (defined $r_max) {\n@@ -4311,6 +4379,16 @@ package Git::SVN::Migration;\n #              --use-separate-remotes option in git-clone (now default)\n #            - we do not automatically migrate to this (following\n #              the example set by core git)\n+#\n+# v5 layout: .rev_db.$UUID => .rev_map.$UUID\n+#            - newer, more-efficient format that uses 24-bytes per record\n+#              with no filler space.\n+#            - use xxd -c24 < .rev_map.$UUID to view and debug\n+#            - This is a one-way migration, repositories updated to the\n+#              new format will not be able to use old git-svn without\n+#              rebuilding the .rev_db.  Rebuilding the rev_db is not\n+#              possible if noMetadata or useSvmProps are set; but should\n+#              be no problem for users that use the (sensible) defaults.\n use strict;\n use warnings;\n use Carp qw/croak/;\ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 8d4a447..9ee35e7 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -82,3 +82,29 @@ stop_httpd () {\n \ttest -z \"$SVN_HTTPD_PORT\" && return\n \t\"$SVN_HTTPD_PATH\" -f \"$GIT_DIR\"/httpd.conf -k stop\n }\n+\n+convert_to_rev_db () {\n+\tperl -w -- - \"$@\" <<\\EOF\n+use strict;\n+@ARGV == 2 or die \"Usage: convert_to_rev_db <input> <output>\";\n+open my $wr, '+>', $ARGV[1] or die \"$!: couldn't open: $ARGV[1]\";\n+open my $rd, '<', $ARGV[0] or die \"$!: couldn't open: $ARGV[0]\";\n+my $size = (stat($rd))[7];\n+($size % 24) == 0 or die \"Inconsistent size: $size\";\n+while (sysread($rd, my $buf, 24) == 24) {\n+\tmy ($r, $c) = unpack('NH40', $buf);\n+\tmy $offset = $r * 41;\n+\tseek $wr, 0, 2 or die $!;\n+\tmy $pos = tell $wr;\n+\tif ($pos < $offset) {\n+\t\tfor (1 .. (($offset - $pos) / 41)) {\n+\t\t\tprint $wr (('0' x 40),\"\\n\") or die $!;\n+\t\t}\n+\t}\n+\tseek $wr, $offset, 0 or die $!;\n+\tprint $wr $c,\"\\n\" or die $!;\n+}\n+close $wr or die $!;\n+close $rd or die $!;\n+EOF\n+}\ndiff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh\nindex 67fdf70..0a41d52 100755\n--- a/t/t9107-git-svn-migrate.sh\n+++ b/t/t9107-git-svn-migrate.sh\n@@ -97,15 +97,19 @@ test_expect_success 'migrate --minimize on old inited layout' \"\n \tgrep '^:refs/remotes/git-svn' fetch.out\n \t\"\n \n-test_expect_success  \".rev_db auto-converted to .rev_db.UUID\" \"\n+test_expect_success  \".rev_db auto-converted to .rev_map.UUID\" \"\n \tgit-svn fetch -i trunk &&\n-\texpect=$GIT_DIR/svn/trunk/.rev_db.* &&\n+\ttest -z \\\"\\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\\\" &&\n+\texpect=\\\"\\$(ls $GIT_DIR/svn/trunk/.rev_map.*)\\\" &&\n \ttest -n \\\"\\$expect\\\" &&\n-\tmv \\$expect $GIT_DIR/svn/trunk/.rev_db &&\n+\trev_db=\\$(echo \\$expect | sed -e 's,_map,_db,') &&\n+\tconvert_to_rev_db \\$expect \\$rev_db &&\n+\trm -f \\$expect &&\n+\ttest -f \\$rev_db &&\n \tgit-svn fetch -i trunk &&\n-\ttest -L $GIT_DIR/svn/trunk/.rev_db &&\n-\ttest -f \\$expect &&\n-\tcmp \\$expect $GIT_DIR/svn/trunk/.rev_db\n+\ttest -z \\\"\\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\\\" &&\n+\ttest ! -e $GIT_DIR/svn/trunk/.rev_db &&\n+\ttest -f \\$expect\n \t\"\n \n test_done\n-- \n1.5.3.7.1114.g6df1d\n"},{"id":"62463","messageId":"1197185262-16765-3-git-send-email-normalperson@yhbt.net","threadId":"11206","inReplyTo":"1197185262-16765-1-git-send-email-normalperson@yhbt.net","subject":"[PATCH 2/2] git-svn: reinstate old rev_db optimization in new rev_map","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-12-09T07:27:42Z","receivedAt":"2007-12-09T07:27:42Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This reinstates an old optimization in .rev_db which\nstored the highest revision number we scanned, allowing\nus to avoid scanning the SVN log for those revisions\nagain in a subsequent invocation.\n\nThis means the last 24-byte record in a .rev_map file\ncan be a 4-byte SVN revision number with 20-bytes of\nzeroes representing a non-existent commit.  This record\ncan and will be overwritten when a new commit iff\nthe commit is all zeroes.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n git-svn.perl |   71 ++++++++++++++++++++++++++++++++++++++++++++++-----------\n 1 files changed, 57 insertions(+), 14 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex b45c7e3..55484f7 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -1904,7 +1904,7 @@ sub last_rev_commit {\n \t\t($self->{last_rev}, $self->{last_commit}) = (undef, undef);\n \t\treturn (undef, undef);\n \t}\n-\tmy ($rev, $commit) = $self->rev_map_max;\n+\tmy ($rev, $commit) = $self->rev_map_max(1);\n \t($self->{last_rev}, $self->{last_commit}) = ($rev, $commit);\n \treturn ($rev, $commit);\n }\n@@ -2534,11 +2534,16 @@ sub rebuild {\n #     * 4 bytes for the integer representing an SVN revision number\n #     * 20 bytes representing the sha1 of a git commit\n #   - No empty padding records like the old format\n+#     (except the last record, which can be overwritten)\n #   - new records are written append-only since SVN revision numbers\n #     increase monotonically\n #   - lookups on SVN revision number are done via a binary search\n-#   - Piping the file to xxd(1) -c24 is a good way of dumping it for\n-#     viewing or editing, should the need ever arise.\n+#   - Piping the file to xxd -c24 is a good way of dumping it for\n+#     viewing or editing (piped back through xxd -r), should the need\n+#     ever arise.\n+#   - The last record can be padding revision with an all-zero sha1\n+#     This is used to optimize fetch performance when using multiple\n+#     \"fetch\" directives in .git/config\n #\n # These files are disposable unless noMetadata or useSvmProps is set\n \n@@ -2548,16 +2553,32 @@ sub _rev_map_set {\n \tmy $size = (stat($fh))[7];\n \t($size % 24) == 0 or croak \"inconsistent size: $size\";\n \n+\tmy $wr_offset = 0;\n \tif ($size > 0) {\n \t\tsysseek($fh, -24, SEEK_END) or croak \"seek: $!\";\n \t\tmy $read = sysread($fh, my $buf, 24) or croak \"read: $!\";\n \t\t$read == 24 or croak \"read only $read bytes (!= 24)\";\n \t\tmy ($last_rev, $last_commit) = unpack(rev_map_fmt, $buf);\n-\t\tif ($last_rev >= $rev) {\n-\t\t\tcroak \"last_rev is higher!: $last_rev >= $rev\";\n+\t\tif ($last_commit eq ('0' x40)) {\n+\t\t\tif ($size >= 48) {\n+\t\t\t\tsysseek($fh, -48, SEEK_END) or croak \"seek: $!\";\n+\t\t\t\t$read = sysread($fh, $buf, 24) or\n+\t\t\t\t    croak \"read: $!\";\n+\t\t\t\t$read == 24 or\n+\t\t\t\t    croak \"read only $read bytes (!= 24)\";\n+\t\t\t\t($last_rev, $last_commit) =\n+\t\t\t\t    unpack(rev_map_fmt, $buf);\n+\t\t\t\tif ($last_commit eq ('0' x40)) {\n+\t\t\t\t\tcroak \"inconsistent .rev_map\\n\";\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif ($last_rev >= $rev) {\n+\t\t\t\tcroak \"last_rev is higher!: $last_rev >= $rev\";\n+\t\t\t}\n+\t\t\t$wr_offset = -24;\n \t\t}\n \t}\n-\tsysseek($fh, 0, SEEK_END) or croak \"seek: $!\";\n+\tsysseek($fh, $wr_offset, SEEK_END) or croak \"seek: $!\";\n \tsyswrite($fh, pack(rev_map_fmt, $rev, $commit), 24) == 24 or\n \t  croak \"write: $!\";\n }\n@@ -2599,7 +2620,7 @@ sub rev_map_set {\n \t\t\t\t\t    \"$db => $db_lock ($!)\\n\";\n \t}\n \n-\tsysopen(my $fh, $db_lock, O_RDWR | O_APPEND | O_CREAT)\n+\tsysopen(my $fh, $db_lock, O_RDWR | O_CREAT)\n \t     or croak \"Couldn't open $db_lock: $!\\n\";\n \t_rev_map_set($fh, $rev, $commit);\n \tif ($sync) {\n@@ -2622,25 +2643,40 @@ sub rev_map_set {\n \t}\n }\n \n+# If want_commit, this will return an array of (rev, commit) where\n+# commit _must_ be a valid commit in the archive.\n+# Otherwise, it'll return the max revision (whether or not the\n+# commit is valid or just a 0x40 placeholder).\n sub rev_map_max {\n-\tmy ($self) = @_;\n+\tmy ($self, $want_commit) = @_;\n \t$self->rebuild;\n \tmy $map_path = $self->map_path;\n-\tstat $map_path or return wantarray ? (0, undef) : 0;\n+\tstat $map_path or return $want_commit ? (0, undef) : 0;\n \tsysopen(my $fh, $map_path, O_RDONLY) or croak \"open: $!\";\n \tmy $size = (stat($fh))[7];\n \t($size % 24) == 0 or croak \"inconsistent size: $size\";\n \n \tif ($size == 0) {\n \t\tclose $fh or croak \"close: $!\";\n-\t\treturn wantarray ? (0, undef) : 0;\n+\t\treturn $want_commit ? (0, undef) : 0;\n \t}\n \n-\tsysseek($fh, -24, SEEK_END);\n+\tsysseek($fh, -24, SEEK_END) or croak \"seek: $!\";\n \tsysread($fh, my $buf, 24) == 24 or croak \"read: $!\";\n-\tclose $fh or croak \"close: $!\";\n \tmy ($r, $c) = unpack(rev_map_fmt, $buf);\n-\twantarray ? ($r, $c) : $r;\n+\tif ($want_commit && $c eq ('0' x40)) {\n+\t\tif ($size < 48) {\n+\t\t\treturn $want_commit ? (0, undef) : 0;\n+\t\t}\n+\t\tsysseek($fh, -48, SEEK_END) or croak \"seek: $!\";\n+\t\tsysread($fh, $buf, 24) == 24 or croak \"read: $!\";\n+\t\t($r, $c) = unpack(rev_map_fmt, $buf);\n+\t\tif ($c eq ('0'x40)) {\n+\t\t\tcroak \"Penultimate record is all-zeroes in $map_path\";\n+\t\t}\n+\t}\n+\tclose $fh or croak \"close: $!\";\n+\t$want_commit ? ($r, $c) : $r;\n }\n \n sub rev_map_get {\n@@ -2672,7 +2708,7 @@ sub rev_map_get {\n \t\t\t$u = $i - 24;\n \t\t} else { # $r == $rev\n \t\t\tclose($fh) or croak \"close: $!\";\n-\t\t\treturn $c;\n+\t\t\treturn $c eq ('0' x 40) ? undef : $c;\n \t\t}\n \t}\n \tclose($fh) or croak \"close: $!\";\n@@ -3863,6 +3899,13 @@ sub gs_fetch_loop_common {\n \t\t\t\t$ra_invalid = undef;\n \t\t\t}\n \t\t}\n+\t\t# pre-fill the .rev_db since it'll eventually get filled in\n+\t\t# with '0' x40 if something new gets committed\n+\t\tforeach my $gs (@$gsv) {\n+\t\t\tnext if $gs->rev_map_max >= $max;\n+\t\t\tnext if defined $gs->rev_map_get($max);\n+\t\t\t$gs->rev_map_set($max, 0 x40);\n+\t\t}\n \t\tforeach my $g (@$globs) {\n \t\t\tmy $k = \"svn-remote.$g->{remote}.$g->{t}-maxRev\";\n \t\t\tGit::SVN::tmp_config($k, $max);\n-- \n1.5.3.7.1114.g6df1d\n"},{"id":"62521","messageId":"1197233768.7185.6.camel@brick","threadId":"11206","inReplyTo":"1197185262-16765-1-git-send-email-normalperson@yhbt.net","subject":"Re: [PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","fromName":"Harvey Harrison","fromEmail":"harvey.harrison@gmail.com","sentAt":"2007-12-09T20:56:08Z","receivedAt":"2007-12-09T20:56:08Z","isPatch":true,"sender":{"key":"harvey.harrison@gmail.com","avatar":null},"body":"On Sat, 2007-12-08 at 23:27 -0800, Eric Wong wrote:\n> This is very lightly tested, but describes the format I described in:\n> \n>   http://article.gmane.org/gmane.comp.version-control.git/67126\n> \n>   (more in the commit messages)\n> \n> I'll be out of town the next few days and I'm not sure how much I'll be\n> able to follow up on it while I'm gone.  Please test, especially if\n> you're dealing with a repository where large .rev_db files are a\n> problem.\n> \n> Junio: not intended for master just yet, but if you hear nothing but\n> good things about it, feel free :)\n\n\nPreliminary tests against the gcc repo are going swimmingly.\n\nSuccessful git svn rebase against trunk, doing a full git svn fetch\nnow to build rev_maps for all svn branches/tags.  At halfway through\nspace has decreased from ~2GB to 17MB for about half of the needed\nmetadata.\n\nCheers,\n\nHarvey\n"},{"id":"62527","messageId":"1197248646.7185.25.camel@brick","threadId":"11206","inReplyTo":"1197233768.7185.6.camel@brick","subject":"Re: [PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","fromName":"Harvey Harrison","fromEmail":"harvey.harrison@gmail.com","sentAt":"2007-12-10T01:04:06Z","receivedAt":"2007-12-10T01:04:06Z","isPatch":true,"sender":{"key":"harvey.harrison@gmail.com","avatar":null},"body":"On Sun, 2007-12-09 at 12:56 -0800, Harvey Harrison wrote:\n> On Sat, 2007-12-08 at 23:27 -0800, Eric Wong wrote:\n> > This is very lightly tested, but describes the format I described in:\n> > \n> >   http://article.gmane.org/gmane.comp.version-control.git/67126\n> > \n> >   (more in the commit messages)\n> > \n> > I'll be out of town the next few days and I'm not sure how much I'll be\n> > able to follow up on it while I'm gone.  Please test, especially if\n> > you're dealing with a repository where large .rev_db files are a\n> > problem.\n> > \n> > Junio: not intended for master just yet, but if you hear nothing but\n> > good things about it, feel free :)\n> Preliminary tests against the gcc repo are going swimmingly.\n> \n> Successful git svn rebase against trunk, doing a full git svn fetch\n> now to build rev_maps for all svn branches/tags.  At halfway through\n> space has decreased from ~2GB to 17MB for about half of the needed\n> metadata.\n> \n\nEric,\n\nI'm very happy with these patches.  For the gcc repo, git-svn metadata\nhas gone from over 5GB to 33MB.  git-svn fetch/rebase are working fine,\nwill shout if I see any odd behavior.\n\nHarvey\n"},{"id":"62939","messageId":"20071212180510.GB18980@untitled","threadId":"11206","inReplyTo":"1197248646.7185.25.camel@brick","subject":"Re: [PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-12-12T18:05:10Z","receivedAt":"2007-12-12T18:05:10Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Harvey Harrison <harvey.harrison@gmail.com> wrote:\n> On Sun, 2007-12-09 at 12:56 -0800, Harvey Harrison wrote:\n> > On Sat, 2007-12-08 at 23:27 -0800, Eric Wong wrote:\n> > > This is very lightly tested, but describes the format I described in:\n> > > \n> > >   http://article.gmane.org/gmane.comp.version-control.git/67126\n> > > \n> > >   (more in the commit messages)\n> > > \n> > > I'll be out of town the next few days and I'm not sure how much I'll be\n> > > able to follow up on it while I'm gone.  Please test, especially if\n> > > you're dealing with a repository where large .rev_db files are a\n> > > problem.\n> > > \n> > > Junio: not intended for master just yet, but if you hear nothing but\n> > > good things about it, feel free :)\n> > Preliminary tests against the gcc repo are going swimmingly.\n> > \n> > Successful git svn rebase against trunk, doing a full git svn fetch\n> > now to build rev_maps for all svn branches/tags.  At halfway through\n> > space has decreased from ~2GB to 17MB for about half of the needed\n> > metadata.\n> > \n> \n> Eric,\n> \n> I'm very happy with these patches.  For the gcc repo, git-svn metadata\n> has gone from over 5GB to 33MB.  git-svn fetch/rebase are working fine,\n> will shout if I see any odd behavior.\n\nHarvey:\n\nThanks for the feedback.  Glad it helps with gcc.  I'll make unlinking\nthe index files the default tonight since it shouldn't hurt performance\nenough to matter, and the disk savings is enough to justify it..\n\nSam (or anybody else using useSvmProps:\n\n  Do you have any feedback with svmProps enabled?\n\n-- \nEric Wong\n"},{"id":"62972","messageId":"47609BF9.4030005@vilain.net","threadId":"11206","inReplyTo":"20071212180510.GB18980@untitled","subject":"Re: [PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2007-12-13T02:42:01Z","receivedAt":"2007-12-13T02:42:01Z","isPatch":true,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"Eric Wong wrote:\n> Sam (or anybody else using useSvmProps:\n> \n>   Do you have any feedback with svmProps enabled?\n\n3682645  120 -rw-rw-r--   1 samv     samv       121893 Dec 13 14:05\ntags/debian_version_1_2_0beta1@2972/.rev_db.d29f7b36-84ff-0310-85ce-ba787dbd31ca\n\n =>\n\n-3683050    4 -rw-rw-r--   1 samv     samv           48 Dec 13 14:26\n./tags/debian_version_1_2_0beta1@2972/.rev_map.d29f7b36-84ff-0310-85ce-ba787dbd31ca\n\ngit-log --all | grep git-svn | tail -3\n\n    git-svn-id:\nfile:///home/samv/.svk/local/mirror/fai/branches/source-dist@2971\nd29f7b36-84ff-0310-85ce-ba787dbd31ca\n    git-svn-id: svn+ssh://svn.debian.org/svn/fai/trunk@2\nba5ec265-b0fb-0310-8e1a-cf9e4c2b1591\n    git-svn-id: svn+ssh://svn.debian.org/svn/fai/trunk@1\nba5ec265-b0fb-0310-8e1a-cf9e4c2b1591\n\nSo, the remapping is still working fine.\n\nThe one that failed there is not a new bug.  Here's the fix anyway\nthough :-)\n\nSubject: [PATCH] git-svn: fix --use-svm-props and --follow-parent\n\nIf, when using --follow-parent, it finds that the parent is an SVM\npath, *and* the SVM path is not at the root of the corresponding SVN\nrepository (the usual case when using SVK), then the logic in\nGit::SVN::ra will not find the correct source repository.  This is\nbecause the freshly created RA object is missing the path.  So, set\nthis after creation.\n\nSigned-off-by: Sam Vilain <sam.vilain@catalyst.net.nz>\n---\n git-svn.perl |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 54d7844..3e5fd82 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -2189,6 +2189,7 @@ sub find_parent_branch {\n \t\t$ref_id .= '-' while find_ref($ref_id);\n \t\tprint STDERR \"Initializing parent: $ref_id\\n\";\n \t\t$gs = Git::SVN->init($new_url, '', $ref_id, $ref_id, 1);\n+\t\t$gs->{path} = $branch_from;\n \t}\n \tmy ($r0, $parent) = $gs->find_rev_before($r, 1);\n \tif (!defined $r0 || !defined $parent) {\n-- \n1.5.3.5\n"},{"id":"63033","messageId":"20071213163003.GB18433@soma","threadId":"11206","inReplyTo":"47609BF9.4030005@vilain.net","subject":"Re: [PATCH 0/2] [RFT] git-svn: more efficient revision -> commit mapping","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-12-13T16:30:03Z","receivedAt":"2007-12-13T16:30:03Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Sam Vilain <sam@vilain.net> wrote:\n> Eric Wong wrote:\n> > Sam (or anybody else using useSvmProps:\n> > \n> >   Do you have any feedback with svmProps enabled?\n> \n> 3682645  120 -rw-rw-r--   1 samv     samv       121893 Dec 13 14:05\n> tags/debian_version_1_2_0beta1@2972/.rev_db.d29f7b36-84ff-0310-85ce-ba787dbd31ca\n> \n>  =>\n> \n> -3683050    4 -rw-rw-r--   1 samv     samv           48 Dec 13 14:26\n> ./tags/debian_version_1_2_0beta1@2972/.rev_map.d29f7b36-84ff-0310-85ce-ba787dbd31ca\n> \n> git-log --all | grep git-svn | tail -3\n> \n>     git-svn-id:\n> file:///home/samv/.svk/local/mirror/fai/branches/source-dist@2971\n> d29f7b36-84ff-0310-85ce-ba787dbd31ca\n>     git-svn-id: svn+ssh://svn.debian.org/svn/fai/trunk@2\n> ba5ec265-b0fb-0310-8e1a-cf9e4c2b1591\n>     git-svn-id: svn+ssh://svn.debian.org/svn/fai/trunk@1\n> ba5ec265-b0fb-0310-8e1a-cf9e4c2b1591\n> \n> So, the remapping is still working fine.\n\nHi Sam, nice.\n\n> The one that failed there is not a new bug.  Here's the fix anyway\n> though :-)\n> \n> Subject: [PATCH] git-svn: fix --use-svm-props and --follow-parent\n> \n> If, when using --follow-parent, it finds that the parent is an SVM\n> path, *and* the SVM path is not at the root of the corresponding SVN\n> repository (the usual case when using SVK), then the logic in\n> Git::SVN::ra will not find the correct source repository.  This is\n> because the freshly created RA object is missing the path.  So, set\n> this after creation.\n\nThis seems to break the non-useSvmProps case (in t9104).  I'll look more\nat it later.\n\n> Signed-off-by: Sam Vilain <sam.vilain@catalyst.net.nz>\n> ---\n>  git-svn.perl |    1 +\n>  1 files changed, 1 insertions(+), 0 deletions(-)\n> \n> diff --git a/git-svn.perl b/git-svn.perl\n> index 54d7844..3e5fd82 100755\n> --- a/git-svn.perl\n> +++ b/git-svn.perl\n> @@ -2189,6 +2189,7 @@ sub find_parent_branch {\n>  \t\t$ref_id .= '-' while find_ref($ref_id);\n>  \t\tprint STDERR \"Initializing parent: $ref_id\\n\";\n>  \t\t$gs = Git::SVN->init($new_url, '', $ref_id, $ref_id, 1);\n> +\t\t$gs->{path} = $branch_from;\n>  \t}\n>  \tmy ($r0, $parent) = $gs->find_rev_before($r, 1);\n>  \tif (!defined $r0 || !defined $parent) {\n\n-- \nEric Wong\n"}]}