git-svn: exclude already merged tips using one rev-list call
authorSam Vilain <sam@vilain.net>
Sat, 19 Dec 2009 16:25:31 +0000 (05:25 +1300)
committerEric Wong <normalperson@yhbt.net>
Mon, 21 Dec 2009 10:32:52 +0000 (02:32 -0800)
The old function would have to check all mentioned merge tips, every time
that the mergeinfo ticket changed.  This involved 1-2 rev-list operation
for each listed mergeinfo line.  If there are a lot of feature branches
being merged into a trunk, this makes for a very expensive operation for
detecting the new parents on every merge.

This new version first uses a single 'rev-list' to figure out which commit
ranges are already reachable from the parents.  This is used to eliminate
the already merged branches from the list.

Signed-off-by: Sam Vilain <sam@vilain.net>
Acked-by: Eric Wong <normalperson@yhbt.net>
git-svn.perl

index a4a45ef3986453571f3063dfd6aee75c7c127744..07d40ba81fa1227733281262c61633845de84a5b 100755 (executable)
@@ -3038,6 +3038,41 @@ BEGIN {
        memoize 'lookup_svn_merge';
 }
 
+sub parents_exclude {
+       my $parents = shift;
+       my @commits = @_;
+       return unless @commits;
+
+       my @excluded;
+       my $excluded;
+       do {
+               my @cmd = ('rev-list', "-1", @commits, "--not", @$parents );
+               $excluded = command_oneline(@cmd);
+               if ( $excluded ) {
+                       my @new;
+                       my $found;
+                       for my $commit ( @commits ) {
+                               if ( $commit eq $excluded ) {
+                                       push @excluded, $commit;
+                                       $found++;
+                                       last;
+                               }
+                               else {
+                                       push @new, $commit;
+                               }
+                       }
+                       die "saw commit '$excluded' in rev-list output, "
+                               ."but we didn't ask for that commit (wanted: @commits --not @$parents)"
+                                       unless $found;
+                       @commits = @new;
+               }
+       }
+               while ($excluded and @commits);
+
+       return @excluded;
+}
+
+
 # note: this function should only be called if the various dirprops
 # have actually changed
 sub find_extra_svn_parents {
@@ -3050,23 +3085,32 @@ sub find_extra_svn_parents {
        # are now marked as merge, we can add the tip as a parent.
        my @merges = split "\n", $mergeinfo;
        my @merge_tips;
-       my @merged_commit_ranges;
        my $url = $self->rewrite_root || $self->{url};
        my $uuid = $self->ra_uuid;
+       my %ranges;
        for my $merge ( @merges ) {
                my ($tip_commit, @ranges) =
                        lookup_svn_merge( $uuid, $url, $merge );
-               push @merged_commit_ranges, @ranges;
                unless (!$tip_commit or
                                grep { $_ eq $tip_commit } @$parents ) {
                        push @merge_tips, $tip_commit;
+                       $ranges{$tip_commit} = \@ranges;
                } else {
                        push @merge_tips, undef;
                }
        }
+
+       my %excluded = map { $_ => 1 }
+               parents_exclude($parents, grep { defined } @merge_tips);
+
+       # check merge tips for new parents
+       my @new_parents;
        for my $merge_tip ( @merge_tips ) {
                my $spec = shift @merges;
-               next unless $merge_tip;
+               next unless $merge_tip and $excluded{$merge_tip};
+
+               my $ranges = $ranges{$merge_tip};
+
                my @cmd = ('rev-list', "-1", $merge_tip,
                           "--not", @$parents );
                my ($msg_fh, $ctx) = command_output_pipe(@cmd);
@@ -3076,7 +3120,7 @@ sub find_extra_svn_parents {
                }
                command_close_pipe($msg_fh, $ctx);
                if ( $new ) {
-                       push @cmd, @merged_commit_ranges;
+                       push @cmd, @$ranges;
                        my ($msg_fh, $ctx) = command_output_pipe(@cmd);
                        my $unmerged;
                        while ( <$msg_fh> ) {