git-svn: detect cherry-picks correctly.

The old function was incorrect; in some instances it marks a cherry picked
range as a merged branch (because of an incorrect assumption that
'rev-list COMMIT --not RANGE' would work).  This is replaced with a
function which should detect them correctly, memoized to limit the expense
of dealing with branches with many cherry picks to one 'merge-base' call
per merge, per branch which used cherry picking.

Signed-off-by: Sam Vilain <sam@vilain.net>
Acked-by: Eric Wong <normalperson@yhbt.net>
This commit is contained in:
Sam Vilain 2009-12-20 05:26:26 +13:00 committed by Eric Wong
parent ea020cbd6a
commit 7a955a5365
2 changed files with 65 additions and 24 deletions

View File

@ -3034,8 +3034,35 @@ sub lookup_svn_merge {
} }
return ($tip_commit, @merged_commit_ranges); return ($tip_commit, @merged_commit_ranges);
} }
sub _rev_list {
my ($msg_fh, $ctx) = command_output_pipe(
"rev-list", @_,
);
my @rv;
while ( <$msg_fh> ) {
chomp;
push @rv, $_;
}
command_close_pipe($msg_fh, $ctx);
@rv;
}
sub check_cherry_pick {
my $base = shift;
my $tip = shift;
my @ranges = @_;
my %commits = map { $_ => 1 }
_rev_list("--no-merges", $tip, "--not", $base);
for my $range ( @ranges ) {
delete @commits{_rev_list($range)};
}
return (keys %commits);
}
BEGIN { BEGIN {
memoize 'lookup_svn_merge'; memoize 'lookup_svn_merge';
memoize 'check_cherry_pick';
} }
sub parents_exclude { sub parents_exclude {
@ -3111,32 +3138,46 @@ sub find_extra_svn_parents {
my $ranges = $ranges{$merge_tip}; my $ranges = $ranges{$merge_tip};
my @cmd = ('rev-list', "-1", $merge_tip, # check out 'new' tips
"--not", @$parents ); my $merge_base = command_oneline(
my ($msg_fh, $ctx) = command_output_pipe(@cmd); "merge-base",
my $new; @$parents, $merge_tip,
while ( <$msg_fh> ) { );
$new=1;last;
# double check that there are no missing non-merge commits
my (@incomplete) = check_cherry_pick(
$merge_base, $merge_tip,
@$ranges,
);
if ( @incomplete ) {
warn "W:svn cherry-pick ignored ($spec) - missing "
.@incomplete." commit(s) (eg $incomplete[0])\n";
} else {
warn
"Found merge parent (svn:mergeinfo prop): ",
$merge_tip, "\n";
push @new_parents, $merge_tip;
} }
command_close_pipe($msg_fh, $ctx); }
if ( $new ) {
push @cmd, @$ranges; # cater for merges which merge commits from multiple branches
my ($msg_fh, $ctx) = command_output_pipe(@cmd); if ( @new_parents > 1 ) {
my $unmerged; for ( my $i = 0; $i <= $#new_parents; $i++ ) {
while ( <$msg_fh> ) { for ( my $j = 0; $j <= $#new_parents; $j++ ) {
$unmerged=1;last; next if $i == $j;
} next unless $new_parents[$i];
command_close_pipe($msg_fh, $ctx); next unless $new_parents[$j];
if ( $unmerged ) { my $revs = command_oneline(
warn "W:svn cherry-pick ignored ($spec)\n"; "rev-list", "-1", "$i..$j",
} else { );
warn if ( !$revs ) {
"Found merge parent (svn:mergeinfo prop): ", undef($new_parents[$i]);
$merge_tip, "\n"; }
push @$parents, $merge_tip;
} }
} }
} }
push @$parents, grep { defined } @new_parents;
} }
sub make_log_entry { sub make_log_entry {

View File

@ -15,13 +15,13 @@ test_expect_success 'load svn dump' "
git svn fetch --all git svn fetch --all
" "
test_expect_failure 'all svn merges became git merge commits' ' test_expect_success 'all svn merges became git merge commits' '
unmarked=$(git rev-list --parents --all --grep=Merge | unmarked=$(git rev-list --parents --all --grep=Merge |
grep -v " .* " | cut -f1 -d" ") grep -v " .* " | cut -f1 -d" ")
[ -z "$unmarked" ] [ -z "$unmarked" ]
' '
test_expect_failure 'cherry picks did not become git merge commits' ' test_expect_success 'cherry picks did not become git merge commits' '
bad_cherries=$(git rev-list --parents --all --grep=Cherry | bad_cherries=$(git rev-list --parents --all --grep=Cherry |
grep " .* " | cut -f1 -d" ") grep " .* " | cut -f1 -d" ")
[ -z "$bad_cherries" ] [ -z "$bad_cherries" ]