chiark / gitweb /
Move old update algorith, which is very wrong according to new THEORY, into its own...
[topbloke.git] / tb-update.pl
index 18acbc5..58e7d60 100755 (executable)
@@ -29,13 +29,25 @@ sub merge_base ($$) {
                });
 }
 
-sub committ_date ($) {
+sub commit_date ($) {
     my ($ref) = @_;
     my $l = run_git_1line(qw(git-log --date=raw -n1 --pretty=format:%cd), $ref);
     $l =~ m/^(\d+)\s/ or die;
     return $l;
 }
 
+sub compare_source_ages ($$) {
+    my ($r,$s) = @_; # refs, returns something like  age($r) cmp age($s)
+    our %memos;
+    return memo(\%memos, "$r $s", sub {
+       my $mb = merge_base($r, $s);
+       return -($mb eq $r) cmp ($mb eq $s)
+           # if merge base is $a then $a must be before $b
+           # ie the commit equal to the merge base is earlier
+           or (commit_date($r) cmp commit_date($s));
+    });
+}
+
 sub update_base ($) {
     my ($patch) = @_;
 
@@ -79,7 +91,7 @@ sub update_base ($) {
                push @sources, { 
                    Name => $depline,
                    Ref => "$tiprefs/$depline", 
-                   Kind => 'tb',
+                   Kind => 'topbloke',
                };
            }
        }
@@ -108,31 +120,82 @@ sub update_base ($) {
        # ie we are ahead of the source.  Skip those sources.
        @sources = grep { $source->{MergeBase} ne $source->{Head} } @sources;
 
-       our %cmp_memos;
-       @sources = sort { 
-           memo(\%cmp_memos, "$a->{Name} $b->{Name}", sub {
-               my $mb = merge_base($a->{Ref}, $b->{Ref});
-               return -($mb eq $a->{Ref}) cmp ($mb eq $b->{Ref})
-                   # if merge base is $a then $a must be before $b
-                   # ie the commit equal to the merge base is earlier
-                   or (committ_date($a->{Ref}) cmp committ_date($b->{Ref}));
-           })
-       } @sources;
-       fixme we do not need to sort, only find best, and then do again
-
-       # Now we run git-rev-list to walk the graph back to those
-       # sources so we can tell which is the most recent.
-       foreach my $source (@sources) {
-           
+       if (!@sources) {
+           print "$patch base is up to date\n" or die $!;
+           last;
+       }
 
-    
-           $source->{MergeBase} = $mergebase;
-           
-           bad_metadata("$patch base topgit $obj") unless $obj eq 'blob';
-           
-           # ok
-       } elsif ($obk 
+       my $best = $sources[0];
+       foreach my $source (@sources[1..$#sources]) {
+           next if compare_source_ages($best->{Ref}, $source->{Ref}) <= 0;
+           $best = $source;
+       }
+
+       my $sref = $source->{Ref};
+
+       if ($source->{Kind} eq 'topbloke') {
+           # Check for unwanted dependency removals
+           my (%source_inc,%anc_inc);
+           $source_inc{$_}=1 foreach split /\n/, 
+               git_get_object("$sref:.topbloke/+included");
+           $anc_inc{$_}=1 foreach split /\n/, 
+               git_get_object("$source->{MergeBase}:.topbloke/+included");
+           my @unwanted_dr;
+           foreach my $dep (keys %desired) {
+               next if $source_inc{$dep};
+               unless unless $anc_inc{$dep};
+               my $unw_dr = { Name => $dep };
+
+
+               # Algorithm
+               # We do a history graph walk.
+               # In each iteration we get git-rev-list to find us
+               # one commit.
 
+               # We get git-rev-list to find us 
+ send us a series of commits
+               # We look up each one.
+               my @prune;
+               my $pruned = sub {
+                   my ($commit) = @_;
+                   return grep { commit_has_ancestor($_, $cand) } @prune;
+               };
+               my $prune = sub {
+                   my ($commit) = @_;
+                   return if $pruned->($commit);
+                   push @prune, $commit;
+               };
+               run_git(sub {
+                   my ($cand, @parents) = split;
+                   if (dep_included_in($dep, $cand)) {
+                       $prune->($cand);
+                       return;
+                   }
+                   my @parents_with =
+                       grep { dep_included_in($dep, $_) } @parents;
+                   return if !@parents_with; # irrelevant merge
+                   return if $pruned->($cand); # not interesting any more
+                   $prune->($_) foreach @parents_with;
+                   
+
+                   PROBLEM @prune is bad we want to know why
+                       we have found thing not just whether found
+                   
+                       # 
+                   return if dep_included_in($dep, $cand);
+                   return if 
+                   # OK, it's missing from $cand but included in
+                   # all of $cand's parents.
+                   
+                   },
+                       qw(git-rev-list --date-order --full-history 
+                           --remove-empty)
+                       '--pretty=format:%H %P%n',
+                       $dep, '--', '.topbloke/+included');
+               
+               
+               push @unwanted_dr, { Name => $dep };
+           
 
 sub done ($) { 
     our %done;