git-upload-pack: Support the multi_ack protocol
[gitweb.git] / git-svnimport.perl
index 2f89c31e54eac82a4f0fa04c3681a2378dda82d1..20a85724cb1660a3411fa34717cd5f1e98ebd731 100755 (executable)
@@ -108,7 +108,7 @@ sub conn {
 sub file {
        my($self,$path,$rev) = @_;
 
-       my ($fh, $name) = tempfile('gitsvn.XXXXXX', 
+       my ($fh, $name) = tempfile('gitsvn.XXXXXX',
                    DIR => File::Spec->tmpdir(), UNLINK => 1);
 
        print "... $rev $path ...\n" if $opt_v;
@@ -163,9 +163,9 @@ ()
 
 sub get_headref($$) {
     my $name    = shift;
-    my $git_dir = shift; 
+    my $git_dir = shift;
     my $sha;
-    
+
     if (open(C,"$git_dir/refs/heads/$name")) {
        chomp($sha = <C>);
        close(C);
@@ -235,7 +235,7 @@ ($$)
 
        $forward_master =
            $opt_o ne 'master' && -f "$git_dir/refs/heads/master" &&
-           system('cmp', '-s', "$git_dir/refs/heads/master", 
+           system('cmp', '-s', "$git_dir/refs/heads/master",
                                "$git_dir/refs/heads/$opt_o") == 0;
 
        # populate index
@@ -265,11 +265,11 @@ ($$$)
        my $svnpath;
        $path = "" if $path eq "/"; # this should not happen, but ...
        if($branch eq "/") {
-               $svnpath = "/$trunk_name/$path";
+               $svnpath = "$trunk_name/$path";
        } elsif($branch =~ m#^/#) {
-               $svnpath = "/$tag_name$branch/$path";
+               $svnpath = "$tag_name$branch/$path";
        } else {
-               $svnpath = "/$branch_name/$branch/$path";
+               $svnpath = "$branch_name/$branch/$path";
        }
 
        # now get it
@@ -280,13 +280,13 @@ ($$$)
                # /svn/!svn/bc/2/django/trunk/django-docs/build.py
                my $url=$svn_url->clone();
                $url->path($url->path."/!svn/bc/$rev/$svn_dir$svnpath");
-               print "Fetching $url...\n" if $opt_v;
+               print "... $path...\n" if $opt_v;
                $req = HTTP::Request->new(GET => $url);
                $res = $lwp_ua->request($req);
                if ($res->is_success) {
                        my $fh;
-                       ($fh, $name) = tempfile('gitsvn.XXXXXX', 
-                       DIR => File::Spec->tmpdir(), UNLINK => 1);
+                       ($fh, $name) = tempfile('gitsvn.XXXXXX',
+                       DIR => File::Spec->tmpdir(), UNLINK => 1);
                        print $fh $res->content;
                        close($fh) or die "Could not write $name: $!\n";
                } else {
@@ -294,7 +294,7 @@ ($$$)
                        die $res->status_line." at $url\n";
                }
        } else {
-               $name = $svn->file($svnpath,$rev);
+               $name = $svn->file("/$svnpath",$rev);
                return undef unless defined $name;
        }
 
@@ -326,6 +326,36 @@ ($$)
        return ($branch,$path);
 }
 
+sub copy_subdir($$$$$$) {
+       # Somebody copied a whole subdirectory.
+       # We need to find the index entries from the old version which the
+       # SVN log entry points to, and add them to the new place.
+
+       my($newrev,$newbranch,$path,$oldpath,$rev,$new) = @_;
+       my($branch,$srcpath) = split_path($rev,$oldpath);
+
+       my $gitrev = $branches{$branch}{$rev};
+       unless($gitrev) {
+               print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n";
+               return;
+       }
+       print "$newrev:$newbranch:$path: copying from $branch:$srcpath @ $rev\n" if $opt_v;
+       $srcpath =~ s#/*$#/#;
+       open my $f,"-|","git-ls-tree","-r","-z",$gitrev,$srcpath;
+       local $/ = "\0";
+       while(<$f>) {
+               chomp;
+               my($m,$p) = split(/\t/,$_,2);
+               my($mode,$type,$sha1) = split(/ /,$m);
+               next if $type ne "blob";
+               $p = substr($p,length($srcpath)-1);
+               print "... found $path$p ...\n" if $opt_v;
+               push(@$new,[$mode,$sha1,$path.$p]);
+       }
+       close($f) or
+               print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n";
+}
+
 sub commit {
        my($branch, $changed_paths, $revision, $author, $date, $message) = @_;
        my($author_name,$author_email,$dest);
@@ -420,10 +450,20 @@ sub commit {
        if($tag and not %$changed_paths) {
                $cid = $rev;
        } else {
-               while(my($path,$action) = each %$changed_paths) {
+               my @paths = sort keys %$changed_paths;
+               foreach my $path(@paths) {
+                       my $action = $changed_paths->{$path};
+
                        if ($action->[0] eq "A") {
                                my $f = get_file($revision,$branch,$path);
-                               push(@new,$f) if $f;
+                               if($f) {
+                                       push(@new,$f) if $f;
+                               } elsif($action->[1]) {
+                                       copy_subdir($revision,$branch,$path,$action->[1],$action->[2],\@new);
+                               } else {
+                                       my $opath = $action->[3];
+                                       print STDERR "$revision: $branch: could not fetch '$opath'\n";
+                               }
                        } elsif ($action->[0] eq "D") {
                                push(@old,$path);
                        } elsif ($action->[0] eq "M") {
@@ -434,16 +474,19 @@ sub commit {
                                push(@old,$path); # remove any old stuff
 
                                # ... and add any new stuff
-                               my($b,$p) = split_path($revision,$action->[1]);
-                               open my $F,"-|","git-ls-tree","-r","-z", $branches{$b}{$action->[2]}, $p;
-                               local $/ = '\0';
+                               my($b,$srcpath) = split_path($revision,$action->[1]);
+                               $srcpath =~ s#/*$#/#;
+                               open my $F,"-|","git-ls-tree","-r","-z", $branches{$b}{$action->[2]}, $srcpath;
+                               local $/ = "\0";
                                while(<$F>) {
                                        chomp;
                                        my($m,$p) = split(/\t/,$_,2);
                                        my($mode,$type,$sha1) = split(/ /,$m);
                                        next if $type ne "blob";
-                                       push(@new,[$mode,$sha1,$p]);
+                                       $p = substr($p,length($srcpath)-1);
+                                       push(@new,[$mode,$sha1,$path.$p]);
                                }
+                               close($F);
                        } else {
                                die "$revision: unknown action '".$action->[0]."' for $path\n";
                        }
@@ -452,7 +495,7 @@ sub commit {
                if(@old) {
                        open my $F, "-|", "git-ls-files", "-z", @old or die $!;
                        @old = ();
-                       local $/ = '\0';
+                       local $/ = "\0";
                        while(<$F>) {
                                chomp;
                                push(@old,$_);
@@ -524,7 +567,7 @@ sub commit {
                                                push @par, '-p', $mparent;
                                                print OUT "Merge parent branch: $mparent\n" if $opt_v;
                                        }
-                               } 
+                               }
                        }
 
                        exec("env",
@@ -561,7 +604,7 @@ sub commit {
                print "... no known parent\n" if $opt_v;
        } elsif(not $tag) {
                print "Writing to refs/heads/$dest\n" if $opt_v;
-               open(C,">$git_dir/refs/heads/$dest") and 
+               open(C,">$git_dir/refs/heads/$dest") and
                print C ("$cid\n") and
                close(C)
                        or die "Cannot write branch $dest for update: $!\n";
@@ -571,7 +614,7 @@ sub commit {
                my($in, $out) = ('','');
                $last_rev = "-" if %$changed_paths;
                # the tag was 'complex', i.e. did not refer to a "real" revision
-               
+
                $dest =~ tr/_/\./ if $opt_u;
 
                my $pid = open2($in, $out, 'git-mktag');
@@ -609,7 +652,7 @@ sub _commit_all {
        ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
        my %p;
        while(my($path,$action) = each %$changed_paths) {
-               $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev ];
+               $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ];
        }
        $changed_paths = \%p;
 }
@@ -634,7 +677,8 @@ sub commit_all {
        $svn->{'svn'}->get_log("/",$current_rev,$current_rev,$current_rev,1,1,\&_commit_all,"");
        commit_all();
        if($opt_l and not --$opt_l) {
-               print STDERR "Exiting due to a memory leak. Repeat, please.\n";
+               print STDERR "Stopping, because there is a memory leak (in the SVN library).\n";
+               print STDERR "Please repeat this command; it will continue safely\n";
                last;
        }
 }