{"thread":{"id":"2280","subject":"[PATCH] Several fixes to import mono's svn tree","startedAt":"2005-10-31T10:09:26Z","lastAt":"2005-10-31T10:09:26Z","messageCount":1,"participants":["Yaacov Akiba Slama"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"10848","messageId":"4365ED56.3040602@slamail.org","threadId":"2280","inReplyTo":null,"subject":"[PATCH] Several fixes to import mono's svn tree","fromName":"Yaacov Akiba Slama","fromEmail":"ya@slamail.org","sentAt":"2005-10-31T10:09:26Z","receivedAt":"2005-10-31T10:09:26Z","isPatch":true,"sender":{"key":"ya@slamail.org","avatar":null},"body":"I successfully imported the mono svn tree (in fact a local mirror of it) \nwhich has more that 50000 revisions with the included git-svnimport.\n\nThanks,\n\n--yas\n\n\n(The mono tree can be found svn://svn.myrealbox.com/source\nand a web interface at http://svn.myrealbox.com/viewcvs/)\n\n1) Fix the memory leak (using svn pools).\n2) Don't output errors if the following paths appear in the logs :\n   /, /branches, /tags, taking into account the opt_b and opt_t.\n3) Unify the handling of \"A\" (add) and \"R\" (replace) actions.\n4) Handle better the difference between directory and file using check_path\n5) When a branch is using files from several branches, add them as parents of\n   the commit.\n6) Handle the case when the only operation in a revision is to add a tag\n   - that is - to create a directory called /tags/thetag.\n7) Other small fixes.\n\nSigned-off-by: Yaacov Akiba Slama <ya@slamail.org>\n\n---\n\n git-svnimport.perl |  147 ++++++++++++++++++++++++++++++++++++----------------\n 1 files changed, 101 insertions(+), 46 deletions(-)\n\napplies-to: 8c8ace55eb085e930dcc9482f7cf66f9399374b4\n850208bc0981001fb62a9af1dfa14b2fb2aed05c\ndiff --git a/git-svnimport.perl b/git-svnimport.perl\nindex 45b6a19..e97f470 100755\n--- a/git-svnimport.perl\n+++ b/git-svnimport.perl\n@@ -112,7 +112,9 @@ sub file {\n \t\t    DIR => File::Spec->tmpdir(), UNLINK => 1);\n \n \tprint \"... $rev $path ...\\n\" if $opt_v;\n-\teval { $self->{'svn'}->get_file($path,$rev,$fh); };\n+\tmy $pool = SVN::Pool->new();\n+\teval { $self->{'svn'}->get_file($path,$rev,$fh,$pool); };\n+\t$pool->clear;\n \tif($@) {\n \t\treturn undef if $@ =~ /Attempted to get checksum/;\n \t\tdie $@;\n@@ -258,10 +260,17 @@ EOM\n \n open BRANCHES,\">>\", \"$git_dir/svn2git\";\n \n-sub get_file($$$) {\n-\tmy($rev,$branch,$path) = @_;\n+sub node_kind($$$) {\n+\tmy ($branch, $path, $revision) = @_;\n+\tmy $pool=SVN::Pool->new;\n+\tmy $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);\n+\t$pool->clear;\n+\treturn $kind;\n+}\n+\n+sub revert_split_path($$) {\n+\tmy($branch,$path) = @_;\n \n-\t# revert split_path(), below\n \tmy $svnpath;\n \t$path = \"\" if $path eq \"/\"; # this should not happen, but ...\n \tif($branch eq \"/\") {\n@@ -272,6 +281,14 @@ sub get_file($$$) {\n \t\t$svnpath = \"$branch_name/$branch/$path\";\n \t}\n \n+\treturn $svnpath\n+}\n+\n+sub get_file($$$) {\n+\tmy($rev,$branch,$path) = @_;\n+\n+\tmy $svnpath = revert_split_path($branch,$path);\n+\n \t# now get it\n \tmy $name;\n \tif($opt_d) {\n@@ -319,28 +336,57 @@ sub split_path($$) {\n \t} elsif($path =~ s#^/\\Q$branch_name\\E/([^/]+)/?##) {\n \t\t$branch = $1;\n \t} else {\n-\t\tprint STDERR \"$rev: Unrecognized path: $path\\n\";\n+\t\tmy %no_error = (\n+\t\t\t\"/\" => 1,\n+\t\t\t\"/$tag_name\" => 1,\n+\t\t\t\"/$branch_name\" => 1\n+\t\t);\n+\t\tprint STDERR \"$rev: Unrecognized path: $path\\n\" unless (defined $no_error{$path});\n \t\treturn ()\n \t}\n \t$path = \"/\" if $path eq \"\";\n \treturn ($branch,$path);\n }\n \n-sub copy_subdir($$$$$$) {\n+sub branch_rev($$) {\n+\n+\tmy ($srcbranch,$uptorev) = @_;\n+\n+\tmy $bbranches = $branches{$srcbranch};\n+\tmy @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches;\n+\tmy $therev;\n+\tforeach my $arev(@revs) {\n+\t\tnext if  ($arev eq 'LAST');\n+\t\tif ($arev <= $uptorev) {\n+\t\t\t$therev = $arev;\n+\t\t\tlast;\n+\t\t}\n+\t}\n+\treturn $therev;\n+}\n+\n+sub copy_path($$$$$$$$) {\n \t# Somebody copied a whole subdirectory.\n \t# We need to find the index entries from the old version which the\n \t# SVN log entry points to, and add them to the new place.\n \n-\tmy($newrev,$newbranch,$path,$oldpath,$rev,$new) = @_;\n-\tmy($branch,$srcpath) = split_path($rev,$oldpath);\n+\tmy($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;\n \n-\tmy $gitrev = $branches{$branch}{$rev};\n+\tmy($srcbranch,$srcpath) = split_path($rev,$oldpath);\n+\tmy $therev = branch_rev($srcbranch, $rev);\n+\tmy $gitrev = $branches{$srcbranch}{$therev};\n \tunless($gitrev) {\n \t\tprint STDERR \"$newrev:$newbranch: could not find $oldpath \\@ $rev\\n\";\n \t\treturn;\n \t}\n-\tprint \"$newrev:$newbranch:$path: copying from $branch:$srcpath @ $rev\\n\" if $opt_v;\n-\t$srcpath =~ s#/*$#/#;\n+\tif ($srcbranch ne $newbranch) {\n+\t\tpush(@$parents, $branches{$srcbranch}{'LAST'});\n+\t}\n+\tprint \"$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\\n\" if $opt_v;\n+\tif ($node_kind eq $SVN::Node::dir) {\n+\t\t\t$srcpath =~ s#/*$#/#;\n+\t}\n+\t\n \topen my $f,\"-|\",\"git-ls-tree\",\"-r\",\"-z\",$gitrev,$srcpath;\n \tlocal $/ = \"\\0\";\n \twhile(<$f>) {\n@@ -348,9 +394,12 @@ sub copy_subdir($$$$$$) {\n \t\tmy($m,$p) = split(/\\t/,$_,2);\n \t\tmy($mode,$type,$sha1) = split(/ /,$m);\n \t\tnext if $type ne \"blob\";\n-\t\t$p = substr($p,length($srcpath)-1);\n-\t\tprint \"... found $path$p ...\\n\" if $opt_v;\n-\t\tpush(@$new,[$mode,$sha1,$path.$p]);\n+\t\tif ($node_kind eq $SVN::Node::dir) {\n+\t\t\t$p = $path . substr($p,length($srcpath)-1);\n+\t\t} else {\n+\t\t\t$p = $path;\n+\t\t}\n+\t\tpush(@$new,[$mode,$sha1,$p]);\t\n \t}\n \tclose($f) or\n \t\tprint STDERR \"$newrev:$newbranch: could not list files in $oldpath \\@ $rev\\n\";\n@@ -359,7 +408,7 @@ sub copy_subdir($$$$$$) {\n sub commit {\n \tmy($branch, $changed_paths, $revision, $author, $date, $message) = @_;\n \tmy($author_name,$author_email,$dest);\n-\tmy(@old,@new);\n+\tmy(@old,@new,@parents);\n \n \tif (not defined $author) {\n \t\t$author_name = $author_email = \"unknown\";\n@@ -446,6 +495,8 @@ sub commit {\n \t\t$last_rev = $rev;\n \t}\n \n+\tpush (@parents, $rev) if defined $rev;\n+\n \tmy $cid;\n \tif($tag and not %$changed_paths) {\n \t\t$cid = $rev;\n@@ -454,39 +505,31 @@ sub commit {\n \t\tforeach my $path(@paths) {\n \t\t\tmy $action = $changed_paths->{$path};\n \n-\t\t\tif ($action->[0] eq \"A\") {\n-\t\t\t\tmy $f = get_file($revision,$branch,$path);\n-\t\t\t\tif($f) {\n-\t\t\t\t\tpush(@new,$f) if $f;\n-\t\t\t\t} elsif($action->[1]) {\n-\t\t\t\t\tcopy_subdir($revision,$branch,$path,$action->[1],$action->[2],\\@new);\n-\t\t\t\t} else {\n-\t\t\t\t\tmy $opath = $action->[3];\n-\t\t\t\t\tprint STDERR \"$revision: $branch: could not fetch '$opath'\\n\";\n+\t\t\tif ($action->[0] eq \"R\") {\n+\t\t\t\t# refer to a file/tree in an earlier commit\n+\t\t\t\tpush(@old,$path); # remove any old stuff\n+\t\t\t}\n+\t\t\tif(($action->[0] eq \"A\") || ($action->[0] eq \"R\")) {\n+\t\t\t\tmy $node_kind = node_kind($branch,$path,$revision);\n+\t\t\t\tif($action->[1]) {\n+\t\t\t\t\tcopy_path($revision,$branch,$path,$action->[1],$action->[2],$node_kind,\\@new,\\@parents);\n+\t\t\t\t} elsif ($node_kind eq $SVN::Node::file) {\n+\t\t\t\t\tmy $f = get_file($revision,$branch,$path);\n+\t\t\t\t\tif ($f) {\n+\t\t\t\t\t\tpush(@new,$f) if $f;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tmy $opath = $action->[3];\n+\t\t\t\t\t\tprint STDERR \"$revision: $branch: could not fetch '$opath'\\n\";\n+\t\t\t\t\t}\n \t\t\t\t}\n \t\t\t} elsif ($action->[0] eq \"D\") {\n \t\t\t\tpush(@old,$path);\n \t\t\t} elsif ($action->[0] eq \"M\") {\n-\t\t\t\tmy $f = get_file($revision,$branch,$path);\n-\t\t\t\tpush(@new,$f) if $f;\n-\t\t\t} elsif ($action->[0] eq \"R\") {\n-\t\t\t\t# refer to a file/tree in an earlier commit\n-\t\t\t\tpush(@old,$path); # remove any old stuff\n-\n-\t\t\t\t# ... and add any new stuff\n-\t\t\t\tmy($b,$srcpath) = split_path($revision,$action->[1]);\n-\t\t\t\t$srcpath =~ s#/*$#/#;\n-\t\t\t\topen my $F,\"-|\",\"git-ls-tree\",\"-r\",\"-z\", $branches{$b}{$action->[2]}, $srcpath;\n-\t\t\t\tlocal $/ = \"\\0\";\n-\t\t\t\twhile(<$F>) {\n-\t\t\t\t\tchomp;\n-\t\t\t\t\tmy($m,$p) = split(/\\t/,$_,2);\n-\t\t\t\t\tmy($mode,$type,$sha1) = split(/ /,$m);\n-\t\t\t\t\tnext if $type ne \"blob\";\n-\t\t\t\t\t$p = substr($p,length($srcpath)-1);\n-\t\t\t\t\tpush(@new,[$mode,$sha1,$path.$p]);\n+\t\t\t\tmy $node_kind = node_kind($branch,$path,$revision);\n+\t\t\t\tif ($node_kind eq $SVN::Node::file) {\n+\t\t\t\t\tmy $f = get_file($revision,$branch,$path);\n+\t\t\t\t\tpush(@new,$f) if $f;\n \t\t\t\t}\n-\t\t\t\tclose($F);\n \t\t\t} else {\n \t\t\t\tdie \"$revision: unknown action '\".$action->[0].\"' for $path\\n\";\n \t\t\t}\n@@ -554,7 +597,6 @@ sub commit {\n \t\t\t$pw->close();\n \n \t\t\tmy @par = ();\n-\t\t\t@par = (\"-p\",$rev) if defined $rev;\n \n \t\t\t# loose detection of merges\n \t\t\t# based on the commit msg\n@@ -564,11 +606,17 @@ sub commit {\n \t\t\t\t\tif ($mparent eq 'HEAD') { $mparent = $opt_o };\n \t\t\t\t\tif ( -e \"$git_dir/refs/heads/$mparent\") {\n \t\t\t\t\t\t$mparent = get_headref($mparent, $git_dir);\n-\t\t\t\t\t\tpush @par, '-p', $mparent;\n+\t\t\t\t\t\tpush (@parents, $mparent);\n \t\t\t\t\t\tprint OUT \"Merge parent branch: $mparent\\n\" if $opt_v;\n \t\t\t\t\t}\n \t\t\t\t}\n \t\t\t}\n+\t\t\tmy %seen_parents = ();\n+\t\t\tmy @unique_parents = grep { ! $seen_parents{$_} ++ } @parents;\n+\t\t\tforeach my $bparent (@unique_parents) {\n+\t\t\t\tpush @par, '-p', $bparent;\n+\t\t\t\tprint OUT \"Merge parent branch: $bparent\\n\" if $opt_v;\n+\t\t\t}\n \n \t\t\texec(\"env\",\n \t\t\t\t\"GIT_AUTHOR_NAME=$author_name\",\n@@ -600,6 +648,10 @@ sub commit {\n \t\tdie \"Error running git-commit-tree: $?\\n\" if $?;\n \t}\n \n+\tif (not defined $cid) {\n+\t\t$cid = $branches{\"/\"}{\"LAST\"};\n+\t}\n+\n \tif(not defined $dest) {\n \t\tprint \"... no known parent\\n\" if $opt_v;\n \t} elsif(not $tag) {\n@@ -616,6 +668,7 @@ sub commit {\n \t\t# the tag was 'complex', i.e. did not refer to a \"real\" revision\n \n \t\t$dest =~ tr/_/\\./ if $opt_u;\n+\t\t$branch = $dest;\n \n \t\tmy $pid = open2($in, $out, 'git-mktag');\n \t\tprint $out (\"object $cid\\n\".\n@@ -674,7 +727,9 @@ sub commit_all {\n }\n \n while(++$current_rev <= $svn->{'maxrev'}) {\n-\t$svn->{'svn'}->get_log(\"/\",$current_rev,$current_rev,$current_rev,1,1,\\&_commit_all,\"\");\n+\tmy $pool=SVN::Pool->new;\n+\t$svn->{'svn'}->get_log(\"/\",$current_rev,$current_rev,1,1,1,\\&_commit_all,$pool);\n+\t$pool->clear;\n \tcommit_all();\n \tif($opt_l and not --$opt_l) {\n \t\tprint STDERR \"Stopping, because there is a memory leak (in the SVN library).\\n\";\n---\n@@GIT_VERSION@@\n"}]}