index 1a8f40eddf46906b202b697ac7ff45e7e6d4e661..25c248dee3795fcbd7f6c48dacc54c25a7fcf0cf 100755 (executable)
--- a/contrib/git-svn/git-svn
+++ b/contrib/git-svn/git-svn
# 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/;
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,
'edit|e' => \$_edit,
'rmdir' => \$_rmdir,
'help|H|h' => \$_help,
+ '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" ],
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";
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;
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) {
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,'-|';
}
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[$#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)
my %log_msg = ( revision => $rev,
date => "$tz $Y-$m-$d $H:$M:$S",
author => $author,
+ lines => $lines,
msg => '' );
push @svn_log, \%log_msg;
$state = 'msg_start';
if ($state eq 'msg_start' && /^$/) {
$state = 'msg';
} elsif ($state eq 'msg') {
- $svn_log[$#svn_log]->{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 $?;
sub git_addremove {
system( "git-diff-files --name-only -z ".
- " | git-update-index --remove -z --stdin; ".
+ " | 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-update-index --add -z --stdin"
) == 0 or croak $?
}