@matheusbsbs / matheus-bagliano / commits / 7a955a5365
Fork 0

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" ]