my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
+my $_esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity, $_cp_remote,
$_repack, $_repack_nr, $_repack_flags, $_q,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
- $_username, $_config_dir, $_no_auth_cache, $_xfer_delta);
+ $_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
+ $_pager, $_color);
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
'no-graft-copy' => \$_no_graft_copy } ],
'multi-init' => [ \&multi_init,
'Initialize multiple trees (like git-svnimport)',
- { %multi_opts, %fc_opts } ],
+ { %multi_opts, %init_opts,
+ 'revision|r=i' => \$_revision,
+ 'username=s' => \$_username,
+ 'config-dir=s' => \$_config_dir,
+ 'no-auth-cache' => \$_no_auth_cache,
+ } ],
'multi-fetch' => [ \&multi_fetch,
'Fetch multiple trees (like git-svnimport)',
\%fc_opts ],
'show-commit' => \$_show_commit,
'non-recursive' => \$_non_recursive,
'authors-file|A=s' => \$_authors,
+ 'color' => \$_color,
+ 'pager=s' => \$_pager,
} ],
'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
{ 'message|m=s' => \$_message,
$min = $max + 1;
$max += $inc;
$max = $head if ($max > $head);
+ $SVN = libsvn_connect($SVN_URL);
}
restore_index($index);
return { revision => $last_rev, commit => $last_commit };
}
}
+ config_pager();
my $pid = open(my $log,'-|');
defined $pid or croak $!;
if (!$pid) {
exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!;
}
- setup_pager();
+ run_pager();
my (@k, $c, $d);
while (<$log>) {
- if (/^commit ($sha1_short)/o) {
+ if (/^${_esc_color}commit ($sha1_short)/o) {
my $cmt = $1;
if ($c && cmt_showable($c) && $c->{r} != $r_last) {
$r_last = $c->{r};
}
$d = undef;
$c = { c => $cmt };
- } elsif (/^author (.+) (\d+) ([\-\+]?\d+)$/) {
+ } elsif (/^${_esc_color}author (.+) (\d+) ([\-\+]?\d+)$/) {
get_author_info($c, $1, $2, $3);
- } elsif (/^(?:tree|parent|committer) /) {
+ } elsif (/^${_esc_color}(?:tree|parent|committer) /) {
# ignore
- } elsif (/^:\d{6} \d{6} $sha1_short/o) {
+ } elsif (/^${_esc_color}:\d{6} \d{6} $sha1_short/o) {
push @{$c->{raw}}, $_;
- } elsif (/^[ACRMDT]\t/) {
+ } elsif (/^${_esc_color}[ACRMDT]\t/) {
# we could add $SVN->{svn_path} here, but that requires
# remote access at the moment (repo_path_split)...
- s#^([ACRMDT])\t# $1 #;
+ s#^(${_esc_color})([ACRMDT])\t#$1 $2 #;
push @{$c->{changed}}, $_;
- } elsif (/^diff /) {
+ } elsif (/^${_esc_color}diff /) {
$d = 1;
push @{$c->{diff}}, $_;
} elsif ($d) {
push @{$c->{diff}}, $_;
- } elsif (/^ (git-svn-id:.+)$/) {
+ } elsif (/^${_esc_color} (git-svn-id:.+)$/) {
($c->{url}, $c->{r}, undef) = extract_metadata($1);
- } elsif (s/^ //) {
+ } elsif (s/^${_esc_color} //) {
push @{$c->{l}}, $_;
}
}
return defined $c->{r};
}
+sub log_use_color {
+ return 1 if $_color;
+ my $dc;
+ chomp($dc = `git-repo-config --get diff.color`);
+ if ($dc eq 'auto') {
+ if (-t *STDOUT || (defined $_pager &&
+ `git-repo-config --bool --get pager.color` !~ /^false/)) {
+ return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
+ }
+ return 0;
+ }
+ return 0 if $dc eq 'never';
+ return 1 if $dc eq 'always';
+ chomp($dc = `git-repo-config --bool --get diff.color`);
+ $dc eq 'true';
+}
+
sub git_svn_log_cmd {
my ($r_min, $r_max) = @_;
my @cmd = (qw/git-log --abbrev-commit --pretty=raw
--default/, "refs/remotes/$GIT_SVN");
push @cmd, '-r' unless $_non_recursive;
push @cmd, qw/--raw --name-status/ if $_verbose;
+ push @cmd, '--color' if log_use_color();
return @cmd unless defined $r_max;
if ($r_max == $r_min) {
push @cmd, '--max-count=1';
while (1) {
my $pool = SVN::Pool->new;
libsvn_get_log(libsvn_dup_ra($SVN), [$path],
- $min, $max, 0, 1, 1,
+ $min, $max, 0, 2, 1,
sub {
libsvn_graft_file_copies($grafts, $tree_paths,
$path, @_);
return ($1 * 60) + ($tz * 3600);
}
-sub setup_pager { # translated to Perl from pager.c
- return unless (-t *STDOUT);
- my $pager = $ENV{PAGER};
- if (!defined $pager) {
- $pager = 'less';
- } elsif (length $pager == 0 || $pager eq 'cat') {
- return;
+# adapted from pager.c
+sub config_pager {
+ $_pager ||= $ENV{GIT_PAGER} || $ENV{PAGER};
+ if (!defined $_pager) {
+ $_pager = 'less';
+ } elsif (length $_pager == 0 || $_pager eq 'cat') {
+ $_pager = undef;
}
+}
+
+sub run_pager {
+ return unless -t *STDOUT;
pipe my $rfd, my $wfd or return;
defined(my $pid = fork) or croak $!;
if (!$pid) {
return;
}
open STDIN, '<&', $rfd or croak $!;
- $ENV{LESS} ||= '-S';
- exec $pager or croak "Can't run pager: $!\n";;
+ $ENV{LESS} ||= 'FRSX';
+ exec $_pager or croak "Can't run pager: $! ($_pager)\n";
}
sub get_author_info {
}
sub process_rm {
- my ($gui, $last_commit, $f) = @_;
+ my ($gui, $last_commit, $f, $q) = @_;
# remove entire directories.
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
defined(my $pid = open my $ls, '-|') or croak $!;
local $/ = "\0";
while (<$ls>) {
print $gui '0 ',0 x 40,"\t",$_ or croak $!;
+ print "\tD\t$_\n" unless $q;
}
+ print "\tD\t$f/\n" unless $q;
close $ls or croak $?;
} else {
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
+ print "\tD\t$f\n" unless $q;
}
}
sub libsvn_fetch_delta {
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
my $pool = SVN::Pool->new;
- my $ed = SVN::Git::Fetcher->new({ c => $last_commit, ra => $SVN,
- paths => $paths });
+ my $ed = SVN::Git::Fetcher->new({ c => $last_commit, q => $_q });
my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
my (undef, $last_rev, undef) = cmt_metadata($last_commit);
$reporter->set_path('', $last_rev, 0, @lock, $pool);
$reporter->finish_report($pool);
$pool->clear;
+ unless ($ed->{git_commit_ok}) {
+ die "SVN connection failed somewhere...\n";
+ }
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
}
sub libsvn_fetch_full {
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!;
- my @amr;
+ my %amr;
my $p = $SVN->{svn_path};
foreach my $f (keys %$paths) {
my $m = $paths->{$f}->action();
$f =~ s#^/##;
}
if ($m =~ /^[DR]$/) {
- print "\t$m\t$f\n" unless $_q;
- process_rm($gui, $last_commit, $f);
+ process_rm($gui, $last_commit, $f, $_q);
next if $m eq 'D';
# 'R' can be file replacements, too, right?
}
my $t = $SVN->check_path($f, $rev, $pool);
if ($t == $SVN::Node::file) {
if ($m =~ /^[AMR]$/) {
- push @amr, [ $m, $f ];
+ $amr{$f} = $m;
} else {
die "Unrecognized action: $m, ($f r$rev)\n";
}
my @traversed = ();
libsvn_traverse($gui, '', $f, $rev, \@traversed);
foreach (@traversed) {
- push @amr, [ $m, $_ ]
+ $amr{$_} = $m;
}
}
$pool->clear;
}
- foreach (@amr) {
- libsvn_get_file($gui, $_->[1], $rev, $_->[0]);
+ foreach (keys %amr) {
+ libsvn_get_file($gui, $_, $rev, $amr{$_});
}
close $gui or croak $?;
return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
# should be OK to use Pool here (r1 - r0) should be small
my $pool = SVN::Pool->new;
libsvn_get_log($SVN, [$path], $r0, $r1,
- 0, 1, 1, sub {$nr++}, $pool);
+ 0, 0, 1, sub {$nr++}, $pool);
$pool->clear;
} else {
my ($url, undef) = repo_path_split($SVN_URL);
sub libsvn_get_log {
my ($ra, @args) = @_;
+ $args[4]-- if $args[4] && $_xfer_delta && ! $_follow_parent;
if ($SVN::Core::VERSION le '1.2.0') {
splice(@args, 3, 1);
}
my ($paths, $rev, $author, $date, $msg) = @_;
if ($_xfer_delta) {
my $pool = SVN::Pool->new;
- my $ed = SVN::Git::Fetcher->new({paths => $paths, ra => $SVN});
+ my $ed = SVN::Git::Fetcher->new({q => $_q});
my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
$reporter->set_path('', $rev, 1, @lock, $pool);
$reporter->finish_report($pool);
$pool->clear;
+ unless ($ed->{git_commit_ok}) {
+ die "SVN connection failed somewhere...\n";
+ }
} else {
open my $gui, '| git-update-index -z --index-info' or croak $!;
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
sub libsvn_ls_fullurl {
my $fullurl = shift;
- $SVN ||= libsvn_connect($fullurl);
+ my $ra = libsvn_connect($fullurl);
my @ret;
my $pool = SVN::Pool->new;
- my ($dirent, undef, undef) = $SVN->get_dir($SVN->{svn_path},
- $SVN->get_latest_revnum, $pool);
+ my $r = defined $_revision ? $_revision : $ra->get_latest_revnum;
+ my ($dirent, undef, undef) = $ra->get_dir('', $r, $pool);
foreach my $d (keys %$dirent) {
if ($dirent->{$d}->kind == $SVN::Node::dir) {
push @ret, "$d/"; # add '/' for compat with cli svn
open my $gui, '| git-update-index -z --index-info' or croak $!;
$self->{gui} = $gui;
$self->{c} = $git_svn->{c} if exists $git_svn->{c};
- if (my $p = $git_svn->{paths} && $git_svn->{ra}) {
- my $s = $git_svn->{ra}->{svn_path};
- $s = length $s ? qr#^/\Q$s\E/# : qr#^/#;
- $self->{paths} = { map { my $x = $_;
- $x =~ s/$s//;
- $x => $p->{$_} } keys %$p };
- }
+ $self->{q} = $git_svn->{q};
require Digest::MD5;
$self;
}
sub delete_entry {
my ($self, $path, $rev, $pb) = @_;
- process_rm($self->{gui}, $self->{c}, $path);
+ process_rm($self->{gui}, $self->{c}, $path, $self->{q});
undef;
}
my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
=~ /^(\d{6}) blob ([a-f\d]{40})\t/);
{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
- pool => SVN::Pool->new };
+ pool => SVN::Pool->new, action => 'M' };
}
sub add_file {
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
{ path => $path, mode_a => 100644, mode_b => 100644,
- pool => SVN::Pool->new };
+ pool => SVN::Pool->new, action => 'A' };
}
sub change_file_prop {
$fb->{pool}->clear;
my $gui = $self->{gui};
print $gui "$fb->{mode_b} $hash\t$path\0" or croak $!;
- print "\t", $self->{paths}->{$path}->action,
- "\t$path\n" if defined $self->{paths}->{$path};
+ print "\t$fb->{action}\t$path\n" if $fb->{action} && ! $self->{q};
undef;
}
sub close_edit {
my $self = shift;
- close $self->{gui} or croak;
+ close $self->{gui} or croak $!;
+ $self->{git_commit_ok} = 1;
$self->SUPER::close_edit(@_);
}