git-svn: change ; to && in addremove()
[git.git] / contrib / git-svn / git-svn
index 62fc14f..25c248d 100755 (executable)
@@ -21,7 +21,7 @@ $ENV{LC_ALL} = 'C';
 
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
-# See what I do with XML::Simple to make the dependency optional.
+# use eval { require SVN::... } to make it lazy load
 use Carp qw/croak/;
 use IO::File qw//;
 use File::Basename qw/dirname basename/;
@@ -30,7 +30,8 @@ use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
 use File::Spec qw//;
 my $sha1 = qr/[a-f\d]{40}/;
 my $sha1_short = qr/[a-f\d]{6,40}/;
-my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit);
+my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
+       $_find_copies_harder, $_l);
 
 GetOptions(    'revision|r=s' => \$_revision,
                'no-ignore-externals' => \$_no_ignore_ext,
@@ -38,7 +39,9 @@ GetOptions(   'revision|r=s' => \$_revision,
                'edit|e' => \$_edit,
                'rmdir' => \$_rmdir,
                'help|H|h' => \$_help,
-               'no-stop-copy' => \$_no_stop_copy );
+               'find-copies-harder' => \$_find_copies_harder,
+               'l=i' => \$_l,
+               'no-stop-on-copy' => \$_no_stop_copy );
 my %cmd = (
        fetch => [ \&fetch, "Download new revisions from SVN" ],
        init => [ \&init, "Initialize and fetch (import)"],
@@ -168,14 +171,14 @@ sub fetch {
        my (@parents) = @_;
        $SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
        my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL);
-       if (-d $SVN_WC && !$_revision) {
-               $_revision = 'BASE:HEAD';
+       unless ($_revision) {
+               $_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD';
        }
-       push @log_args, "-r$_revision" if $_revision;
+       push @log_args, "-r$_revision";
        push @log_args, '--stop-on-copy' unless $_no_stop_copy;
 
-       eval { require XML::Simple or croak $! };
-       my $svn_log = $@ ? svn_log_raw(@log_args) : svn_log_xml(@log_args);
+       my $svn_log = svn_log_raw(@log_args);
+       @$svn_log = sort { $a->{revision} <=> $b->{revision} } @$svn_log;
 
        my $base = shift @$svn_log or croak "No base revision!\n";
        my $last_commit = undef;
@@ -212,14 +215,21 @@ sub commit {
                print "Reading from stdin...\n";
                @commits = ();
                while (<STDIN>) {
-                       if (/^([a-f\d]{6,40})\b/) {
+                       if (/\b([a-f\d]{6,40})\b/) {
                                unshift @commits, $1;
                        }
                }
        }
        my @revs;
-       foreach (@commits) {
-               push @revs, (safe_qx('git-rev-parse',$_));
+       foreach my $c (@commits) {
+               chomp(my @tmp = safe_qx('git-rev-parse',$c));
+               if (scalar @tmp == 1) {
+                       push @revs, $tmp[0];
+               } elsif (scalar @tmp > 1) {
+                       push @revs, reverse (safe_qx('git-rev-list',@tmp));
+               } else {
+                       die "Failed to rev-parse $c\n";
+               }
        }
        chomp @revs;
 
@@ -347,7 +357,10 @@ sub svn_checkout_tree {
        my $pid = open my $diff_fh, '-|';
        defined $pid or croak $!;
        if ($pid == 0) {
-               exec(qw(git-diff-tree -z -r -C), $from, $commit) or croak $!;
+               my @diff_tree = qw(git-diff-tree -z -r -C);
+               push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
+               push @diff_tree, "-l$_l" if defined $_l;
+               exec(@diff_tree, $from, $commit) or croak $!;
        }
        my $mods = parse_diff_tree($diff_fh);
        unless (@$mods) {
@@ -462,49 +475,6 @@ sub svn_commit_tree {
        return fetch("$rev_committed=$commit")->{revision};
 }
 
-sub svn_log_xml {
-       my (@log_args) = @_;
-       my $log_fh = IO::File->new_tmpfile or croak $!;
-
-       my $pid = fork;
-       defined $pid or croak $!;
-
-       if ($pid == 0) {
-               open STDOUT, '>&', $log_fh or croak $!;
-               exec (qw(svn log --xml), @log_args) or croak $!
-       }
-
-       waitpid $pid, 0;
-       croak $? if $?;
-
-       seek $log_fh, 0, 0;
-       my @svn_log;
-       my $log = XML::Simple::XMLin( $log_fh,
-                               ForceArray => ['path','revision','logentry'],
-                               KeepRoot => 0,
-                               KeyAttr => {    logentry => '+revision',
-                                               paths => '+path' },
-                       )->{logentry};
-       foreach my $r (sort {$a <=> $b} keys %$log) {
-               my $log_msg = $log->{$r};
-               my ($Y,$m,$d,$H,$M,$S) = ($log_msg->{date} =~
-                                       /(\d{4})\-(\d\d)\-(\d\d)T
-                                        (\d\d)\:(\d\d)\:(\d\d)\.\d+Z$/x)
-                                        or croak "Failed to parse date: ",
-                                                $log->{$r}->{date};
-               $log_msg->{date} = "+0000 $Y-$m-$d $H:$M:$S";
-
-               # XML::Simple can't handle <msg></msg> as a string:
-               if (ref $log_msg->{msg} eq 'HASH') {
-                       $log_msg->{msg} = "\n";
-               } else {
-                       $log_msg->{msg} .= "\n";
-               }
-               push @svn_log, $log->{$r};
-       }
-       return \@svn_log;
-}
-
 sub svn_log_raw {
        my (@log_args) = @_;
        my $pid = open my $log_fh,'-|';
@@ -515,21 +485,42 @@ sub svn_log_raw {
        }
 
        my @svn_log;
-       my $state;
+       my $state = 'sep';
        while (<$log_fh>) {
                chomp;
                if (/^\-{72}$/) {
+                       if ($state eq 'msg') {
+                               if ($svn_log[$#svn_log]->{lines}) {
+                                       $svn_log[$#svn_log]->{msg} .= $_."\n";
+                                       unless(--$svn_log[$#svn_log]->{lines}) {
+                                               $state = 'sep';
+                                       }
+                               } else {
+                                       croak "Log parse error at: $_\n",
+                                               $svn_log[$#svn_log]->{revision},
+                                               "\n";
+                               }
+                               next;
+                       }
+                       if ($state ne 'sep') {
+                               croak "Log parse error at: $_\n",
+                                       "state: $state\n",
+                                       $svn_log[$#svn_log]->{revision},
+                                       "\n";
+                       }
                        $state = 'rev';
 
                        # if we have an empty log message, put something there:
                        if (@svn_log) {
-                               $svn_log[0]->{msg} ||= "\n";
+                               $svn_log[$#svn_log]->{msg} ||= "\n";
+                               delete $svn_log[$#svn_log]->{lines};
                        }
                        next;
                }
                if ($state eq 'rev' && s/^r(\d+)\s*\|\s*//) {
                        my $rev = $1;
-                       my ($author, $date) = split(/\s*\|\s*/, $_, 2);
+                       my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3);
+                       ($lines) = ($lines =~ /(\d+)/);
                        my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
                                        /(\d{4})\-(\d\d)\-(\d\d)\s
                                         (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
@@ -537,8 +528,9 @@ sub svn_log_raw {
                        my %log_msg = ( revision => $rev,
                                        date => "$tz $Y-$m-$d $H:$M:$S",
                                        author => $author,
+                                       lines => $lines,
                                        msg => '' );
-                       unshift @svn_log, \%log_msg;
+                       push @svn_log, \%log_msg;
                        $state = 'msg_start';
                        next;
                }
@@ -546,7 +538,15 @@ sub svn_log_raw {
                if ($state eq 'msg_start' && /^$/) {
                        $state = 'msg';
                } elsif ($state eq 'msg') {
-                       $svn_log[0]->{msg} .= $_."\n";
+                       if ($svn_log[$#svn_log]->{lines}) {
+                               $svn_log[$#svn_log]->{msg} .= $_."\n";
+                               unless (--$svn_log[$#svn_log]->{lines}) {
+                                       $state = 'sep';
+                               }
+                       } else {
+                               croak "Log parse error at: $_\n",
+                                       $svn_log[$#svn_log]->{revision},"\n";
+                       }
                }
        }
        close $log_fh or croak $?;
@@ -579,13 +579,12 @@ sub svn_info {
 sub sys { system(@_) == 0 or croak $? }
 
 sub git_addremove {
-       system( "git-ls-files -z --others ".
+       system( "git-diff-files --name-only -z ".
+                               " | git-update-index --remove -z --stdin && ".
+               "git-ls-files -z --others ".
                        "'--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude'".
-                               "| git-update-index --add -z --stdin; ".
-               "git-ls-files -z --deleted ".
-                               "| git-update-index --remove -z --stdin; ".
-               "git-ls-files -z --modified".
-                               "| git-update-index -z --stdin") == 0 or croak $?
+                               " | git-update-index --add -z --stdin"
+               ) == 0 or croak $?
 }
 
 sub s_to_file {