2 # usage: tb-update [options]
10 Getopt::Long::Configure(qw(bundling));
12 die "bad usage\n" if @ARGV;
18 my ($memos,$key,$code) = @_;
19 return $memos->{$key} if exists $memos->{$key};
21 $memos->{$key} = $code->();
25 my ($r,$s) = @_; # refs, ideally
27 return memo(\%memos, "$r $s", sub {
28 run_git_1line(qw(merge-base), $r, $s);
32 sub committ_date ($) {
34 my $l = run_git_1line(qw(git-log --date=raw -n1 --pretty=format:%cd), $ref);
35 $l =~ m/^(\d+)\s/ or die;
43 check_baseref_metadata("$baserefs/$patch");
45 my $head = git_run_1line(qw(rev-parse),"$baserefs/$patch");
47 # 2.i. Compute set of desired included deps
49 my $add_desired = sub {
51 my ($obk,$deps) = git_get_object("$baserefs/$sub:.topbloke/deps");
52 die "$sub $obk ??" unless $obk eq 'blob';
53 foreach my $depline (split /\n/, $deps) {
54 next if exists $desired{$depline};
55 $desired{$depline} = 1;
56 if ($depline =~ m/^- /) {
57 } elsif ($depline =~ m/^-/) {
60 check_baseref_metadata("$baserefs/$depline");
61 $add_desired->($depline);
65 $add_desired->($patch);
68 # first, find the list of sources
71 my ($obk,$deps) = git_get_object("$baserefs/$patch:.topbloke/deps");
72 die "$patch $obk ??" unless $obk eq 'blob';
73 foreach my $depline (split /\n/, $deps) {
74 if ($depline =~ m/^- /) {
75 push @sources, { Ref => "$'", Kind => 'foreign' };
76 } elsif ($depline =~ m/^-/) {
77 die "$depline ?"; # should have failed earlier
81 Ref => "$tiprefs/$depline",
87 my ($obk,$tg) = git_get_object("$baserefs/$patch:.topbloke/topgit-");
88 if ($obk ne 'missing') {
89 $obj eq 'blob' or die "$patch $obk ??";
90 chomp $tg or die "$patch ??";
92 Name => "-topgit $tg",
93 Ref => "refs/top-bases/$tg",
98 # This bit involves rather too much history walking
99 # and could perhaps be optimised.
101 # Find the merge base for each source
102 foreach my $source (@sources) {
103 $source->{Head} = run_git_1line(qw(rev-parse), $source->{Ref});
104 $source->{MergeBase} = merge_base($head, "$baserefs/$patch");
106 # The merge base is contained in $head, so if it is equal
107 # to the source's head, the source is contained in $head -
108 # ie we are ahead of the source. Skip those sources.
109 @sources = grep { $source->{MergeBase} ne $source->{Head} } @sources;
113 memo(\%cmp_memos, "$a->{Name} $b->{Name}", sub {
114 my $mb = merge_base($a->{Ref}, $b->{Ref});
115 return -($mb eq $a->{Ref}) cmp ($mb eq $b->{Ref})
116 # if merge base is $a then $a must be before $b
117 # ie the commit equal to the merge base is earlier
118 or (committ_date($a->{Ref}) cmp committ_date($b->{Ref}));
121 fixme we do not need to sort, only find best, and then do again
123 # Now we run git-rev-list to walk the graph back to those
124 # sources so we can tell which is the most recent.
125 foreach my $source (@sources) {
129 $source->{MergeBase} = $mergebase;
131 bad_metadata("$patch base topgit $obj") unless $obj eq 'blob';
139 return 1 if $done{$_[0]}++;
144 my ($key,$code) = @_;
146 return $memo{$key} if exists $memo{$key};
148 $memo{$key} = $code->();
153 sub tip_sources ($) {
156 push @sources, { How => 'base', Ref => "$baserefs/$patch" };
157 foreach my $remote (get_configured_remotes()) {
158 push @sources, { How => 'remote',
159 Ref => "refs/remotes/$remote/topbloke-tips/$patch" };
162 sub compute_desired_deps ($) {
164 return memo("compute_desired_deps $patch", {
165 foreach my $sourceref (tip_source_refs($patch)) {
170 sub update_base ($) {
172 return if done("update_base $patch");
173 my @desired_deps = compute_desired_deps($patch);
176 sub update_deps ($) {
178 return if done("update_deps $patch");
179 my $deps = git_get_object("$baserefs/$patch:.topbloke/deps");
180 foreach my $dep (split /\n/, $deps) {
181 if ($dep =~ m/^tb /) {
182 my $dep_patch = $'; #';
183 update_patch($dep_patch);
188 sub update_patch ($) {
195 our $current = current_branch();
196 if ($current->{Kind} eq 'tip') {
197 update_patch($current);
198 } elsif ($current->{Kind} eq 'base') {
199 update_deps($current);
200 update_base($current);
202 die "Not a topbloke branch ($current->{Kind})\n";