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>
Sam Vilain committed
Dec 20, 2009 at 05:26 UTC
7a955a5365d9ebd5e12c12ed926b2b51b61c02ee
2 files changed
+66
-25
git-svn.perl
+64
-23
@@ -3034,8 +3034,35 @@ sub lookup_svn_merge {
3034
}
3035
return ($tip_commit, @merged_commit_ranges);
3036
}
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
+
3063
BEGIN {
3064
memoize 'lookup_svn_merge';
3065
+ memoize 'check_cherry_pick';
3066
}
3067
3068
sub parents_exclude {
@@ -3111,32 +3138,46 @@ sub find_extra_svn_parents {
3138
3139
my $ranges = $ranges{$merge_tip};
3140
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
+ }
3177
}
3178
}
3179
}
3180
+ push @$parents, grep { defined } @new_parents;
3181
}
3182
3183
sub make_log_entry {
t/t9151-svn-mergeinfo.sh
+2
-2
@@ -15,13 +15,13 @@ test_expect_success 'load svn dump' "
15
git svn fetch --all
16
"
17
18
-test_expect_failure 'all svn merges became git merge commits' '
18
+test_expect_success 'all svn merges became git merge commits' '
19
unmarked=$(git rev-list --parents --all --grep=Merge |
20
grep -v " .* " | cut -f1 -d" ")
21
[ -z "$unmarked" ]
22
'
23
24
-test_expect_failure 'cherry picks did not become git merge commits' '
24
+test_expect_success 'cherry picks did not become git merge commits' '
25
bad_cherries=$(git rev-list --parents --all --grep=Cherry |
26
grep " .* " | cut -f1 -d" ")
27
[ -z "$bad_cherries" ]