--- /dev/null
+#!/usr/bin/perl
+# usage: tb-update [options]
+
+use warnings;
+use strict;
+
+use Getopt::Long;
+use Topbloke;
+
+Getopt::Long::Configure(qw(bundling));
+
+die "bad usage\n" if @ARGV;
+
+check_clean_tree();
+
+
+sub update_base ($) {
+ my ($patch) = @_;
+
+ for (;;) {
+ check_baseref_metadata("$baserefs/$patch");
+
+ my $head = git_run_1line(qw(rev-parse),"$baserefs/$patch");
+
+ # 2.i. Compute set of desired included deps
+ my %desired;
+ my $add_desired = sub {
+ my ($sub) = @_;
+ my ($obk,$deps) = git_get_object("$baserefs/$sub:.topbloke/deps");
+ die "$sub $obk ??" unless $obk eq 'blob';
+ foreach my $depline (split /\n/, $deps) {
+ next if exists $desired{$depline};
+ $desired{$depline} = 1;
+ if ($depline =~ m/^- /) {
+ } elsif ($depline =~ m/^-/) {
+ die "$depline ?";
+ } else {
+ check_baseref_metadata("$baserefs/$depline");
+ $add_desired->($depline);
+ }
+ }
+ };
+ $add_desired->($patch);
+
+ # 2.ii. do the merges
+ # first, find the list of sources
+
+ my @sources;
+ my ($obk,$deps) = git_get_object("$baserefs/$patch:.topbloke/deps");
+ die "$patch $obk ??" unless $obk eq 'blob';
+ foreach my $depline (split /\n/, $deps) {
+ if ($depline =~ m/^- /) {
+ push @sources, { Ref => "$'", Kind => 'foreign' };
+ } elsif ($depline =~ m/^-/) {
+ die "$depline ?"; # should have failed earlier
+ } else {
+ push @sources, { Ref => "$tiprefs/$depline", Kind => 'tb' };
+ }
+ }
+
+ my ($obk,$tg) = git_get_object("$baserefs/$patch:.topbloke/topgit-");
+ if ($obk ne 'missing') {
+ $obj eq 'blob' or die "$patch $obk ??";
+ chomp $tg or die "$patch ??";
+ push @sources, { Ref => "refs/top-bases/$tg", Kind => 'topgit' };
+ }
+
+ # This bit involves rather too much history walking
+ # and could perhaps be optimised.
+
+ # Find the merge base for each source
+ foreach my $source (@sources) {
+ $source->{Head} = run_git_1line(qw(rev-parse), $source->{Ref});
+ $source->{MergeBase} =
+ run_git_1line(qw(merge-base), $head, "$baserefs/$patch");
+ }
+ # The merge base is contained in $head, so if it is equal
+ # to the source's head, the source is contained in $head -
+ # ie we are ahead of the source. Skip those sources.
+ @sources = grep { $source->{MergeBase} ne $source->{Head} } @sources;
+
+ # 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) {
+
+
+
+ $source->{MergeBase} = $mergebase;
+
+ bad_metadata("$patch base topgit $obj") unless $obj eq 'blob';
+
+ # ok
+ } elsif ($obk
+
+
+sub done ($) {
+ our %done;
+ return 1 if $done{$_[0]}++;
+ debug("----- $key");
+}
+
+sub memo ($$) {
+ my ($key,$code) = @_;
+ our %memo;
+ return $memo{$key} if exists $memo{$key};
+ debug("----- $key");
+ $memo{$key} = $code->();
+}
+
+sub tip_sources_core
+
+sub tip_sources ($) {
+ my ($patch) = @_;
+ my @sources = ();
+ push @sources, { How => 'base', Ref => "$baserefs/$patch" };
+ foreach my $remote (get_configured_remotes()) {
+ push @sources, { How => 'remote',
+ Ref => "refs/remotes/$remote/topbloke-tips/$patch" };
+ }
+
+sub compute_desired_deps ($) {
+ my ($patch) = @_;
+ return memo("compute_desired_deps $patch", {
+ foreach my $sourceref (tip_source_refs($patch)) {
+ die...
+ });
+}
+
+sub update_base ($) {
+ my ($patch) = @_;
+ return if done("update_base $patch");
+ my @desired_deps = compute_desired_deps($patch);
+ die...
+
+sub update_deps ($) {
+ my $patch = @_;
+ return if done("update_deps $patch");
+ my $deps = git_get_object("$baserefs/$patch:.topbloke/deps");
+ foreach my $dep (split /\n/, $deps) {
+ if ($dep =~ m/^tb /) {
+ my $dep_patch = $'; #';
+ update_patch($dep_patch);
+ }
+ }
+}
+
+sub update_patch ($) {
+ my $patch = @_;
+ update_deps($patch);
+ update_base($patch);
+ update_tip($patch);
+}
+
+our $current = current_branch();
+if ($current->{Kind} eq 'tip') {
+ update_patch($current);
+} elsif ($current->{Kind} eq 'base') {
+ update_deps($current);
+ update_base($current);
+} else {
+ die "Not a topbloke branch ($current->{Kind})\n";
+}