Skip to content

Commit 7a955a5

Browse files
samvEric Wong
authored andcommitted
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 <[email protected]> Acked-by: Eric Wong <[email protected]>
1 parent ea020cb commit 7a955a5

File tree

2 files changed

+66
-25
lines changed

2 files changed

+66
-25
lines changed

git-svn.perl

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3034,8 +3034,35 @@ sub lookup_svn_merge {
30343034
}
30353035
return ($tip_commit, @merged_commit_ranges);
30363036
}
3037+
3038+
sub _rev_list {
3039+
my ($msg_fh, $ctx) = command_output_pipe(
3040+
"rev-list", @_,
3041+
);
3042+
my @rv;
3043+
while ( <$msg_fh> ) {
3044+
chomp;
3045+
push @rv, $_;
3046+
}
3047+
command_close_pipe($msg_fh, $ctx);
3048+
@rv;
3049+
}
3050+
3051+
sub check_cherry_pick {
3052+
my $base = shift;
3053+
my $tip = shift;
3054+
my @ranges = @_;
3055+
my %commits = map { $_ => 1 }
3056+
_rev_list("--no-merges", $tip, "--not", $base);
3057+
for my $range ( @ranges ) {
3058+
delete @commits{_rev_list($range)};
3059+
}
3060+
return (keys %commits);
3061+
}
3062+
30373063
BEGIN {
30383064
memoize 'lookup_svn_merge';
3065+
memoize 'check_cherry_pick';
30393066
}
30403067

30413068
sub parents_exclude {
@@ -3111,32 +3138,46 @@ sub find_extra_svn_parents {
31113138

31123139
my $ranges = $ranges{$merge_tip};
31133140

3114-
my @cmd = ('rev-list', "-1", $merge_tip,
3115-
"--not", @$parents );
3116-
my ($msg_fh, $ctx) = command_output_pipe(@cmd);
3117-
my $new;
3118-
while ( <$msg_fh> ) {
3119-
$new=1;last;
3120-
}
3121-
command_close_pipe($msg_fh, $ctx);
3122-
if ( $new ) {
3123-
push @cmd, @$ranges;
3124-
my ($msg_fh, $ctx) = command_output_pipe(@cmd);
3125-
my $unmerged;
3126-
while ( <$msg_fh> ) {
3127-
$unmerged=1;last;
3128-
}
3129-
command_close_pipe($msg_fh, $ctx);
3130-
if ( $unmerged ) {
3131-
warn "W:svn cherry-pick ignored ($spec)\n";
3132-
} else {
3133-
warn
3134-
"Found merge parent (svn:mergeinfo prop): ",
3135-
$merge_tip, "\n";
3136-
push @$parents, $merge_tip;
3141+
# check out 'new' tips
3142+
my $merge_base = command_oneline(
3143+
"merge-base",
3144+
@$parents, $merge_tip,
3145+
);
3146+
3147+
# double check that there are no missing non-merge commits
3148+
my (@incomplete) = check_cherry_pick(
3149+
$merge_base, $merge_tip,
3150+
@$ranges,
3151+
);
3152+
3153+
if ( @incomplete ) {
3154+
warn "W:svn cherry-pick ignored ($spec) - missing "
3155+
.@incomplete." commit(s) (eg $incomplete[0])\n";
3156+
} else {
3157+
warn
3158+
"Found merge parent (svn:mergeinfo prop): ",
3159+
$merge_tip, "\n";
3160+
push @new_parents, $merge_tip;
3161+
}
3162+
}
3163+
3164+
# cater for merges which merge commits from multiple branches
3165+
if ( @new_parents > 1 ) {
3166+
for ( my $i = 0; $i <= $#new_parents; $i++ ) {
3167+
for ( my $j = 0; $j <= $#new_parents; $j++ ) {
3168+
next if $i == $j;
3169+
next unless $new_parents[$i];
3170+
next unless $new_parents[$j];
3171+
my $revs = command_oneline(
3172+
"rev-list", "-1", "$i..$j",
3173+
);
3174+
if ( !$revs ) {
3175+
undef($new_parents[$i]);
3176+
}
31373177
}
31383178
}
31393179
}
3180+
push @$parents, grep { defined } @new_parents;
31403181
}
31413182

31423183
sub make_log_entry {

t/t9151-svn-mergeinfo.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ test_expect_success 'load svn dump' "
1515
git svn fetch --all
1616
"
1717

18-
test_expect_failure 'all svn merges became git merge commits' '
18+
test_expect_success 'all svn merges became git merge commits' '
1919
unmarked=$(git rev-list --parents --all --grep=Merge |
2020
grep -v " .* " | cut -f1 -d" ")
2121
[ -z "$unmarked" ]
2222
'
2323

24-
test_expect_failure 'cherry picks did not become git merge commits' '
24+
test_expect_success 'cherry picks did not become git merge commits' '
2525
bad_cherries=$(git rev-list --parents --all --grep=Cherry |
2626
grep " .* " | cut -f1 -d" ")
2727
[ -z "$bad_cherries" ]

0 commit comments

Comments
 (0)