From 733a65aa5d33196fac708ebd12a98a1060cbf3c2 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 13 Jun 2007 02:23:28 -0700 Subject: git-svn: allow dcommit to retain local merge information dcommit will still rewrite the HEAD commit and the history of the first parents of each HEAD~1, HEAD~2, HEAD~3 as it always has. However, any merge parents (HEAD^2, HEAD^^2, HEAD~2^2) will now be preserved when the new HEAD and HEAD~[0-9]+ commits are rewritten to SVN with dcommit. Commits written to SVN will still not have any merge information besides anything in the commit message. Thanks to Joakim Tjernlund, Junio C Hamano and Steven Grimm for explanations, feedback, examples and test case. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 9 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 0ae8d70de..42906767a 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -372,16 +372,9 @@ sub cmd_dcommit { die "Unable to determine upstream SVN information from ", "$head history\n"; } - my $c = $refs[-1]; my $last_rev; - foreach my $d (@refs) { - if (!verify_ref("$d~1")) { - fatal "Commit $d\n", - "has no parent commit, and therefore ", - "nothing to diff against.\n", - "You should be working from a repository ", - "originally created by git-svn\n"; - } + my ($linear_refs, $parents) = linearize_history($gs, \@refs); + foreach my $d (@$linear_refs) { unless (defined $last_rev) { (undef, $last_rev, undef) = cmt_metadata("$d~1"); unless (defined $last_rev) { @@ -403,6 +396,9 @@ sub cmd_dcommit { svn_path => ''); if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) { print "No changes\n$d~1 == $d\n"; + } elsif ($parents->{$d} && @{$parents->{$d}}) { + $gs->{inject_parents_dcommit}->{$last_rev} = + $parents->{$d}; } } } @@ -821,6 +817,59 @@ sub working_head_info { (undef, undef, undef, undef); } +sub read_commit_parents { + my ($parents, $c) = @_; + my ($fh, $ctx) = command_output_pipe(qw/cat-file commit/, $c); + while (<$fh>) { + chomp; + last if ''; + /^parent ($sha1)/ or next; + push @{$parents->{$c}}, $1; + } + close $fh; # break the pipe +} + +sub linearize_history { + my ($gs, $refs) = @_; + my %parents; + foreach my $c (@$refs) { + read_commit_parents(\%parents, $c); + } + + my @linear_refs; + my %skip = (); + my $last_svn_commit = $gs->last_commit; + foreach my $c (reverse @$refs) { + next if $c eq $last_svn_commit; + last if $skip{$c}; + + unshift @linear_refs, $c; + $skip{$c} = 1; + + # we only want the first parent to diff against for linear + # history, we save the rest to inject when we finalize the + # svn commit + my $fp_a = verify_ref("$c~1"); + my $fp_b = shift @{$parents{$c}} if $parents{$c}; + if (!$fp_a || !$fp_b) { + die "Commit $c\n", + "has no parent commit, and therefore ", + "nothing to diff against.\n", + "You should be working from a repository ", + "originally created by git-svn\n"; + } + if ($fp_a ne $fp_b) { + die "$c~1 = $fp_a, however parsing commit $c ", + "revealed that:\n$c~1 = $fp_b\nBUG!\n"; + } + + foreach my $p (@{$parents{$c}}) { + $skip{$p} = 1; + } + } + (\@linear_refs, \%parents); +} + package Git::SVN; use strict; use warnings; @@ -1541,6 +1590,11 @@ sub get_commit_parents { if (my $cur = ::verify_ref($self->refname.'^0')) { push @tmp, $cur; } + if (my $ipd = $self->{inject_parents_dcommit}) { + if (my $commit = delete $ipd->{$log_entry->{revision}}) { + push @tmp, @$commit; + } + } push @tmp, $_ foreach (@{$log_entry->{parents}}, @tmp); while (my $p = shift @tmp) { next if $seen{$p}; -- cgit v1.2.1