git-svn: convert to using Git.pm

Thanks to Git.pm, I've been able to greatly reduce the amount
of extra work that needs to be done to manage input/output
pipes in Perl.

chomp usage has also been greatly reduced, too.

All tests (including full-svn-test) still pass, but this has
not been tested extensively in the real-world.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Eric Wong 2006-12-15 10:59:54 -08:00 committed by Junio C Hamano
parent 82dca84871
commit aef4e921a0

View File

@ -46,6 +46,8 @@ use File::Copy qw/copy/;
use POSIX qw/strftime/; use POSIX qw/strftime/;
use IPC::Open3; use IPC::Open3;
use Memoize; use Memoize;
use Git qw/command command_oneline command_noisy
command_output_pipe command_input_pipe command_close_pipe/;
memoize('revisions_eq'); memoize('revisions_eq');
memoize('cmt_metadata'); memoize('cmt_metadata');
memoize('get_commit_time'); memoize('get_commit_time');
@ -232,28 +234,27 @@ sub version {
} }
sub rebuild { sub rebuild {
if (quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0")) { if (!verify_ref("refs/remotes/$GIT_SVN^0")) {
copy_remote_ref(); copy_remote_ref();
} }
$SVN_URL = shift or undef; $SVN_URL = shift or undef;
my $newest_rev = 0; my $newest_rev = 0;
if ($_upgrade) { if ($_upgrade) {
sys('git-update-ref',"refs/remotes/$GIT_SVN","$GIT_SVN-HEAD"); command_noisy('update-ref',"refs/remotes/$GIT_SVN","
$GIT_SVN-HEAD");
} else { } else {
check_upgrade_needed(); check_upgrade_needed();
} }
my $pid = open(my $rev_list,'-|'); my ($rev_list, $ctx) = command_output_pipe("rev-list",
defined $pid or croak $!; "refs/remotes/$GIT_SVN");
if ($pid == 0) {
exec("git-rev-list","refs/remotes/$GIT_SVN") or croak $!;
}
my $latest; my $latest;
while (<$rev_list>) { while (<$rev_list>) {
chomp; chomp;
my $c = $_; my $c = $_;
croak "Non-SHA1: $c\n" unless $c =~ /^$sha1$/o; croak "Non-SHA1: $c\n" unless $c =~ /^$sha1$/o;
my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`); my @commit = grep(/^git-svn-id: /,
command(qw/cat-file commit/, $c));
next if (!@commit); # skip merges next if (!@commit); # skip merges
my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]); my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]);
if (!defined $rev || !$uuid) { if (!defined $rev || !$uuid) {
@ -279,7 +280,7 @@ sub rebuild {
print "r$rev = $c\n"; print "r$rev = $c\n";
$newest_rev = $rev if ($rev > $newest_rev); $newest_rev = $rev if ($rev > $newest_rev);
} }
close $rev_list or croak $?; command_close_pipe($rev_list, $ctx);
goto out if $_use_lib; goto out if $_use_lib;
if (!chdir $SVN_WC) { if (!chdir $SVN_WC) {
@ -287,7 +288,7 @@ sub rebuild {
chdir $SVN_WC or croak $!; chdir $SVN_WC or croak $!;
} }
$pid = fork; my $pid = fork;
defined $pid or croak $!; defined $pid or croak $!;
if ($pid == 0) { if ($pid == 0) {
my @svn_up = qw(svn up); my @svn_up = qw(svn up);
@ -323,10 +324,10 @@ sub init {
$SVN_URL = $url; $SVN_URL = $url;
unless (-d $GIT_DIR) { unless (-d $GIT_DIR) {
my @init_db = ('git-init-db'); my @init_db = ('init-db');
push @init_db, "--template=$_template" if defined $_template; push @init_db, "--template=$_template" if defined $_template;
push @init_db, "--shared" if defined $_shared; push @init_db, "--shared" if defined $_shared;
sys(@init_db); command_noisy(@init_db);
} }
setup_git_svn(); setup_git_svn();
} }
@ -335,9 +336,8 @@ sub fetch {
check_upgrade_needed(); check_upgrade_needed();
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_); my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_);
if ($ret->{commit} && quiet_run(qw(git-rev-parse --verify if ($ret->{commit} && !verify_ref('refs/heads/master^0')) {
refs/heads/master^0))) { command_noisy(qw(update-ref refs/heads/master),$ret->{commit});
sys(qw(git-update-ref refs/heads/master),$ret->{commit});
} }
return $ret; return $ret;
} }
@ -416,16 +416,16 @@ sub fetch_lib {
read_uuid(); read_uuid();
if (defined $last_commit) { if (defined $last_commit) {
unless (-e $GIT_SVN_INDEX) { unless (-e $GIT_SVN_INDEX) {
sys(qw/git-read-tree/, $last_commit); command_noisy('read-tree', $last_commit);
} }
chomp (my $x = `git-write-tree`); my $x = command_oneline('write-tree');
my ($y) = (`git-cat-file commit $last_commit` my ($y) = (command(qw/cat-file commit/, $last_commit)
=~ /^tree ($sha1)/m); =~ /^tree ($sha1)/m);
if ($y ne $x) { if ($y ne $x) {
unlink $GIT_SVN_INDEX or croak $!; unlink $GIT_SVN_INDEX or croak $!;
sys(qw/git-read-tree/, $last_commit); command_noisy('read-tree', $last_commit);
} }
chomp ($x = `git-write-tree`); $x = command_oneline('write-tree');
if ($y ne $x) { if ($y ne $x) {
print STDERR "trees ($last_commit) $y != $x\n", print STDERR "trees ($last_commit) $y != $x\n",
"Something is seriously wrong...\n"; "Something is seriously wrong...\n";
@ -489,16 +489,15 @@ sub commit {
} }
my @revs; my @revs;
foreach my $c (@commits) { foreach my $c (@commits) {
chomp(my @tmp = safe_qx('git-rev-parse',$c)); my @tmp = command('rev-parse',$c);
if (scalar @tmp == 1) { if (scalar @tmp == 1) {
push @revs, $tmp[0]; push @revs, $tmp[0];
} elsif (scalar @tmp > 1) { } elsif (scalar @tmp > 1) {
push @revs, reverse (safe_qx('git-rev-list',@tmp)); push @revs, reverse(command('rev-list',@tmp));
} else { } else {
die "Failed to rev-parse $c\n"; die "Failed to rev-parse $c\n";
} }
} }
chomp @revs;
$_use_lib ? commit_lib(@revs) : commit_cmd(@revs); $_use_lib ? commit_lib(@revs) : commit_cmd(@revs);
print "Done committing ",scalar @revs," revisions to SVN\n"; print "Done committing ",scalar @revs," revisions to SVN\n";
} }
@ -606,10 +605,10 @@ sub commit_lib {
sub dcommit { sub dcommit {
my $head = shift || 'HEAD'; my $head = shift || 'HEAD';
my $gs = "refs/remotes/$GIT_SVN"; my $gs = "refs/remotes/$GIT_SVN";
chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..$head")); my @refs = command(qw/rev-list --no-merges/, "$gs..$head");
my $last_rev; my $last_rev;
foreach my $d (reverse @refs) { foreach my $d (reverse @refs) {
if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) { if (!verify_ref("$d~1")) {
die "Commit $d\n", die "Commit $d\n",
"has no parent commit, and therefore ", "has no parent commit, and therefore ",
"nothing to diff against.\n", "nothing to diff against.\n",
@ -633,7 +632,7 @@ sub dcommit {
} }
return if $_dry_run; return if $_dry_run;
fetch(); fetch();
my @diff = safe_qx('git-diff-tree', $head, $gs); my @diff = command('diff-tree', $head, $gs, '--');
my @finish; my @finish;
if (@diff) { if (@diff) {
@finish = qw/rebase/; @finish = qw/rebase/;
@ -645,7 +644,7 @@ sub dcommit {
"Resetting to the latest $gs\n"; "Resetting to the latest $gs\n";
@finish = qw/reset --mixed/; @finish = qw/reset --mixed/;
} }
sys('git', @finish, $gs); command_noisy(@finish, $gs);
} }
sub show_ignore { sub show_ignore {
@ -689,7 +688,7 @@ sub graft_branches {
if (%$grafts) { if (%$grafts) {
# temporarily disable our grafts file to make this idempotent # temporarily disable our grafts file to make this idempotent
chomp($gr_sha1 = safe_qx(qw/git-hash-object -w/,$gr_file)); chomp($gr_sha1 = command(qw/hash-object -w/,$gr_file));
rename $gr_file, "$gr_file~$gr_sha1" or croak $!; rename $gr_file, "$gr_file~$gr_sha1" or croak $!;
} }
@ -743,7 +742,7 @@ sub multi_init {
unless (-d $GIT_SVN_DIR) { unless (-d $GIT_SVN_DIR) {
print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id; print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id;
init($_trunk); init($_trunk);
sys('git-repo-config', 'svn.trunk', $_trunk); command_noisy('repo-config', 'svn.trunk', $_trunk);
} }
complete_url_ls_init($url, $_branches, '--branches/-b', ''); complete_url_ls_init($url, $_branches, '--branches/-b', '');
complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/'); complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
@ -781,11 +780,8 @@ sub show_log {
} }
config_pager(); config_pager();
my $pid = open(my $log,'-|'); @args = (git_svn_log_cmd($r_min, $r_max), @args);
defined $pid or croak $!; my $log = command_output_pipe(@args);
if (!$pid) {
exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!;
}
run_pager(); run_pager();
my (@k, $c, $d); my (@k, $c, $d);
@ -832,7 +828,7 @@ sub show_log {
process_commit($_, $r_min, $r_max) foreach reverse @k; process_commit($_, $r_min, $r_max) foreach reverse @k;
} }
out: out:
close $log; eval { command_close_pipe($log) };
print '-' x72,"\n" unless $_incremental || $_oneline; print '-' x72,"\n" unless $_incremental || $_oneline;
} }
@ -912,7 +908,7 @@ sub cmt_showable {
return 1 if defined $c->{r}; return 1 if defined $c->{r};
if ($c->{l} && $c->{l}->[-1] eq "...\n" && if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
$c->{a_raw} =~ /\@([a-f\d\-]+)>$/) { $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
my @msg = safe_qx(qw/git-cat-file commit/, $c->{c}); my @msg = command(qw/cat-file commit/, $c->{c});
shift @msg while ($msg[0] ne "\n"); shift @msg while ($msg[0] ne "\n");
shift @msg; shift @msg;
@{$c->{l}} = grep !/^git-svn-id: /, @msg; @{$c->{l}} = grep !/^git-svn-id: /, @msg;
@ -961,7 +957,7 @@ sub log_use_color {
sub git_svn_log_cmd { sub git_svn_log_cmd {
my ($r_min, $r_max) = @_; my ($r_min, $r_max) = @_;
my @cmd = (qw/git-log --abbrev-commit --pretty=raw my @cmd = (qw/log --abbrev-commit --pretty=raw
--default/, "refs/remotes/$GIT_SVN"); --default/, "refs/remotes/$GIT_SVN");
push @cmd, '-r' unless $_non_recursive; push @cmd, '-r' unless $_non_recursive;
push @cmd, qw/--raw --name-status/ if $_verbose; push @cmd, qw/--raw --name-status/ if $_verbose;
@ -1071,7 +1067,7 @@ sub complete_url_ls_init {
waitpid $pid, 0; waitpid $pid, 0;
croak $? if $?; croak $? if $?;
my ($n) = ($switch =~ /^--(\w+)/); my ($n) = ($switch =~ /^--(\w+)/);
sys('git-repo-config', "svn.$n", $var); command_noisy('repo-config', "svn.$n", $var);
} }
sub common_prefix { sub common_prefix {
@ -1103,11 +1099,8 @@ sub graft_tree_joins {
git_svn_each(sub { git_svn_each(sub {
my $i = shift; my $i = shift;
defined(my $pid = open my $fh, '-|') or croak $!; my @args = (qw/rev-list --pretty=raw/, "refs/remotes/$i");
if (!$pid) { my ($fh, $ctx) = command_output_pipe(@args);
exec qw/git-rev-list --pretty=raw/,
"refs/remotes/$i" or croak $!;
}
while (<$fh>) { while (<$fh>) {
next unless /^commit ($sha1)$/o; next unless /^commit ($sha1)$/o;
my $c = $1; my $c = $1;
@ -1130,9 +1123,7 @@ sub graft_tree_joins {
foreach my $p (@{$tree_map{$t}}) { foreach my $p (@{$tree_map{$t}}) {
next if $p eq $c; next if $p eq $c;
my $mb = eval { my $mb = eval { command('merge-base', $c, $p) };
safe_qx('git-merge-base', $c, $p)
};
next unless ($@ || $?); next unless ($@ || $?);
if (defined $r_a) { if (defined $r_a) {
# see if SVN says it's a relative # see if SVN says it's a relative
@ -1161,7 +1152,7 @@ sub graft_tree_joins {
# what should we do when $ct == $s ? # what should we do when $ct == $s ?
} }
} }
close $fh or croak $?; command_close_pipe($fh, $ctx);
}); });
} }
@ -1297,6 +1288,11 @@ sub read_uuid {
} }
} }
sub verify_ref {
my ($ref) = @_;
eval { command_oneline([ 'rev-parse', $ref ], { STDERR => 0 }) };
}
sub quiet_run { sub quiet_run {
my $pid = fork; my $pid = fork;
defined $pid or croak $!; defined $pid or croak $!;
@ -1379,13 +1375,14 @@ sub assert_svn_wc_clean {
sub get_tree_from_treeish { sub get_tree_from_treeish {
my ($treeish) = @_; my ($treeish) = @_;
croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o; croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o;
chomp(my $type = `git-cat-file -t $treeish`); my $type = command_oneline(qw/cat-file -t/, $treeish);
my $expected; my $expected;
while ($type eq 'tag') { while ($type eq 'tag') {
chomp(($treeish, $type) = `git-cat-file tag $treeish`); ($treeish, $type) = command(qw/cat-file tag/, $treeish);
} }
if ($type eq 'commit') { if ($type eq 'commit') {
$expected = (grep /^tree /,`git-cat-file commit $treeish`)[0]; $expected = (grep /^tree /, command(qw/cat-file commit/,
$treeish))[0];
($expected) = ($expected =~ /^tree ($sha1)$/); ($expected) = ($expected =~ /^tree ($sha1)$/);
die "Unable to get tree from $treeish\n" unless $expected; die "Unable to get tree from $treeish\n" unless $expected;
} elsif ($type eq 'tree') { } elsif ($type eq 'tree') {
@ -1407,7 +1404,7 @@ sub assert_tree {
} }
my $old_index = set_index($tmpindex); my $old_index = set_index($tmpindex);
index_changes(1); index_changes(1);
chomp(my $tree = `git-write-tree`); my $tree = command_oneline('write-tree');
restore_index($old_index); restore_index($old_index);
if ($tree ne $expected) { if ($tree ne $expected) {
croak "Tree mismatch, Got: $tree, Expected: $expected\n"; croak "Tree mismatch, Got: $tree, Expected: $expected\n";
@ -1415,8 +1412,20 @@ sub assert_tree {
unlink $tmpindex; unlink $tmpindex;
} }
sub parse_diff_tree { sub get_diff {
my $diff_fh = shift; my ($from, $treeish) = @_;
assert_tree($from);
print "diff-tree $from $treeish\n";
my @diff_tree = qw(diff-tree -z -r);
if ($_cp_similarity) {
push @diff_tree, "-C$_cp_similarity";
} else {
push @diff_tree, '-C';
}
push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
push @diff_tree, "-l$_l" if defined $_l;
push @diff_tree, $from, $treeish;
my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
local $/ = "\0"; local $/ = "\0";
my $state = 'meta'; my $state = 'meta';
my @mods; my @mods;
@ -1452,8 +1461,7 @@ sub parse_diff_tree {
croak "Error parsing $_\n"; croak "Error parsing $_\n";
} }
} }
close $diff_fh or croak $?; command_close_pipe($diff_fh, $ctx);
return \@mods; return \@mods;
} }
@ -1547,26 +1555,6 @@ sub precommit_check {
} }
sub get_diff {
my ($from, $treeish) = @_;
assert_tree($from);
print "diff-tree $from $treeish\n";
my $pid = open my $diff_fh, '-|';
defined $pid or croak $!;
if ($pid == 0) {
my @diff_tree = qw(git-diff-tree -z -r);
if ($_cp_similarity) {
push @diff_tree, "-C$_cp_similarity";
} else {
push @diff_tree, '-C';
}
push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
push @diff_tree, "-l$_l" if defined $_l;
exec(@diff_tree, $from, $treeish) or croak $!;
}
return parse_diff_tree($diff_fh);
}
sub svn_checkout_tree { sub svn_checkout_tree {
my ($from, $treeish) = @_; my ($from, $treeish) = @_;
my $mods = get_diff($from->{commit}, $treeish); my $mods = get_diff($from->{commit}, $treeish);
@ -1676,14 +1664,10 @@ sub get_commit_message {
my %log_msg = ( msg => '' ); my %log_msg = ( msg => '' );
open my $msg, '>', $commit_msg or croak $!; open my $msg, '>', $commit_msg or croak $!;
chomp(my $type = `git-cat-file -t $commit`); my $type = command_oneline(qw/cat-file -t/, $commit);
if ($type eq 'commit' || $type eq 'tag') { if ($type eq 'commit' || $type eq 'tag') {
my $pid = open my $msg_fh, '-|'; my ($msg_fh, $ctx) = command_output_pipe('cat-file',
defined $pid or croak $!; $type, $commit);
if ($pid == 0) {
exec('git-cat-file', $type, $commit) or croak $!;
}
my $in_msg = 0; my $in_msg = 0;
while (<$msg_fh>) { while (<$msg_fh>) {
if (!$in_msg) { if (!$in_msg) {
@ -1695,7 +1679,7 @@ sub get_commit_message {
print $msg $_ or croak $!; print $msg $_ or croak $!;
} }
} }
close $msg_fh or croak $?; command_close_pipe($msg_fh, $ctx);
} }
close $msg or croak $!; close $msg or croak $!;
@ -1771,13 +1755,8 @@ sub svn_commit_tree {
} }
sub rev_list_raw { sub rev_list_raw {
my (@args) = @_; my ($fh, $c) = command_output_pipe(qw/rev-list --pretty=raw/, @_);
my $pid = open my $fh, '-|'; return { fh => $fh, ctx => $c, t => { } };
defined $pid or croak $!;
if (!$pid) {
exec(qw/git-rev-list --pretty=raw/, @args) or croak $!;
}
return { fh => $fh, t => { } };
} }
sub next_rev_list_entry { sub next_rev_list_entry {
@ -1799,6 +1778,7 @@ sub next_rev_list_entry {
$x->{m} .= $_; $x->{m} .= $_;
} }
} }
command_close_pipe($fh, $rl->{ctx});
return ($x != $rl->{t}) ? $x : undef; return ($x != $rl->{t}) ? $x : undef;
} }
@ -1924,15 +1904,10 @@ sub sys { system(@_) == 0 or croak $? }
sub do_update_index { sub do_update_index {
my ($z_cmd, $cmd, $no_text_base) = @_; my ($z_cmd, $cmd, $no_text_base) = @_;
my $z = open my $p, '-|'; my ($p, $pctx) = command_output_pipe(@$z_cmd);
defined $z or croak $!;
unless ($z) { exec @$z_cmd or croak $! }
my $pid = open my $ui, '|-'; my ($ui, $uctx) = command_input_pipe('update-index',
defined $pid or croak $!; "--$cmd",'-z','--stdin');
unless ($pid) {
exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!;
}
local $/ = "\0"; local $/ = "\0";
while (my $x = <$p>) { while (my $x = <$p>) {
chomp $x; chomp $x;
@ -1956,7 +1931,8 @@ sub do_update_index {
} }
print $ui $x,"\0"; print $ui $x,"\0";
} }
close $ui or croak $?; command_close_pipe($p, $pctx);
command_close_pipe($ui, $uctx);
} }
sub index_changes { sub index_changes {
@ -1968,10 +1944,10 @@ sub index_changes {
close $fd or croak $!; close $fd or croak $!;
} }
my $no_text_base = shift; my $no_text_base = shift;
do_update_index([qw/git-diff-files --name-only -z/], do_update_index([qw/diff-files --name-only -z/],
'remove', 'remove',
$no_text_base); $no_text_base);
do_update_index([qw/git-ls-files -z --others/, do_update_index([qw/ls-files -z --others/,
"--exclude-from=$GIT_SVN_DIR/info/exclude"], "--exclude-from=$GIT_SVN_DIR/info/exclude"],
'add', 'add',
$no_text_base); $no_text_base);
@ -2004,10 +1980,10 @@ sub assert_revision_unknown {
sub trees_eq { sub trees_eq {
my ($x, $y) = @_; my ($x, $y) = @_;
my @x = safe_qx('git-cat-file','commit',$x); my @x = command(qw/cat-file commit/,$x);
my @y = safe_qx('git-cat-file','commit',$y); my @y = command(qw/cat-file commit/,$y);
if (($y[0] ne $x[0]) || $x[0] !~ /^tree $sha1\n$/ if (($y[0] ne $x[0]) || $x[0] ne "tree $sha1"
|| $y[0] !~ /^tree $sha1\n$/) { || $y[0] ne "tree $sha1") {
print STDERR "Trees not equal: $y[0] != $x[0]\n"; print STDERR "Trees not equal: $y[0] != $x[0]\n";
return 0 return 0
} }
@ -2039,33 +2015,24 @@ sub git_commit {
if (!defined $tree) { if (!defined $tree) {
my $index = set_index($GIT_SVN_INDEX); my $index = set_index($GIT_SVN_INDEX);
index_changes(); index_changes();
chomp($tree = `git-write-tree`); $tree = command_oneline('write-tree');
croak $? if $?; croak $? if $?;
restore_index($index); restore_index($index);
} }
# just in case we clobber the existing ref, we still want that ref # just in case we clobber the existing ref, we still want that ref
# as our parent: # as our parent:
open my $null, '>', '/dev/null' or croak $!; if (my $cur = verify_ref("refs/remotes/$GIT_SVN^0")) {
open my $stderr, '>&', \*STDERR or croak $!;
open STDERR, '>&', $null or croak $!;
if (my $cur = eval { safe_qx('git-rev-parse',
"refs/remotes/$GIT_SVN^0") }) {
chomp $cur; chomp $cur;
push @tmp_parents, $cur; push @tmp_parents, $cur;
} }
open STDERR, '>&', $stderr or croak $!;
close $stderr or croak $!;
close $null or croak $!;
if (exists $tree_map{$tree}) { if (exists $tree_map{$tree}) {
foreach my $p (@{$tree_map{$tree}}) { foreach my $p (@{$tree_map{$tree}}) {
my $skip; my $skip;
foreach (@tmp_parents) { foreach (@tmp_parents) {
# see if a common parent is found # see if a common parent is found
my $mb = eval { my $mb = eval { command('merge-base', $_, $p) };
safe_qx('git-merge-base', $_, $p)
};
next if ($@ || $?); next if ($@ || $?);
$skip = 1; $skip = 1;
last; last;
@ -2107,7 +2074,7 @@ sub git_commit {
if ($commit !~ /^$sha1$/o) { if ($commit !~ /^$sha1$/o) {
die "Failed to commit, invalid sha1: $commit\n"; die "Failed to commit, invalid sha1: $commit\n";
} }
sys('git-update-ref',"refs/remotes/$GIT_SVN",$commit); command_noisy('update-ref',"refs/remotes/$GIT_SVN",$commit);
revdb_set($REVDB, $log_msg->{revision}, $commit); revdb_set($REVDB, $log_msg->{revision}, $commit);
# this output is read via pipe, do not change: # this output is read via pipe, do not change:
@ -2119,7 +2086,8 @@ sub git_commit {
sub check_repack { sub check_repack {
if ($_repack && (--$_repack_nr == 0)) { if ($_repack && (--$_repack_nr == 0)) {
$_repack_nr = $_repack; $_repack_nr = $_repack;
sys("git repack $_repack_flags"); # repack doesn't use any arguments with spaces in them, does it?
command_noisy('repack', split(/\s+/, $_repack_flags));
} }
} }
@ -2238,20 +2206,11 @@ sub check_upgrade_needed {
open my $fh, '>>',$REVDB or croak $!; open my $fh, '>>',$REVDB or croak $!;
close $fh; close $fh;
} }
my $old = eval { return unless eval {
my $pid = open my $child, '-|'; command([qw/rev-parse --verify/,"$GIT_SVN-HEAD^0"],
defined $pid or croak $!; {STDERR => 0});
if ($pid == 0) {
close STDERR;
exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $!;
}
my @ret = (<$child>);
close $child or croak $?;
die $? if $?; # just in case close didn't error out
return wantarray ? @ret : join('',@ret);
}; };
return unless $old; my $head = eval { command('rev-parse',"refs/remotes/$GIT_SVN") };
my $head = eval { safe_qx('git-rev-parse',"refs/remotes/$GIT_SVN") };
if ($@ || !$head) { if ($@ || !$head) {
print STDERR "Please run: $0 rebuild --upgrade\n"; print STDERR "Please run: $0 rebuild --upgrade\n";
exit 1; exit 1;
@ -2263,12 +2222,8 @@ sub check_upgrade_needed {
sub map_tree_joins { sub map_tree_joins {
my %seen; my %seen;
foreach my $br (@_branch_from) { foreach my $br (@_branch_from) {
my $pid = open my $pipe, '-|'; my $pipe = command_output_pipe(qw/rev-list
defined $pid or croak $!; --topo-order --pretty=raw/, $br);
if ($pid == 0) {
exec(qw(git-rev-list --topo-order --pretty=raw), $br)
or croak $!;
}
while (<$pipe>) { while (<$pipe>) {
if (/^commit ($sha1)$/o) { if (/^commit ($sha1)$/o) {
my $commit = $1; my $commit = $1;
@ -2284,7 +2239,7 @@ sub map_tree_joins {
$seen{$commit} = 1; $seen{$commit} = 1;
} }
} }
close $pipe; # we could be breaking the pipe early eval { command_close_pipe($pipe) };
} }
} }
@ -2296,7 +2251,7 @@ sub load_all_refs {
# don't worry about rev-list on non-commit objects/tags, # don't worry about rev-list on non-commit objects/tags,
# it shouldn't blow up if a ref is a blob or tree... # it shouldn't blow up if a ref is a blob or tree...
chomp(@_branch_from = `git-rev-parse --symbolic --all`); @_branch_from = command(qw/rev-parse --symbolic --all/);
} }
# '<svn username> = real-name <email address>' mapping based on git-svnimport: # '<svn username> = real-name <email address>' mapping based on git-svnimport:
@ -2330,7 +2285,7 @@ sub svn_propget_base {
sub git_svn_each { sub git_svn_each {
my $sub = shift; my $sub = shift;
foreach (`git-rev-parse --symbolic --all`) { foreach (command(qw/rev-parse --symbolic --all/)) {
next unless s#^refs/remotes/##; next unless s#^refs/remotes/##;
chomp $_; chomp $_;
next unless -f "$GIT_DIR/svn/$_/info/url"; next unless -f "$GIT_DIR/svn/$_/info/url";
@ -2371,7 +2326,7 @@ sub migration_check {
"$GIT_SVN_DIR\n\t(required for this version ", "$GIT_SVN_DIR\n\t(required for this version ",
"($VERSION) of git-svn) does not.\n"; "($VERSION) of git-svn) does not.\n";
foreach my $x (`git-rev-parse --symbolic --all`) { foreach my $x (command(qw/rev-parse --symbolic --all/)) {
next unless $x =~ s#^refs/remotes/##; next unless $x =~ s#^refs/remotes/##;
chomp $x; chomp $x;
next unless -f "$GIT_DIR/$x/info/url"; next unless -f "$GIT_DIR/$x/info/url";
@ -2476,11 +2431,7 @@ sub write_grafts {
my $p = $grafts->{$c}; my $p = $grafts->{$c};
my %x; # real parents my %x; # real parents
delete $p->{$c}; # commits are not self-reproducing... delete $p->{$c}; # commits are not self-reproducing...
my $pid = open my $ch, '-|'; my $ch = command_output_pipe(qw/cat-file commit/, $c);
defined $pid or croak $!;
if (!$pid) {
exec(qw/git-cat-file commit/, $c) or croak $!;
}
while (<$ch>) { while (<$ch>) {
if (/^parent ($sha1)/) { if (/^parent ($sha1)/) {
$x{$1} = $p->{$1} = 1; $x{$1} = $p->{$1} = 1;
@ -2488,7 +2439,7 @@ sub write_grafts {
last unless /^\S/; last unless /^\S/;
} }
} }
close $ch; # breaking the pipe eval { command_close_pipe($ch) }; # breaking the pipe
# if real parents are the only ones in the grafts, drop it # if real parents are the only ones in the grafts, drop it
next if join(' ',sort keys %$p) eq join(' ',sort keys %x); next if join(' ',sort keys %$p) eq join(' ',sort keys %x);
@ -2500,7 +2451,7 @@ sub write_grafts {
next if $del{$i} || $p->{$i} == 2; next if $del{$i} || $p->{$i} == 2;
foreach my $j (@jp) { foreach my $j (@jp) {
next if $i eq $j || $del{$j} || $p->{$j} == 2; next if $i eq $j || $del{$j} || $p->{$j} == 2;
$mb = eval { safe_qx('git-merge-base',$i,$j) }; $mb = eval { command('merge-base', $i, $j) };
next unless $mb; next unless $mb;
chomp $mb; chomp $mb;
next if $x{$mb}; next if $x{$mb};
@ -2571,15 +2522,12 @@ sub extract_metadata {
sub cmt_metadata { sub cmt_metadata {
return extract_metadata((grep(/^git-svn-id: /, return extract_metadata((grep(/^git-svn-id: /,
safe_qx(qw/git-cat-file commit/, shift)))[-1]); command(qw/cat-file commit/, shift)))[-1]);
} }
sub get_commit_time { sub get_commit_time {
my $cmt = shift; my $cmt = shift;
defined(my $pid = open my $fh, '-|') or croak $!; my $fh = command_output_pipe(qw/rev-list --pretty=raw -n1/, $cmt);
if (!$pid) {
exec qw/git-rev-list --pretty=raw -n1/, $cmt or croak $!;
}
while (<$fh>) { while (<$fh>) {
/^committer\s(?:.+) (\d+) ([\-\+]?\d+)$/ or next; /^committer\s(?:.+) (\d+) ([\-\+]?\d+)$/ or next;
my ($s, $tz) = ($1, $2); my ($s, $tz) = ($1, $2);
@ -2588,7 +2536,7 @@ sub get_commit_time {
} elsif ($tz =~ s/^\-//) { } elsif ($tz =~ s/^\-//) {
$s -= tz_to_s_offset($tz); $s -= tz_to_s_offset($tz);
} }
close $fh; eval { command_close_pipe($fh) };
return $s; return $s;
} }
die "Can't get commit time for commit: $cmt\n"; die "Can't get commit time for commit: $cmt\n";
@ -2748,7 +2696,6 @@ sub libsvn_load {
push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor'; push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor'; push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
*SVN::Git::Fetcher::process_rm = *process_rm; *SVN::Git::Fetcher::process_rm = *process_rm;
*SVN::Git::Fetcher::safe_qx = *safe_qx;
my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file. my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
$SVN::Node::dir.$SVN::Node::unknown. $SVN::Node::dir.$SVN::Node::unknown.
$SVN::Node::none.$SVN::Node::file. $SVN::Node::none.$SVN::Node::file.
@ -2965,7 +2912,7 @@ sub libsvn_get_file {
my $mode = exists $props->{'svn:executable'} ? '100755' : '100644'; my $mode = exists $props->{'svn:executable'} ? '100755' : '100644';
if (exists $props->{'svn:special'}) { if (exists $props->{'svn:special'}) {
$mode = '120000'; $mode = '120000';
my $link = `git-cat-file blob $hash`; my $link = `git-cat-file blob $hash`; # no chomping symlinks
$link =~ s/^link // or die "svn:special file with contents: <", $link =~ s/^link // or die "svn:special file with contents: <",
$link, "> is not understood\n"; $link, "> is not understood\n";
defined($pid = open3($in, $out, '>&STDERR', defined($pid = open3($in, $out, '>&STDERR',
@ -3069,19 +3016,17 @@ sub libsvn_log_entry {
sub process_rm { sub process_rm {
my ($gui, $last_commit, $f, $q) = @_; my ($gui, $last_commit, $f, $q) = @_;
# remove entire directories. # remove entire directories.
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) { if (command('ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
defined(my $pid = open my $ls, '-|') or croak $!; my ($ls, $ctx) = command_output_pipe(qw/ls-tree
if (!$pid) { -r --name-only -z/,
exec(qw/git-ls-tree -r --name-only -z/, $last_commit,'--',$f);
$last_commit,'--',$f) or croak $!;
}
local $/ = "\0"; local $/ = "\0";
while (<$ls>) { while (<$ls>) {
print $gui '0 ',0 x 40,"\t",$_ or croak $!; print $gui '0 ',0 x 40,"\t",$_ or croak $!;
print "\tD\t$_\n" unless $q; print "\tD\t$_\n" unless $q;
} }
print "\tD\t$f/\n" unless $q; print "\tD\t$f/\n" unless $q;
close $ls or croak $?; command_close_pipe($ls, $ctx);
return $SVN::Node::dir; return $SVN::Node::dir;
} else { } else {
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!; print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
@ -3112,7 +3057,7 @@ sub libsvn_fetch_delta {
sub libsvn_fetch_full { sub libsvn_fetch_full {
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_; my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!; my ($gui, $ctx) = command_input_pipe(qw/update-index -z --index-info/);
my %amr; my %amr;
my $ut = { empty => {}, dir_prop => {}, file_prop => {} }; my $ut = { empty => {}, dir_prop => {}, file_prop => {} };
my $p = $SVN->{svn_path}; my $p = $SVN->{svn_path};
@ -3166,20 +3111,14 @@ sub libsvn_fetch_full {
%{$ut->{dir_prop}->{''}} = %$props; %{$ut->{dir_prop}->{''}} = %$props;
$pool->clear; $pool->clear;
} }
close $gui or croak $?; command_close_pipe($gui, $ctx);
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ut); libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ut);
} }
sub svn_grab_base_rev { sub svn_grab_base_rev {
defined(my $pid = open my $fh, '-|') or croak $!; my $c = eval { command_oneline([qw/rev-parse --verify/,
if (!$pid) { "refs/remotes/$GIT_SVN^0"],
open my $null, '>', '/dev/null' or croak $!; { STDERR => 0 }) };
open STDERR, '>&', $null or croak $!;
exec qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0"
or croak $!;
}
chomp(my $c = do { local $/; <$fh> });
close $fh;
if (defined $c && length $c) { if (defined $c && length $c) {
my ($url, $rev, $uuid) = cmt_metadata($c); my ($url, $rev, $uuid) = cmt_metadata($c);
return ($rev, $c) if defined $rev; return ($rev, $c) if defined $rev;
@ -3358,7 +3297,7 @@ sub libsvn_find_parent_branch {
if (revisions_eq($branch_from, $r0, $r)) { if (revisions_eq($branch_from, $r0, $r)) {
unlink $GIT_SVN_INDEX; unlink $GIT_SVN_INDEX;
print STDERR "Found branch parent: ($GIT_SVN) $parent\n"; print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
sys(qw/git-read-tree/, $parent); command_noisy('read-tree', $parent);
unless (libsvn_can_do_switch()) { unless (libsvn_can_do_switch()) {
return libsvn_fetch_full($parent, $paths, $rev, return libsvn_fetch_full($parent, $paths, $rev,
$author, $date, $msg); $author, $date, $msg);
@ -3367,7 +3306,7 @@ sub libsvn_find_parent_branch {
# included with SVN 1.4.2 (the latest version at the moment), # included with SVN 1.4.2 (the latest version at the moment),
# so we can't rely on it. # so we can't rely on it.
my $ra = libsvn_connect("$url/$branch_from"); my $ra = libsvn_connect("$url/$branch_from");
my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q}); my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q });
my $pool = SVN::Pool->new; my $pool = SVN::Pool->new;
my $reporter = $ra->do_switch($rev, '', 1, $SVN->{url}, my $reporter = $ra->do_switch($rev, '', 1, $SVN->{url},
$ed, $pool); $ed, $pool);
@ -3413,9 +3352,10 @@ sub libsvn_new_tree {
$ut = $ed; $ut = $ed;
} else { } else {
$ut = { empty => {}, dir_prop => {}, file_prop => {} }; $ut = { empty => {}, dir_prop => {}, file_prop => {} };
open my $gui, '| git-update-index -z --index-info' or croak $!; my ($gui, $ctx) = command_input_pipe(qw/update-index
-z --index-info/);
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev, undef, $ut); libsvn_traverse($gui, '', $SVN->{svn_path}, $rev, undef, $ut);
close $gui or croak $?; command_close_pipe($gui, $ctx);
} }
libsvn_log_entry($rev, $author, $date, $msg, [], $ut); libsvn_log_entry($rev, $author, $date, $msg, [], $ut);
} }
@ -3487,7 +3427,7 @@ sub libsvn_commit_cb {
my $log = libsvn_log_entry($rev,$committer,$date,$msg); my $log = libsvn_log_entry($rev,$committer,$date,$msg);
$log->{tree} = get_tree_from_treeish($c); $log->{tree} = get_tree_from_treeish($c);
my $cmt = git_commit($log, $cmt_last, $c); my $cmt = git_commit($log, $cmt_last, $c);
my @diff = safe_qx('git-diff-tree', $cmt, $c); my @diff = command('diff-tree', $cmt, $c);
if (@diff) { if (@diff) {
print STDERR "Trees differ: $cmt $c\n", print STDERR "Trees differ: $cmt $c\n",
join('',@diff),"\n"; join('',@diff),"\n";
@ -3579,8 +3519,8 @@ sub revdb_get {
sub copy_remote_ref { sub copy_remote_ref {
my $origin = $_cp_remote ? $_cp_remote : 'origin'; my $origin = $_cp_remote ? $_cp_remote : 'origin';
my $ref = "refs/remotes/$GIT_SVN"; my $ref = "refs/remotes/$GIT_SVN";
if (safe_qx('git-ls-remote', $origin, $ref)) { if (command('ls-remote', $origin, $ref)) {
sys(qw/git fetch/, $origin, "$ref:$ref"); command_noisy('fetch', $origin, "$ref:$ref");
} elsif ($_cp_remote && !$_upgrade) { } elsif ($_cp_remote && !$_upgrade) {
die "Unable to find remote reference: ", die "Unable to find remote reference: ",
"refs/remotes/$GIT_SVN on $origin\n"; "refs/remotes/$GIT_SVN on $origin\n";
@ -3592,14 +3532,14 @@ use strict;
use warnings; use warnings;
use Carp qw/croak/; use Carp qw/croak/;
use IO::File qw//; use IO::File qw//;
use Git qw/command command_oneline command_noisy
command_output_pipe command_input_pipe command_close_pipe/;
# file baton members: path, mode_a, mode_b, pool, fh, blob, base # file baton members: path, mode_a, mode_b, pool, fh, blob, base
sub new { sub new {
my ($class, $git_svn) = @_; my ($class, $git_svn) = @_;
my $self = SVN::Delta::Editor->new; my $self = SVN::Delta::Editor->new;
bless $self, $class; bless $self, $class;
open my $gui, '| git-update-index -z --index-info' or croak $!;
$self->{gui} = $gui;
$self->{c} = $git_svn->{c} if exists $git_svn->{c}; $self->{c} = $git_svn->{c} if exists $git_svn->{c};
$self->{q} = $git_svn->{q}; $self->{q} = $git_svn->{q};
$self->{empty} = {}; $self->{empty} = {};
@ -3607,6 +3547,8 @@ sub new {
$self->{file_prop} = {}; $self->{file_prop} = {};
$self->{absent_dir} = {}; $self->{absent_dir} = {};
$self->{absent_file} = {}; $self->{absent_file} = {};
($self->{gui}, $self->{ctx}) = command_input_pipe(
qw/update-index -z --index-info/);
require Digest::MD5; require Digest::MD5;
$self; $self;
} }
@ -3629,7 +3571,7 @@ sub delete_entry {
sub open_file { sub open_file {
my ($self, $path, $pb, $rev) = @_; my ($self, $path, $pb, $rev) = @_;
my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path) my ($mode, $blob) = (command('ls-tree', $self->{c}, '--',$path)
=~ /^(\d{6}) blob ([a-f\d]{40})\t/); =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
unless (defined $mode && defined $blob) { unless (defined $mode && defined $blob) {
die "$path was not found in commit $self->{c} (r$rev)\n"; die "$path was not found in commit $self->{c} (r$rev)\n";
@ -3764,13 +3706,13 @@ sub close_file {
sub abort_edit { sub abort_edit {
my $self = shift; my $self = shift;
close $self->{gui}; eval { command_close_pipe($self->{gui}, $self->{ctx}) };
$self->SUPER::abort_edit(@_); $self->SUPER::abort_edit(@_);
} }
sub close_edit { sub close_edit {
my $self = shift; my $self = shift;
close $self->{gui} or croak $!; command_close_pipe($self->{gui}, $self->{ctx});
$self->{git_commit_ok} = 1; $self->{git_commit_ok} = 1;
$self->SUPER::close_edit(@_); $self->SUPER::close_edit(@_);
} }
@ -3781,6 +3723,8 @@ use strict;
use warnings; use warnings;
use Carp qw/croak/; use Carp qw/croak/;
use IO::File; use IO::File;
use Git qw/command command_oneline command_noisy
command_output_pipe command_input_pipe command_close_pipe/;
sub new { sub new {
my $class = shift; my $class = shift;
@ -3830,10 +3774,8 @@ sub rmdirs {
delete $rm->{''}; # we never delete the url we're tracking delete $rm->{''}; # we never delete the url we're tracking
return unless %$rm; return unless %$rm;
defined(my $pid = open my $fh,'-|') or croak $!; my ($fh, $ctx) = command_output_pipe(
if (!$pid) { qw/ls-tree --name-only -r -z/, $self->{c});
exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
}
local $/ = "\0"; local $/ = "\0";
while (<$fh>) { while (<$fh>) {
chomp; chomp;
@ -3842,11 +3784,11 @@ sub rmdirs {
delete $rm->{join '/', @dn}; delete $rm->{join '/', @dn};
} }
unless (%$rm) { unless (%$rm) {
close $fh; eval { command_close_pipe($fh) };
return; return;
} }
} }
close $fh; command_close_pipe($fh, $ctx);
my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat}); my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat});
foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) { foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {