зеркало из https://github.com/microsoft/git.git
git-svn: convert the 'commit-diff' command to Git::SVN
Also, convert all usage of 'log_msg' to 'log_entry' for consistency's sake SVN::Git::Editor::apply_diff now drives the rest of the editor. Signed-off-by: Eric Wong <normalperson@yhbt.net>
This commit is contained in:
Родитель
c843c464b8
Коммит
44320b9e0e
366
git-svn.perl
366
git-svn.perl
|
@ -8,7 +8,8 @@ use vars qw/ $AUTHOR $VERSION
|
|||
$GIT_SVN_INDEX $GIT_SVN
|
||||
$GIT_DIR $GIT_SVN_DIR $REVDB
|
||||
$_follow_parent $sha1 $sha1_short $_revision
|
||||
$_cp_remote $_upgrade/;
|
||||
$_cp_remote $_upgrade $_rmdir $_q $_cp_similarity
|
||||
$_find_copies_harder $_l/;
|
||||
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
||||
$VERSION = '@@GIT_VERSION@@';
|
||||
|
||||
|
@ -70,9 +71,8 @@ my ($SVN);
|
|||
my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
|
||||
$sha1 = qr/[a-f\d]{40}/;
|
||||
$sha1_short = qr/[a-f\d]{4,40}/;
|
||||
my ($_stdin,$_help,$_rmdir,$_edit,
|
||||
$_find_copies_harder, $_l, $_cp_similarity,
|
||||
$_repack, $_repack_nr, $_repack_flags, $_q,
|
||||
my ($_stdin, $_help, $_edit,
|
||||
$_repack, $_repack_nr, $_repack_flags,
|
||||
$_message, $_file, $_no_metadata,
|
||||
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
|
||||
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
|
||||
|
@ -154,7 +154,8 @@ my %cmd = (
|
|||
'color' => \$Git::SVN::Log::color,
|
||||
'pager=s' => \$Git::SVN::Log::pager,
|
||||
} ],
|
||||
'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
|
||||
'commit-diff' => [ \&cmd_commit_diff,
|
||||
'Commit a diff between two trees',
|
||||
{ 'message|m=s' => \$_message,
|
||||
'file|F=s' => \$_file,
|
||||
'revision|r=s' => \$_revision,
|
||||
|
@ -354,18 +355,18 @@ sub fetch_lib {
|
|||
# on the limiter.
|
||||
$SVN->dup->get_log([''], $min, $max, 0, 1, 1,
|
||||
sub {
|
||||
my $log_msg;
|
||||
my $log_entry;
|
||||
if ($last_commit) {
|
||||
$log_msg = libsvn_fetch(
|
||||
$log_entry = libsvn_fetch(
|
||||
$last_commit, @_);
|
||||
$last_commit = git_commit(
|
||||
$log_msg,
|
||||
$log_entry,
|
||||
$last_commit,
|
||||
@parents);
|
||||
} else {
|
||||
$log_msg = libsvn_new_tree(@_);
|
||||
$log_entry = libsvn_new_tree(@_);
|
||||
$last_commit = git_commit(
|
||||
$log_msg, @parents);
|
||||
$log_entry, @parents);
|
||||
}
|
||||
});
|
||||
exit 0;
|
||||
|
@ -428,7 +429,7 @@ sub commit_lib {
|
|||
my $repo;
|
||||
set_svn_commit_env();
|
||||
foreach my $c (@revs) {
|
||||
my $log_msg = get_commit_message($c, $commit_msg);
|
||||
my $log_entry = get_commit_entry($c, $commit_msg);
|
||||
|
||||
# fork for each commit because there's a memory leak I
|
||||
# can't track down... (it's probably in the SVN code)
|
||||
|
@ -438,25 +439,21 @@ sub commit_lib {
|
|||
my $ed = SVN::Git::Editor->new(
|
||||
{ r => $r_last,
|
||||
ra => $SVN->dup,
|
||||
c => $c,
|
||||
svn_path => $SVN->{svn_path},
|
||||
},
|
||||
$SVN->get_commit_editor(
|
||||
$log_msg->{msg},
|
||||
$log_entry->{log},
|
||||
sub {
|
||||
libsvn_commit_cb(
|
||||
@_, $c,
|
||||
$log_msg->{msg},
|
||||
$log_entry->{log},
|
||||
$r_last,
|
||||
$cmt_last)
|
||||
}, $pool)
|
||||
);
|
||||
my $mods = libsvn_checkout_tree($cmt_last, $c, $ed);
|
||||
my $mods = $ed->apply_diff($cmt_last, $c);
|
||||
if (@$mods == 0) {
|
||||
print "No changes\nr$r_last = $cmt_last\n";
|
||||
$ed->abort_edit;
|
||||
} else {
|
||||
$ed->close_edit;
|
||||
}
|
||||
$pool->clear;
|
||||
exit 0;
|
||||
|
@ -599,6 +596,55 @@ sub multi_fetch {
|
|||
rec_fetch('', "$GIT_DIR/svn", @_);
|
||||
}
|
||||
|
||||
# this command is special because it requires no metadata
|
||||
sub cmd_commit_diff {
|
||||
my ($ta, $tb, $url) = @_;
|
||||
my $usage = "Usage: $0 commit-diff -r<revision> ".
|
||||
"<tree-ish> <tree-ish> [<URL>]\n";
|
||||
fatal($usage) if (!defined $ta || !defined $tb);
|
||||
if (!defined $url) {
|
||||
my $gs = eval { Git::SVN->new };
|
||||
if (!$gs) {
|
||||
fatal("Needed URL or usable git-svn --id in ",
|
||||
"the command-line\n", $usage);
|
||||
}
|
||||
$url = $gs->{url};
|
||||
}
|
||||
unless (defined $_revision) {
|
||||
fatal("-r|--revision is a required argument\n", $usage);
|
||||
}
|
||||
if (defined $_message && defined $_file) {
|
||||
fatal("Both --message/-m and --file/-F specified ",
|
||||
"for the commit message.\n",
|
||||
"I have no idea what you mean\n");
|
||||
}
|
||||
if (defined $_file) {
|
||||
$_message = file_to_s($_file);
|
||||
} else {
|
||||
$_message ||= get_commit_entry($tb)->{log};
|
||||
}
|
||||
my $ra ||= Git::SVN::Ra->new($url);
|
||||
my $r = $_revision;
|
||||
if ($r eq 'HEAD') {
|
||||
$r = $ra->get_latest_revnum;
|
||||
} elsif ($r !~ /^\d+$/) {
|
||||
die "revision argument: $r not understood by git-svn\n";
|
||||
}
|
||||
my $pool = SVN::Pool->new;
|
||||
my %ed_opts = ( r => $r,
|
||||
ra => $ra->dup,
|
||||
svn_path => $ra->{svn_path} );
|
||||
my $ed = SVN::Git::Editor->new(\%ed_opts,
|
||||
$ra->get_commit_editor($_message,
|
||||
sub { print "Committed r$_[0]\n" }),
|
||||
$pool);
|
||||
my $mods = $ed->apply_diff($ta, $tb);
|
||||
if (@$mods == 0) {
|
||||
print "No changes\n$ta == $tb\n";
|
||||
}
|
||||
$pool->clear;
|
||||
}
|
||||
|
||||
sub commit_diff_usage {
|
||||
print STDERR "Usage: $0 commit-diff <tree-ish> <tree-ish> [<URL>]\n";
|
||||
exit 1
|
||||
|
@ -628,8 +674,8 @@ sub commit_diff {
|
|||
if (defined $_file) {
|
||||
$_message = file_to_s($_file);
|
||||
} else {
|
||||
$_message ||= get_commit_message($tb,
|
||||
"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
|
||||
$_message ||= get_commit_entry($tb,
|
||||
"$GIT_DIR/.svn-commit.tmp.$$")->{log};
|
||||
}
|
||||
$SVN ||= Git::SVN::Ra->new($SVN_URL);
|
||||
if ($r eq 'HEAD') {
|
||||
|
@ -641,7 +687,6 @@ sub commit_diff {
|
|||
my $pool = SVN::Pool->new;
|
||||
my $ed = SVN::Git::Editor->new({ r => $r,
|
||||
ra => $SVN->dup,
|
||||
c => $tb,
|
||||
svn_path => $SVN->{svn_path}
|
||||
},
|
||||
$SVN->get_commit_editor($_message,
|
||||
|
@ -652,12 +697,9 @@ sub commit_diff {
|
|||
$pool)
|
||||
);
|
||||
eval {
|
||||
my $mods = libsvn_checkout_tree($ta, $tb, $ed);
|
||||
my $mods = $ed->apply_diff($ta, $tb);
|
||||
if (@$mods == 0) {
|
||||
print "No changes\n$ta == $tb\n";
|
||||
$ed->abort_edit;
|
||||
} else {
|
||||
$ed->close_edit;
|
||||
}
|
||||
};
|
||||
$pool->clear;
|
||||
|
@ -963,7 +1005,7 @@ sub setup_git_svn {
|
|||
|
||||
sub get_tree_from_treeish {
|
||||
my ($treeish) = @_;
|
||||
croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o;
|
||||
# $treeish can be a symbolic ref, too:
|
||||
my $type = command_oneline(qw/cat-file -t/, $treeish);
|
||||
my $expected;
|
||||
while ($type eq 'tag') {
|
||||
|
@ -972,7 +1014,7 @@ sub get_tree_from_treeish {
|
|||
if ($type eq 'commit') {
|
||||
$expected = (grep /^tree /, command(qw/cat-file commit/,
|
||||
$treeish))[0];
|
||||
($expected) = ($expected =~ /^tree ($sha1)$/);
|
||||
($expected) = ($expected =~ /^tree ($sha1)$/o);
|
||||
die "Unable to get tree from $treeish\n" unless $expected;
|
||||
} elsif ($type eq 'tree') {
|
||||
$expected = $treeish;
|
||||
|
@ -1034,58 +1076,44 @@ sub get_diff {
|
|||
return \@mods;
|
||||
}
|
||||
|
||||
sub libsvn_checkout_tree {
|
||||
my ($from, $treeish, $ed) = @_;
|
||||
my $mods = get_diff($from, $treeish);
|
||||
return $mods unless (scalar @$mods);
|
||||
my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
|
||||
foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
|
||||
my $f = $m->{chg};
|
||||
if (defined $o{$f}) {
|
||||
$ed->$f($m, $_q);
|
||||
} else {
|
||||
croak "Invalid change type: $f\n";
|
||||
}
|
||||
}
|
||||
$ed->rmdirs($_q) if $_rmdir;
|
||||
return $mods;
|
||||
}
|
||||
sub get_commit_entry {
|
||||
my ($treeish) = shift;
|
||||
my %log_entry = ( log => '', tree => get_tree_from_treeish($treeish) );
|
||||
my $commit_editmsg = "$ENV{GIT_DIR}/COMMIT_EDITMSG";
|
||||
my $commit_msg = "$ENV{GIT_DIR}/COMMIT_MSG";
|
||||
open my $log_fh, '>', $commit_editmsg or croak $!;
|
||||
|
||||
sub get_commit_message {
|
||||
my ($commit, $commit_msg) = (@_);
|
||||
my %log_msg = ( msg => '' );
|
||||
open my $msg, '>', $commit_msg or croak $!;
|
||||
|
||||
my $type = command_oneline(qw/cat-file -t/, $commit);
|
||||
my $type = command_oneline(qw/cat-file -t/, $treeish);
|
||||
if ($type eq 'commit' || $type eq 'tag') {
|
||||
my ($msg_fh, $ctx) = command_output_pipe('cat-file',
|
||||
$type, $commit);
|
||||
$type, $treeish);
|
||||
my $in_msg = 0;
|
||||
while (<$msg_fh>) {
|
||||
if (!$in_msg) {
|
||||
$in_msg = 1 if (/^\s*$/);
|
||||
} elsif (/^git-svn-id: /) {
|
||||
# skip this, we regenerate the correct one
|
||||
# on re-fetch anyways
|
||||
# skip this for now, we regenerate the
|
||||
# correct one on re-fetch anyways
|
||||
# TODO: set *:merge properties or like...
|
||||
} else {
|
||||
print $msg $_ or croak $!;
|
||||
print $log_fh $_ or croak $!;
|
||||
}
|
||||
}
|
||||
command_close_pipe($msg_fh, $ctx);
|
||||
}
|
||||
close $msg or croak $!;
|
||||
close $log_fh or croak $!;
|
||||
|
||||
if ($_edit || ($type eq 'tree')) {
|
||||
my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
|
||||
system($editor, $commit_msg);
|
||||
# TODO: strip out spaces, comments, like git-commit.sh
|
||||
system($editor, $commit_editmsg);
|
||||
}
|
||||
|
||||
# file_to_s removes all trailing newlines, so just use chomp() here:
|
||||
open $msg, '<', $commit_msg or croak $!;
|
||||
{ local $/; chomp($log_msg{msg} = <$msg>); }
|
||||
close $msg or croak $!;
|
||||
|
||||
return \%log_msg;
|
||||
rename $commit_editmsg, $commit_msg or croak $!;
|
||||
open $log_fh, '<', $commit_msg or croak $!;
|
||||
{ local $/; chomp($log_entry{log} = <$log_fh>); }
|
||||
close $log_fh or croak $!;
|
||||
unlink $commit_msg;
|
||||
\%log_entry;
|
||||
}
|
||||
|
||||
sub set_svn_commit_env {
|
||||
|
@ -1150,12 +1178,12 @@ sub assert_revision_unknown {
|
|||
}
|
||||
|
||||
sub git_commit {
|
||||
my ($log_msg, @parents) = @_;
|
||||
assert_revision_unknown($log_msg->{revision});
|
||||
my ($log_entry, @parents) = @_;
|
||||
assert_revision_unknown($log_entry->{revision});
|
||||
map_tree_joins() if (@_branch_from && !%tree_map);
|
||||
|
||||
my (@tmp_parents, @exec_parents, %seen_parent);
|
||||
if (my $lparents = $log_msg->{parents}) {
|
||||
if (my $lparents = $log_entry->{parents}) {
|
||||
@tmp_parents = @$lparents
|
||||
}
|
||||
# commit parents can be conditionally bound to a particular
|
||||
|
@ -1163,14 +1191,14 @@ sub git_commit {
|
|||
foreach my $p (@parents) {
|
||||
next unless defined $p;
|
||||
if ($p =~ /^(\d+)=($sha1_short)$/o) {
|
||||
if ($1 == $log_msg->{revision}) {
|
||||
if ($1 == $log_entry->{revision}) {
|
||||
push @tmp_parents, $2;
|
||||
}
|
||||
} else {
|
||||
push @tmp_parents, $p if $p =~ /$sha1_short/o;
|
||||
}
|
||||
}
|
||||
my $tree = $log_msg->{tree};
|
||||
my $tree = $log_entry->{tree};
|
||||
if (!defined $tree) {
|
||||
my $index = set_index($GIT_SVN_INDEX);
|
||||
$tree = command_oneline('write-tree');
|
||||
|
@ -1197,7 +1225,7 @@ sub git_commit {
|
|||
next if $skip;
|
||||
my ($url_p, $r_p, $uuid_p) = cmt_metadata($p);
|
||||
next if (($SVN->uuid eq $uuid_p) &&
|
||||
($log_msg->{revision} > $r_p));
|
||||
($log_entry->{revision} > $r_p));
|
||||
next if (defined $url_p && defined $SVN_URL &&
|
||||
($SVN->uuid eq $uuid_p) &&
|
||||
($url_p eq $SVN_URL));
|
||||
|
@ -1212,14 +1240,14 @@ sub git_commit {
|
|||
last if @exec_parents > 16;
|
||||
}
|
||||
|
||||
set_commit_env($log_msg);
|
||||
set_commit_env($log_entry);
|
||||
my @exec = ('git-commit-tree', $tree);
|
||||
push @exec, '-p', $_ foreach @exec_parents;
|
||||
defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec))
|
||||
or croak $!;
|
||||
print $msg_fh $log_msg->{msg} or croak $!;
|
||||
print $msg_fh $log_entry->{log} or croak $!;
|
||||
unless ($_no_metadata) {
|
||||
print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_msg->{revision} ",
|
||||
print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_entry->{revision} ",
|
||||
$SVN->uuid,"\n" or croak $!;
|
||||
}
|
||||
$msg_fh->flush == 0 or croak $!;
|
||||
|
@ -1232,10 +1260,10 @@ sub git_commit {
|
|||
die "Failed to commit, invalid sha1: $commit\n";
|
||||
}
|
||||
command_noisy('update-ref',"refs/remotes/$GIT_SVN",$commit);
|
||||
revdb_set($REVDB, $log_msg->{revision}, $commit);
|
||||
revdb_set($REVDB, $log_entry->{revision}, $commit);
|
||||
|
||||
# this output is read via pipe, do not change:
|
||||
print "r$log_msg->{revision} = $commit\n";
|
||||
print "r$log_entry->{revision} = $commit\n";
|
||||
return $commit;
|
||||
}
|
||||
|
||||
|
@ -1248,8 +1276,8 @@ sub check_repack {
|
|||
}
|
||||
|
||||
sub set_commit_env {
|
||||
my ($log_msg) = @_;
|
||||
my $author = $log_msg->{author};
|
||||
my ($log_entry) = @_;
|
||||
my $author = $log_entry->{author};
|
||||
if (!defined $author || length $author == 0) {
|
||||
$author = '(no author)';
|
||||
}
|
||||
|
@ -1257,7 +1285,7 @@ sub set_commit_env {
|
|||
: ($author,$author . '@' . $SVN->uuid);
|
||||
$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
|
||||
$ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email;
|
||||
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_msg->{date};
|
||||
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date};
|
||||
}
|
||||
|
||||
sub check_upgrade_needed {
|
||||
|
@ -1767,14 +1795,14 @@ sub assert_index_clean {
|
|||
}
|
||||
|
||||
sub get_commit_parents {
|
||||
my ($self, $log_msg, @parents) = @_;
|
||||
my ($self, $log_entry, @parents) = @_;
|
||||
my (%seen, @ret, @tmp);
|
||||
# commit parents can be conditionally bound to a particular
|
||||
# svn revision via: "svn_revno=commit_sha1", filter them out here:
|
||||
foreach my $p (@parents) {
|
||||
next unless defined $p;
|
||||
if ($p =~ /^(\d+)=($::sha1_short)$/o) {
|
||||
push @tmp, $2 if $1 == $log_msg->{revision};
|
||||
push @tmp, $2 if $1 == $log_entry->{revision};
|
||||
} else {
|
||||
push @tmp, $p if $p =~ /^$::sha1_short$/o;
|
||||
}
|
||||
|
@ -1782,7 +1810,7 @@ sub get_commit_parents {
|
|||
if (my $cur = ::verify_ref($self->refname.'^0')) {
|
||||
push @tmp, $cur;
|
||||
}
|
||||
push @tmp, $_ foreach (@{$log_msg->{parents}}, @tmp);
|
||||
push @tmp, $_ foreach (@{$log_entry->{parents}}, @tmp);
|
||||
while (my $p = shift @tmp) {
|
||||
next if $seen{$p};
|
||||
$seen{$p} = 1;
|
||||
|
@ -1791,7 +1819,7 @@ sub get_commit_parents {
|
|||
last if @ret >= 16;
|
||||
}
|
||||
if (@tmp) {
|
||||
die "r$log_msg->{revision}: No room for parents:\n\t",
|
||||
die "r$log_entry->{revision}: No room for parents:\n\t",
|
||||
join("\n\t", @tmp), "\n";
|
||||
}
|
||||
@ret;
|
||||
|
@ -1812,17 +1840,18 @@ sub check_upgrade_needed {
|
|||
}
|
||||
|
||||
sub do_git_commit {
|
||||
my ($self, $log_msg, @parents) = @_;
|
||||
if (my $c = $self->rev_db_get($log_msg->{revision})) {
|
||||
croak "$log_msg->{revision} = $c already exists! ",
|
||||
my ($self, $log_entry, @parents) = @_;
|
||||
if (my $c = $self->rev_db_get($log_entry->{revision})) {
|
||||
croak "$log_entry->{revision} = $c already exists! ",
|
||||
"Why are we refetching it?\n";
|
||||
}
|
||||
my ($name, $email) = ::author_name_email($log_msg->{author}, $self->ra);
|
||||
my ($name, $email) = ::author_name_email($log_entry->{author},
|
||||
$self->ra);
|
||||
$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
|
||||
$ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email;
|
||||
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_msg->{date};
|
||||
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date};
|
||||
|
||||
my $tree = $log_msg->{tree};
|
||||
my $tree = $log_entry->{tree};
|
||||
if (!defined $tree) {
|
||||
$tree = $self->tmp_index_do(sub {
|
||||
command_oneline('write-tree') });
|
||||
|
@ -1830,14 +1859,15 @@ sub do_git_commit {
|
|||
die "Tree is not a valid sha1: $tree\n" if $tree !~ /^$::sha1$/o;
|
||||
|
||||
my @exec = ('git-commit-tree', $tree);
|
||||
foreach ($self->get_commit_parents($log_msg, @parents)) {
|
||||
foreach ($self->get_commit_parents($log_entry, @parents)) {
|
||||
push @exec, '-p', $_;
|
||||
}
|
||||
defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec))
|
||||
or croak $!;
|
||||
print $msg_fh $log_msg->{log} or croak $!;
|
||||
print $msg_fh "\ngit-svn-id: $self->{ra}->{url}\@$log_msg->{revision}",
|
||||
" ", $self->ra->uuid,"\n" or croak $!;
|
||||
print $msg_fh $log_entry->{log} or croak $!;
|
||||
print $msg_fh "\ngit-svn-id: ", $self->ra->{url}, '@',
|
||||
$log_entry->{revision}, ' ',
|
||||
$self->ra->uuid, "\n" or croak $!;
|
||||
$msg_fh->flush == 0 or croak $!;
|
||||
close $msg_fh or croak $!;
|
||||
chomp(my $commit = do { local $/; <$out_fh> });
|
||||
|
@ -1849,16 +1879,16 @@ sub do_git_commit {
|
|||
}
|
||||
|
||||
command_noisy('update-ref',$self->refname, $commit);
|
||||
$self->rev_db_set($log_msg->{revision}, $commit);
|
||||
$self->rev_db_set($log_entry->{revision}, $commit);
|
||||
|
||||
$self->{last_rev} = $log_msg->{revision};
|
||||
$self->{last_rev} = $log_entry->{revision};
|
||||
$self->{last_commit} = $commit;
|
||||
print "r$log_msg->{revision} = $commit\n";
|
||||
print "r$log_entry->{revision} = $commit\n";
|
||||
return $commit;
|
||||
}
|
||||
|
||||
sub do_fetch {
|
||||
my ($self, $paths, $rev) = @_; #, $author, $date, $msg) = @_;
|
||||
my ($self, $paths, $rev) = @_; #, $author, $date, $log) = @_;
|
||||
my $ed = SVN::Git::Fetcher->new($self);
|
||||
my ($last_rev, @parents);
|
||||
if ($self->{last_commit}) {
|
||||
|
@ -1958,7 +1988,7 @@ sub fetch {
|
|||
while (1) {
|
||||
my @revs;
|
||||
$self->ra->get_log([''], $min, $max, 0, 1, 1, sub {
|
||||
my ($paths, $rev, $author, $date, $msg) = @_;
|
||||
my ($paths, $rev, $author, $date, $log) = @_;
|
||||
push @revs, $rev });
|
||||
foreach (@revs) {
|
||||
my $log_entry = $self->do_fetch(undef, $_);
|
||||
|
@ -1993,7 +2023,6 @@ sub set_tree {
|
|||
my $pool = SVN::Pool->new;
|
||||
my $ed = SVN::Git::Editor->new({ r => $self->{last_rev},
|
||||
ra => $self->ra->dup,
|
||||
c => $tree,
|
||||
svn_path => $self->ra->{svn_path}
|
||||
},
|
||||
$self->ra->get_commit_editor(
|
||||
|
@ -2226,7 +2255,7 @@ sub uri_decode {
|
|||
}
|
||||
|
||||
sub libsvn_log_entry {
|
||||
my ($rev, $author, $date, $msg, $parents, $untracked) = @_;
|
||||
my ($rev, $author, $date, $log, $parents, $untracked) = @_;
|
||||
my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
|
||||
(\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
|
||||
or die "Unable to parse date: $date\n";
|
||||
|
@ -2234,7 +2263,7 @@ sub libsvn_log_entry {
|
|||
defined $_authors && ! defined $users{$author}) {
|
||||
die "Author: $author not defined in $_authors file\n";
|
||||
}
|
||||
$msg = '' if ($rev == 0 && !defined $msg);
|
||||
$log = '' if ($rev == 0 && !defined $log);
|
||||
|
||||
open my $un, '>>', "$GIT_SVN_DIR/unhandled.log" or croak $!;
|
||||
my $h;
|
||||
|
@ -2290,18 +2319,18 @@ sub libsvn_log_entry {
|
|||
close $un or croak $!;
|
||||
|
||||
{ revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
|
||||
author => $author, msg => $msg."\n", parents => $parents || [],
|
||||
author => $author, log => $log."\n", parents => $parents || [],
|
||||
revprops => $rp }
|
||||
}
|
||||
|
||||
sub libsvn_fetch {
|
||||
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
|
||||
my ($last_commit, $paths, $rev, $author, $date, $log) = @_;
|
||||
my $ed = SVN::Git::Fetcher->new({ c => $last_commit, q => $_q });
|
||||
my (undef, $last_rev, undef) = cmt_metadata($last_commit);
|
||||
unless ($SVN->gs_do_update($last_rev, $rev, '', 1, $ed)) {
|
||||
die "SVN connection failed somewhere...\n";
|
||||
}
|
||||
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ed);
|
||||
libsvn_log_entry($rev, $author, $date, $log, [$last_commit], $ed);
|
||||
}
|
||||
|
||||
sub svn_grab_base_rev {
|
||||
|
@ -2390,7 +2419,7 @@ sub revisions_eq {
|
|||
}
|
||||
|
||||
sub libsvn_find_parent_branch {
|
||||
my ($paths, $rev, $author, $date, $msg) = @_;
|
||||
my ($paths, $rev, $author, $date, $log) = @_;
|
||||
my $svn_path = '/'.$SVN->{svn_path};
|
||||
|
||||
# look for a parent from another branch:
|
||||
|
@ -2442,7 +2471,7 @@ sub libsvn_find_parent_branch {
|
|||
command_noisy('read-tree', $parent);
|
||||
unless ($SVN->can_do_switch) {
|
||||
return _libsvn_new_tree($paths, $rev, $author, $date,
|
||||
$msg, [$parent]);
|
||||
$log, [$parent]);
|
||||
}
|
||||
# do_switch works with svn/trunk >= r22312, but that is not
|
||||
# included with SVN 1.4.2 (the latest version at the moment),
|
||||
|
@ -2451,7 +2480,7 @@ sub libsvn_find_parent_branch {
|
|||
my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q });
|
||||
$ra->gs_do_switch($r0, $rev, '', 1, $SVN->{url}, $ed) or
|
||||
die "SVN connection failed somewhere...\n";
|
||||
return libsvn_log_entry($rev, $author, $date, $msg, [$parent]);
|
||||
return libsvn_log_entry($rev, $author, $date, $log, [$parent]);
|
||||
}
|
||||
print STDERR "Nope, branch point not imported or unknown\n";
|
||||
return undef;
|
||||
|
@ -2461,17 +2490,17 @@ sub libsvn_new_tree {
|
|||
if (my $log_entry = libsvn_find_parent_branch(@_)) {
|
||||
return $log_entry;
|
||||
}
|
||||
my ($paths, $rev, $author, $date, $msg) = @_; # $pool is last
|
||||
_libsvn_new_tree($paths, $rev, $author, $date, $msg, []);
|
||||
my ($paths, $rev, $author, $date, $log) = @_; # $pool is last
|
||||
_libsvn_new_tree($paths, $rev, $author, $date, $log, []);
|
||||
}
|
||||
|
||||
sub _libsvn_new_tree {
|
||||
my ($paths, $rev, $author, $date, $msg, $parents) = @_;
|
||||
my ($paths, $rev, $author, $date, $log, $parents) = @_;
|
||||
my $ed = SVN::Git::Fetcher->new({q => $_q});
|
||||
unless ($SVN->gs_do_update($rev, $rev, '', 1, $ed)) {
|
||||
die "SVN connection failed somewhere...\n";
|
||||
}
|
||||
libsvn_log_entry($rev, $author, $date, $msg, $parents, $ed);
|
||||
libsvn_log_entry($rev, $author, $date, $log, $parents, $ed);
|
||||
}
|
||||
|
||||
sub find_graft_path_commit {
|
||||
|
@ -2536,9 +2565,9 @@ sub restore_index {
|
|||
}
|
||||
|
||||
sub libsvn_commit_cb {
|
||||
my ($rev, $date, $committer, $c, $msg, $r_last, $cmt_last) = @_;
|
||||
my ($rev, $date, $committer, $c, $log, $r_last, $cmt_last) = @_;
|
||||
if ($_optimize_commits && $rev == ($r_last + 1)) {
|
||||
my $log = libsvn_log_entry($rev,$committer,$date,$msg);
|
||||
my $log = libsvn_log_entry($rev,$committer,$date,$log);
|
||||
$log->{tree} = get_tree_from_treeish($c);
|
||||
my $cmt = git_commit($log, $cmt_last, $c);
|
||||
my @diff = command('diff-tree', $cmt, $c);
|
||||
|
@ -2843,7 +2872,7 @@ sub new {
|
|||
my $git_svn = shift;
|
||||
my $self = SVN::Delta::Editor->new(@_);
|
||||
bless $self, $class;
|
||||
foreach (qw/svn_path c r ra /) {
|
||||
foreach (qw/svn_path r ra/) {
|
||||
die "$_ required!\n" unless (defined $git_svn->{$_});
|
||||
$self->{$_} = $git_svn->{$_};
|
||||
}
|
||||
|
@ -2868,7 +2897,7 @@ sub url_path {
|
|||
}
|
||||
|
||||
sub rmdirs {
|
||||
my ($self, $q) = @_;
|
||||
my ($self, $tree_b) = @_;
|
||||
my $rm = $self->{rm};
|
||||
delete $rm->{''}; # we never delete the url we're tracking
|
||||
return unless %$rm;
|
||||
|
@ -2887,7 +2916,7 @@ sub rmdirs {
|
|||
return unless %$rm;
|
||||
|
||||
my ($fh, $ctx) = command_output_pipe(
|
||||
qw/ls-tree --name-only -r -z/, $self->{c});
|
||||
qw/ls-tree --name-only -r -z/, $tree_b);
|
||||
local $/ = "\0";
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
|
@ -2906,7 +2935,7 @@ sub rmdirs {
|
|||
foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {
|
||||
$self->close_directory($bat->{$d}, $p);
|
||||
my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#);
|
||||
print "\tD+\t$d/\n" unless $q;
|
||||
print "\tD+\t$d/\n" unless $::_q;
|
||||
$self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p);
|
||||
delete $bat->{$d};
|
||||
}
|
||||
|
@ -2945,23 +2974,23 @@ sub ensure_path {
|
|||
}
|
||||
|
||||
sub A {
|
||||
my ($self, $m, $q) = @_;
|
||||
my ($self, $m) = @_;
|
||||
my ($dir, $file) = split_path($m->{file_b});
|
||||
my $pbat = $self->ensure_path($dir);
|
||||
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
|
||||
undef, -1);
|
||||
print "\tA\t$m->{file_b}\n" unless $q;
|
||||
print "\tA\t$m->{file_b}\n" unless $::_q;
|
||||
$self->chg_file($fbat, $m);
|
||||
$self->close_file($fbat,undef,$self->{pool});
|
||||
}
|
||||
|
||||
sub C {
|
||||
my ($self, $m, $q) = @_;
|
||||
my ($self, $m) = @_;
|
||||
my ($dir, $file) = split_path($m->{file_b});
|
||||
my $pbat = $self->ensure_path($dir);
|
||||
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
|
||||
$self->url_path($m->{file_a}), $self->{r});
|
||||
print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $q;
|
||||
print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
|
||||
$self->chg_file($fbat, $m);
|
||||
$self->close_file($fbat,undef,$self->{pool});
|
||||
}
|
||||
|
@ -2975,12 +3004,12 @@ sub delete_entry {
|
|||
}
|
||||
|
||||
sub R {
|
||||
my ($self, $m, $q) = @_;
|
||||
my ($self, $m) = @_;
|
||||
my ($dir, $file) = split_path($m->{file_b});
|
||||
my $pbat = $self->ensure_path($dir);
|
||||
my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
|
||||
$self->url_path($m->{file_a}), $self->{r});
|
||||
print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $q;
|
||||
print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
|
||||
$self->chg_file($fbat, $m);
|
||||
$self->close_file($fbat,undef,$self->{pool});
|
||||
|
||||
|
@ -2990,12 +3019,12 @@ sub R {
|
|||
}
|
||||
|
||||
sub M {
|
||||
my ($self, $m, $q) = @_;
|
||||
my ($self, $m) = @_;
|
||||
my ($dir, $file) = split_path($m->{file_b});
|
||||
my $pbat = $self->ensure_path($dir);
|
||||
my $fbat = $self->open_file($self->repo_path($m->{file_b}),
|
||||
$pbat,$self->{r},$self->{pool});
|
||||
print "\t$m->{chg}\t$m->{file_b}\n" unless $q;
|
||||
print "\t$m->{chg}\t$m->{file_b}\n" unless $::_q;
|
||||
$self->chg_file($fbat, $m);
|
||||
$self->close_file($fbat,undef,$self->{pool});
|
||||
}
|
||||
|
@ -3046,10 +3075,10 @@ sub chg_file {
|
|||
}
|
||||
|
||||
sub D {
|
||||
my ($self, $m, $q) = @_;
|
||||
my ($self, $m) = @_;
|
||||
my ($dir, $file) = split_path($m->{file_b});
|
||||
my $pbat = $self->ensure_path($dir);
|
||||
print "\tD\t$m->{file_b}\n" unless $q;
|
||||
print "\tD\t$m->{file_b}\n" unless $::_q;
|
||||
$self->delete_entry($m->{file_b}, $pbat);
|
||||
}
|
||||
|
||||
|
@ -3069,6 +3098,77 @@ sub abort_edit {
|
|||
$self->{pool}->clear;
|
||||
}
|
||||
|
||||
# this drives the editor
|
||||
sub apply_diff {
|
||||
my ($self, $tree_a, $tree_b) = @_;
|
||||
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, $tree_a, $tree_b;
|
||||
my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
|
||||
my $nl = $/;
|
||||
local $/ = "\0";
|
||||
my $state = 'meta';
|
||||
my @mods;
|
||||
while (<$diff_fh>) {
|
||||
chomp $_; # this gets rid of the trailing "\0"
|
||||
if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
|
||||
$::sha1\s($::sha1)\s
|
||||
([MTCRAD])\d*$/xo) {
|
||||
push @mods, { mode_a => $1, mode_b => $2,
|
||||
sha1_b => $3, chg => $4 };
|
||||
if ($4 =~ /^(?:C|R)$/) {
|
||||
$state = 'file_a';
|
||||
} else {
|
||||
$state = 'file_b';
|
||||
}
|
||||
} elsif ($state eq 'file_a') {
|
||||
my $x = $mods[$#mods] or croak "Empty array\n";
|
||||
if ($x->{chg} !~ /^(?:C|R)$/) {
|
||||
croak "Error parsing $_, $x->{chg}\n";
|
||||
}
|
||||
$x->{file_a} = $_;
|
||||
$state = 'file_b';
|
||||
} elsif ($state eq 'file_b') {
|
||||
my $x = $mods[$#mods] or croak "Empty array\n";
|
||||
if (exists $x->{file_a} && $x->{chg} !~ /^(?:C|R)$/) {
|
||||
croak "Error parsing $_, $x->{chg}\n";
|
||||
}
|
||||
if (!exists $x->{file_a} && $x->{chg} =~ /^(?:C|R)$/) {
|
||||
croak "Error parsing $_, $x->{chg}\n";
|
||||
}
|
||||
$x->{file_b} = $_;
|
||||
$state = 'meta';
|
||||
} else {
|
||||
croak "Error parsing $_\n";
|
||||
}
|
||||
}
|
||||
command_close_pipe($diff_fh, $ctx);
|
||||
$/ = $nl;
|
||||
|
||||
my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
|
||||
foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @mods) {
|
||||
my $f = $m->{chg};
|
||||
if (defined $o{$f}) {
|
||||
$self->$f($m);
|
||||
} else {
|
||||
fatal("Invalid change type: $f\n");
|
||||
}
|
||||
}
|
||||
$self->rmdirs($tree_b) if $::_rmdir;
|
||||
if (@mods == 0) {
|
||||
$self->abort_edit;
|
||||
} else {
|
||||
$self->close_edit;
|
||||
}
|
||||
\@mods;
|
||||
}
|
||||
|
||||
package Git::SVN::Ra;
|
||||
use vars qw/@ISA $config_dir/;
|
||||
use strict;
|
||||
|
@ -3144,9 +3244,9 @@ sub get_log {
|
|||
}
|
||||
|
||||
sub get_commit_editor {
|
||||
my ($self, $msg, $cb, $pool) = @_;
|
||||
my ($self, $log, $cb, $pool) = @_;
|
||||
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
|
||||
$self->SUPER::get_commit_editor($msg, $cb, @lock, $pool);
|
||||
$self->SUPER::get_commit_editor($log, $cb, @lock, $pool);
|
||||
}
|
||||
|
||||
sub uuid {
|
||||
|
@ -3211,13 +3311,13 @@ sub cmt_showable {
|
|||
return 1 if defined $c->{r};
|
||||
if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
|
||||
$c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
|
||||
my @msg = command(qw/cat-file commit/, $c->{c});
|
||||
shift @msg while ($msg[0] ne "\n");
|
||||
shift @msg;
|
||||
@{$c->{l}} = grep !/^git-svn-id: /, @msg;
|
||||
my @log = command(qw/cat-file commit/, $c->{c});
|
||||
shift @log while ($log[0] ne "\n");
|
||||
shift @log;
|
||||
@{$c->{l}} = grep !/^git-svn-id: /, @log;
|
||||
|
||||
(undef, $c->{r}, undef) = ::extract_metadata(
|
||||
(grep(/^git-svn-id: /, @msg))[-1]);
|
||||
(grep(/^git-svn-id: /, @log))[-1]);
|
||||
}
|
||||
return defined $c->{r};
|
||||
}
|
||||
|
@ -3503,9 +3603,9 @@ __END__
|
|||
|
||||
Data structures:
|
||||
|
||||
$log_msg hashref as returned by libsvn_log_entry()
|
||||
$log_entry hashref as returned by libsvn_log_entry()
|
||||
{
|
||||
msg => 'whitespace-formatted log entry
|
||||
log => 'whitespace-formatted log entry
|
||||
', # trailing newline is preserved
|
||||
revision => '8', # integer
|
||||
date => '2004-02-24T17:01:44.108345Z', # commit date
|
||||
|
|
Загрузка…
Ссылка в новой задаче