Meta/cook: What's cooking script (third generation)

Junio C Hamano committed Apr 12, 2010 at 14:29 UTC 2f557677f429c2bff9c7b57a5e6a37d00534b520
2 files changed +548 -449
cook new
+548
@@ -0,0 +1,548 @@
1 +#!/usr/bin/perl -w
2 +# Maintain "what's cooking" messages
3 +
4 +use strict;
5 +
6 +sub phrase_these {
7 + my (@u) = @_;
8 + my @d = ();
9 + for (my $i = 0; $i < @u; $i++) {
10 + push @d, $u[$i];
11 + if ($i == @u - 2) {
12 + push @d, " and ";
13 + } elsif ($i < @u - 2) {
14 + push @d, ", ";
15 + }
16 + }
17 + return join('', @d);
18 +}
19 +
20 +sub describe_relation {
21 + my ($topic_info) = @_;
22 + my @desc;
23 +
24 + if (exists $topic_info->{'used'}) {
25 + push @desc, ("is used by " .
26 + phrase_these(@{$topic_info->{'used'}}));
27 + }
28 +
29 + if (exists $topic_info->{'uses'}) {
30 + push @desc, ("uses " .
31 + phrase_these(@{$topic_info->{'uses'}}));
32 + }
33 +
34 + if (exists $topic_info->{'shares'}) {
35 + push @desc, ("shares commits with " .
36 + phrase_these(@{$topic_info->{'shares'}}));
37 + }
38 +
39 + if (!@desc) {
40 + return "";
41 + }
42 +
43 + return "(this branch " . join("; ", @desc) . ".)";
44 +}
45 +
46 +sub forks_from {
47 + my ($topic, $fork, $forkee, @overlap) = @_;
48 + my %ovl = map { $_ => 1 } (@overlap, @{$topic->{$forkee}{'log'}});
49 +
50 + push @{$topic->{$fork}{'uses'}}, $forkee;
51 + push @{$topic->{$forkee}{'used'}}, $fork;
52 + @{$topic->{$fork}{'log'}} = (grep { !exists $ovl{$_} }
53 + @{$topic->{$fork}{'log'}});
54 +}
55 +
56 +sub topic_relation {
57 + my ($topic, $one, $two) = @_;
58 +
59 + my $fh;
60 + open($fh, '-|',
61 + qw(git log --abbrev=7), "--format=%m %h",
62 + "$one...$two", "^master")
63 + or die "$!: open log --left-right";
64 + my (@left, @right);
65 + while (<$fh>) {
66 + my ($sign, $sha1) = /^(.) (.*)/;
67 + if ($sign eq '<') {
68 + push @left, $sha1;
69 + } elsif ($sign eq '>') {
70 + push @right, $sha1;
71 + }
72 + }
73 + close($fh) or die "$!: close log --left-right";
74 +
75 + if (!@left) {
76 + if (@right) {
77 + forks_from($topic, $two, $one);
78 + }
79 + } elsif (!@right) {
80 + forks_from($topic, $one, $two);
81 + } else {
82 + if (@left < @right) {
83 + forks_from($topic, $two, $one, @left);
84 + } elsif (@right < @left) {
85 + forks_from($topic, $one, $two, @right);
86 + } else {
87 + push @{$topic->{$one}{'shares'}}, $two;
88 + push @{$topic->{$two}{'shares'}}, $one;
89 + }
90 + }
91 +}
92 +
93 +sub get_commit {
94 + my (@base) = qw(master next pu);
95 + my $fh;
96 + open($fh, '-|',
97 + qw(git for-each-ref),
98 + "--format=%(refname:short) %(authordate:iso8601)",
99 + "refs/heads/??/*")
100 + or die "$!: open for-each-ref";
101 + my @topic;
102 + my %topic;
103 +
104 + while (<$fh>) {
105 + chomp;
106 + my ($branch, $date) = /^(\S+) (.*)$/;
107 + push @topic, $branch;
108 + $date =~ s/ .*//;
109 + $topic{$branch} = +{
110 + log => [],
111 + tipdate => $date,
112 + };
113 + }
114 + close($fh) or die "$!: close for-each-ref";
115 +
116 + my %base = map { $_ => undef } @base;
117 + my %commit;
118 + my $show_branch_batch = 20;
119 +
120 + while (@topic) {
121 + my @t = (@base, splice(@topic, 0, $show_branch_batch));
122 + my $header_delim = '-' x scalar(@t);
123 + my $contain_pat = '.' x scalar(@t);
124 + open($fh, '-|', qw(git show-branch --sparse --sha1-name),
125 + map { "refs/heads/$_" } @t)
126 + or die "$!: open show-branch";
127 + while (<$fh>) {
128 + chomp;
129 + if ($header_delim) {
130 + if (/^$header_delim$/) {
131 + $header_delim = undef;
132 + }
133 + next;
134 + }
135 + my ($contain, $sha1, $log) =
136 + ($_ =~ /^($contain_pat) \[([0-9a-f]+)\] (.*)$/);
137 +
138 + for (my $i = 0; $i < @t; $i++) {
139 + my $branch = $t[$i];
140 + my $sign = substr($contain, $i, 1);
141 + next if ($sign eq ' ');
142 + next if (substr($contain, 0, 1) ne ' ');
143 +
144 + if (!exists $commit{$sha1}) {
145 + $commit{$sha1} = +{
146 + branch => {},
147 + log => $log,
148 + };
149 + }
150 + my $co = $commit{$sha1};
151 + $co->{'branch'}{$branch} = 1;
152 + next if (exists $base{$branch});
153 + push @{$topic{$branch}{'log'}}, $sha1;
154 + }
155 + }
156 + close($fh) or die "$!: close show-branch";
157 + }
158 +
159 + my %shared;
160 + for my $sha1 (keys %commit) {
161 + my $sign;
162 + my $co = $commit{$sha1};
163 + if (exists $co->{'branch'}{'next'}) {
164 + $sign = '+';
165 + } elsif (exists $co->{'branch'}{'pu'}) {
166 + $sign = '-';
167 + } else {
168 + $sign = '.';
169 + }
170 + $co->{'log'} = $sign . ' ' . $co->{'log'};
171 + my @t = (sort grep { !exists $base{$_} }
172 + keys %{$co->{'branch'}});
173 + next if (@t < 2);
174 + my $t = "@t";
175 + $shared{$t} = 1;
176 + }
177 +
178 + for my $combo (keys %shared) {
179 + my @combo = split(' ', $combo);
180 + for (my $i = 0; $i < @combo - 1; $i++) {
181 + for (my $j = $i + 1; $j < @combo; $j++) {
182 + topic_relation(\%topic, $combo[$i], $combo[$j]);
183 + }
184 + }
185 + }
186 +
187 + open($fh, '-|',
188 + qw(git log --first-parent --abbrev=7),
189 + "--format=%ci %h %p :%s", "master..next")
190 + or die "$!: open log master..next";
191 + while (<$fh>) {
192 + my ($date, $commit, $parent, $tips);
193 + unless (($date, $commit, $parent, $tips) =
194 + /^([-0-9]+) ..:..:.. .\d{4} (\S+) (\S+) ([^:]*):/) {
195 + die "Oops: $_";
196 + }
197 + for my $tip (split(' ', $tips)) {
198 + my $co = $commit{$tip};
199 + $co->{'merged'} = " (merged to 'next' on $date at $commit)";
200 + }
201 + }
202 + close($fh) or die "$!: close log master..next";
203 +
204 + for my $branch (keys %topic) {
205 + my @log = ();
206 + my $n = scalar(@{$topic{$branch}{'log'}});
207 + if (!$n) {
208 + delete $topic{$branch};
209 + next;
210 + } elsif ($n == 1) {
211 + $n = "1 commit";
212 + } else {
213 + $n = "$n commits";
214 + }
215 + my $d = $topic{$branch}{'tipdate'};
216 + my $head = "* $branch ($d) $n\n";
217 + my @desc;
218 + for (@{$topic{$branch}{'log'}}) {
219 + my $co = $commit{$_};
220 + if (exists $co->{'merged'}) {
221 + push @desc, $co->{'merged'};
222 + }
223 + push @desc, $commit{$_}->{'log'};
224 + }
225 + my $list = join("\n", map { " " . $_ } @desc);
226 + my $relation = describe_relation($topic{$branch});
227 + $topic{$branch}{'desc'} = $head . $list;
228 + if ($relation) {
229 + $topic{$branch}{'desc'} .= "\n $relation";
230 + }
231 + }
232 +
233 + return \%topic;
234 +}
235 +
236 +sub blurb_text {
237 + my ($mon, $year, $issue, $dow, $date,
238 + $master_at, $next_at, $text) = @_;
239 +
240 + my $now_string = localtime;
241 + my ($current_dow, $current_mon, $current_date, $current_year) =
242 + ($now_string =~ /^(\w+) (\w+) (\d+) [\d:]+ (\d+)$/);
243 +
244 + $mon ||= $current_mon;
245 + $year ||= $current_year;
246 + $issue ||= "01";
247 + $dow ||= $current_dow;
248 + $date ||= $current_date;
249 + $master_at ||= '0' x 40;
250 + $next_at ||= '0' x 40;
251 + $text ||= <<'EOF';
252 +Here are the topics that have been cooking. Commits prefixed with '-' are
253 +only in 'pu' while commits prefixed with '+' are in 'next'. The ones
254 +marked with '.' do not appear in any of the integration branches, but I am
255 +still holding onto them.
256 +EOF
257 +
258 + $text = <<EOF;
259 +To: git\@vger.kernel.org
260 +Subject: What's cooking in git.git ($mon $year, #$issue; $dow, $date)
261 +X-master-at: $master_at
262 +X-next-at: $next_at
263 +
264 +What's cooking in git.git ($mon $year, #$issue; $dow, $date)
265 +--------------------------------------------------
266 +
267 +$text
268 +EOF
269 + $text =~ s/\n+\Z/\n/;
270 + return $text;
271 +}
272 +
273 +my $blurb_match = <<'EOF';
274 +To: .*
275 +Subject: What's cooking in \S+ \((\w+) (\d+), #(\d+); (\w+), (\d+)\)
276 +X-master-at: ([0-9a-f]{40})
277 +X-next-at: ([0-9a-f]{40})
278 +
279 +What's cooking in \S+ \(\1 \2, #\3; \4, \5\)
280 +-{30,}
281 +\n*
282 +EOF
283 +
284 +my $blurb = "b..l..u..r..b";
285 +sub read_previous {
286 + my ($fn) = @_;
287 + my $fh;
288 + my $section = undef;
289 + my $serial = 1;
290 + my $branch = $blurb;
291 + my $last_empty = undef;
292 + my (@section, %section, @branch, %branch, %description, @leader);
293 + my $in_unedited_olde = 0;
294 +
295 + if (!-r $fn) {
296 + return +{
297 + 'section_list' => [],
298 + 'section_data' => {},
299 + 'topic_description' => {
300 + $blurb => {
301 + desc => undef,
302 + text => blurb_text(),
303 + },
304 + },
305 + };
306 + }
307 +
308 + open ($fh, '<', $fn) or die "$!: open $fn";
309 + while (<$fh>) {
310 + chomp;
311 + if ($in_unedited_olde) {
312 + if (/^>>$/) {
313 + $in_unedited_olde = 0;
314 + $_ = " | $_";
315 + }
316 + } elsif (/^<<$/) {
317 + $in_unedited_olde = 1;
318 + }
319 +
320 + if ($in_unedited_olde) {
321 + $_ = " | $_";
322 + }
323 +
324 + if (defined $section && /^-{20,}$/) {
325 + $_ = "";
326 + }
327 + if (/^$/) {
328 + $last_empty = 1;
329 + next;
330 + }
331 + if (/^\[(.*)\]\s*$/) {
332 + $section = $1;
333 + $branch = undef;
334 + if (!exists $section{$section}) {
335 + push @section, $section;
336 + $section{$section} = [];
337 + }
338 + next;
339 + }
340 + if (defined $section && /^\* (\S+) /) {
341 + $branch = $1;
342 + $last_empty = 0;
343 + if (!exists $branch{$branch}) {
344 + push @branch, [$branch, $section];
345 + $branch{$branch} = 1;
346 + }
347 + push @{$section{$section}}, $branch;
348 + }
349 + if (defined $branch) {
350 + my $was_last_empty = $last_empty;
351 + $last_empty = 0;
352 + if (!exists $description{$branch}) {
353 + $description{$branch} = [];
354 + }
355 + if ($was_last_empty) {
356 + push @{$description{$branch}}, "";
357 + }
358 + push @{$description{$branch}}, $_;
359 + }
360 + }
361 + close($fh);
362 +
363 + for my $branch (keys %description) {
364 + my $ary = $description{$branch};
365 + if ($branch eq $blurb) {
366 + while (@{$ary} && $ary->[-1] =~ /^-{30,}$/) {
367 + pop @{$ary};
368 + }
369 + $description{$branch} = +{
370 + desc => undef,
371 + text => join("\n", @{$ary}),
372 + };
373 + } else {
374 + my @desc = ();
375 + while (@{$ary}) {
376 + my $elem = shift @{$ary};
377 + last if ($elem eq '');
378 + push @desc, $elem;
379 + }
380 + $description{$branch} = +{
381 + desc => join("\n", @desc),
382 + text => join("\n", @{$ary}),
383 + };
384 + }
385 + }
386 +
387 + return +{
388 + section_list => \@section,
389 + section_data => \%section,
390 + topic_description => \%description,
391 + };
392 +}
393 +
394 +sub write_cooking {
395 + my ($fn, $cooking) = @_;
396 + my $fh;
397 +
398 + open($fh, '>', $fn) or die "$!: open $fn";
399 + print $fh $cooking->{'topic_description'}{$blurb}{'text'};
400 +
401 + for my $section_name (@{$cooking->{'section_list'}}) {
402 + my $topic_list = $cooking->{'section_data'}{$section_name};
403 + next if (!@{$topic_list});
404 +
405 + print $fh "\n";
406 + print $fh '-' x 50, "\n";
407 + print $fh "[$section_name]\n";
408 + for my $topic (@{$topic_list}) {
409 + my $d = $cooking->{'topic_description'}{$topic};
410 +
411 + print $fh "\n", $d->{'desc'}, "\n";
412 + if ($d->{'text'}) {
413 + print $fh "\n", $d->{'text'}, "\n";
414 + }
415 + }
416 + }
417 + close($fh);
418 +}
419 +
420 +my $graduated = 'Graduated to "master"';
421 +my $new_topics = 'New Topics';
422 +my $old_new_topics = 'Old New Topics';
423 +
424 +sub update_issue {
425 + my ($cooking) = @_;
426 + my ($fh, $master_at, $next_at, $incremental);
427 +
428 + open($fh, '-|',
429 + qw(git for-each-ref),
430 + "--format=%(refname:short) %(objectname)",
431 + "refs/heads/master",
432 + "refs/heads/next") or die "$!: open for-each-ref";
433 + while (<$fh>) {
434 + my ($branch, $at) = /^(\S+) (\S+)$/;
435 + if ($branch eq 'master') { $master_at = $at; }
436 + if ($branch eq 'next') { $next_at = $at; }
437 + }
438 + close($fh) or die "$!: close for-each-ref";
439 +
440 + $incremental = ((-r "Meta/whats-cooking.txt") &&
441 + system("cd Meta && " .
442 + "git diff --quiet --no-ext-diff HEAD -- " .
443 + "whats-cooking.txt"));
444 +
445 + my $now_string = localtime;
446 + my ($current_dow, $current_mon, $current_date, $current_year) =
447 + ($now_string =~ /^(\w+) (\w+) (\d+) [\d:]+ (\d+)$/);
448 +
449 + my $btext = $cooking->{'topic_description'}{$blurb}{'text'};
450 + if ($btext !~ s/\A$blurb_match//) {
451 + die "match pattern broken?";
452 + }
453 + my ($mon, $year, $issue, $dow, $date) = ($1, $2, $3, $4, $5);
454 +
455 + if ($current_mon ne $mon || $current_year ne $year) {
456 + $issue = "01";
457 + } elsif (!$incremental) {
458 + $issue =~ s/^0*//;
459 + $issue = sprintf "%02d", ($issue + 1);
460 + }
461 + $mon = $current_mon;
462 + $year = $current_year;
463 + $dow = $current_dow;
464 + $date = $current_date;
465 +
466 + $cooking->{'topic_description'}{$blurb}{'text'} =
467 + blurb_text($mon, $year, $issue, $dow, $date,
468 + $master_at, $next_at, $btext);
469 +
470 + if (!$incremental) {
471 + my $sd = $cooking->{'section_data'};
472 + my $sl = $cooking->{'section_list'};
473 + for (my $i = 0; $i < @{$sl}; $i++) {
474 + if ($sl->[$i] eq $new_topics) {
475 + $sl->[$i] = $old_new_topics;
476 + unshift @{$sl}, $new_topics;
477 + last;
478 + }
479 + }
480 + $sd->{$old_new_topics} = $sd->{$new_topics};
481 + $sd->{$new_topics} = [];
482 + }
483 +}
484 +
485 +sub merge_cooking {
486 + my ($cooking, $current) = @_;
487 + my $td = $cooking->{'topic_description'};
488 + my $sd = $cooking->{'section_data'};
489 + my $sl = $cooking->{'section_list'};
490 + my (@new_topic, @gone_topic);
491 +
492 + if (!exists $sd->{$new_topics}) {
493 + $sd->{$new_topics} = [];
494 + unshift @{$sl}, $new_topics;
495 + }
496 +
497 + if (!exists $sd->{$graduated}) {
498 + $sd->{$graduated} = [];
499 + unshift @{$sl}, $graduated;
500 + }
501 +
502 + update_issue($cooking);
503 +
504 + for my $topic (sort keys %{$current}) {
505 + if (!exists $td->{$topic}) {
506 + push @new_topic, $topic;
507 + next;
508 + }
509 + my $n = $current->{$topic}{'desc'};
510 + my $o = $td->{$topic}{'desc'};
511 + if ($n ne $o) {
512 + $td->{$topic}{'desc'} = $n . "\n<<\n" . $o ."\n>>";
513 + }
514 + }
515 +
516 + for my $topic (sort keys %{$td}) {
517 + next if ($topic eq $blurb);
518 + if (!exists $current->{$topic}) {
519 + push @gone_topic, $topic;
520 + }
521 + }
522 +
523 + for (@new_topic) {
524 + push @{$sd->{$new_topics}}, $_;
525 + $td->{$_}{'desc'} = $current->{$_}{'desc'};
526 + }
527 +
528 + if (@gone_topic) {
529 + for my $topic (@gone_topic) {
530 + for my $section (@{$sl}) {
531 + @{$sd->{$section}} = (grep { $_ ne $topic }
532 + @{$sd->{$section}});
533 + }
534 + }
535 + for (@gone_topic) {
536 + push @{$sd->{$graduated}}, $_;
537 + }
538 + }
539 +
540 +}
541 +
542 +################################################################
543 +# Main
544 +
545 +my $topic = get_commit();
546 +my $cooking = read_previous('Meta/whats-cooking.txt');
547 +merge_cooking($cooking, $topic);
548 +write_cooking('Meta/whats-cooking.txt', $cooking);
cook.sh deleted
-449
@@ -1,449 +0,0 @@
1 -#!/bin/sh
2 -
3 -LANG=C LC_ALL=C GIT_PAGER=cat
4 -export LANG LC_ALL GIT_PAGER
5 -
6 -tmpdir=/var/tmp/cook.$$
7 -mkdir "$tmpdir" || exit
8 -tmp="$tmpdir/t"
9 -trap 'rm -fr "$tmpdir"' 0
10 -
11 -git branch --merged "master" | sed -n -e 's/^..//' -e '/\//p' >"$tmp.in.master"
12 -git branch --merged "pu" | sed -n -e 's/^..//' -e '/\//p' >"$tmp.in.pu"
13 -{
14 - comm -13 "$tmp.in.master" "$tmp.in.pu"
15 - git branch --no-merged pu |
16 - sed -n -e 's/^..//' -e '/\//p'
17 -} >"$tmp.branches"
18 -
19 -git log --first-parent --format="%H %ci" master..next |
20 -sed -e 's/ [0-2][0-9]:[0-6][0-9]:[0-6][0-9] [-+][0-2][0-9][0-6][0-9]$//' >"$tmp.next"
21 -git rev-list master..pu >"$tmp.commits.in.pu"
22 -
23 -format_branch () {
24 - # branch=$1 others=$2
25 - git rev-list --no-merges --topo-order "master..$1" --not $2 >"$tmp.list"
26 - count=$(wc -l <"$tmp.list" | tr -d ' ')
27 - label="* $1 ($(git show -s --format="%ai" $1 | sed -e 's/ .*//')) $count commit"
28 - test "$count" = 1 || label="${label}s"
29 -
30 - echo "$label"
31 - lasttimelabel=
32 - lastfoundmerge=
33 - while read commit
34 - do
35 - merged= merged_with=
36 - while read merge at
37 - do
38 - if test -n "$lastfoundmerge"
39 - then
40 - if test "$lastfoundmerge" = "$merge"
41 - then
42 - lastfoundmerge=
43 - else
44 - continue
45 - fi
46 - fi
47 - mb=$(git merge-base $merge $commit)
48 - if test "$mb" = "$commit"
49 - then
50 - merged=$at merged_with=$merge
51 - else
52 - break
53 - fi
54 - done <"$tmp.next"
55 -
56 - lastfoundmerge=$merged_with
57 - thistimelabel=
58 - if test -n "$merged"
59 - then
60 - thistimelabel=$merged
61 - commitlabel="+"
62 - elif grep "$commit" "$tmp.commits.in.pu" >/dev/null
63 - then
64 - commitlabel="-"
65 - else
66 - commitlabel="."
67 - fi
68 - if test "$lasttimelabel" != "$thistimelabel"
69 - then
70 - with=$(git rev-parse --short $merged_with)
71 - echo " (merged to 'next' on $thistimelabel at $with)"
72 - lasttimelabel=$thistimelabel
73 - fi
74 - git show -s --format=" $commitlabel %s" $commit
75 - done <"$tmp.list"
76 -}
77 -
78 -add_desc () {
79 - kind=$1
80 - shift
81 - test -z "$description" || description="$description;"
82 - others=
83 - while :
84 - do
85 - other=$1
86 - case "$other" in
87 - "#EPO-"*) other="early parts of ${other#"#EPO-"}"
88 - esac
89 - shift
90 - case "$#,$others" in
91 - 0,)
92 - others="$other"
93 - break ;;
94 - 0,?*)
95 - others="$others and $other"
96 - break ;;
97 - *,)
98 - others="$other"
99 - ;;
100 - *,?*)
101 - others="$others, $other"
102 - ;;
103 - esac
104 - done
105 - description="$description $kind $others"
106 -}
107 -
108 -show_topic () {
109 - old=$1 new=$2
110 -
111 - sed -n -e '/^ ..*/p' -e '/^\* /p' "$old" >"$tmp.old.nc"
112 - sed -n -e '/^ ..*/p' -e '/^\* /p' "$new" >"$tmp.new.nc"
113 - if cmp "$tmp.old.nc" "$tmp.new.nc" >/dev/null
114 - then
115 - cat "$old"
116 - else
117 - cat "$new"
118 - echo "<<"
119 - cat "$old"
120 - echo ">>"
121 - fi
122 -}
123 -
124 -compare_topic () {
125 - b=$1 r=$2
126 - based=$(git rev-list --no-merges $b..$r | wc -l | tr -d ' ')
127 - bases=$(git rev-list --no-merges $r..$b | wc -l | tr -d ' ')
128 - case "$based,$bases" in
129 - 0,0) echo same; exit ;;
130 - 0,*) echo left; exit ;;
131 - *,0) echo right; exit ;;
132 - esac
133 -
134 - if test $based -lt $bases
135 - then
136 - echo left-p
137 - else
138 - echo fork
139 - fi
140 -}
141 -
142 -# List commits that are shared between more than one topic branches
143 -while read b
144 -do
145 - git rev-list --no-merges "master..$b"
146 -done <"$tmp.branches" | sort | uniq -d >"$tmp.shared"
147 -
148 -# Set of branches related to each other due to sharing the same commit
149 -while read shared
150 -do
151 - b=$(git branch --contains "$shared" | sed -n -e 's/^..//' -e '/\//p')
152 - echo "" $b ""
153 -done <"$tmp.shared" | sort -u >"$tmp.related"
154 -
155 -serial=1
156 -while read b
157 -do
158 - related=$(grep " $b " "$tmp.related" | tr ' ' '\012' | sort -u | sed -e '/^$/d')
159 -
160 - based_on= based_on_msg=
161 - used_by=
162 - forks=
163 - same_as=
164 - if test -n "$related"
165 - then
166 - for r in $related
167 - do
168 - test "$b" = "$r" && continue
169 - case "$(compare_topic "$b" "$r")" in
170 - same)
171 - same_as="$same_as$r "
172 - ;;
173 - left)
174 - based_on="$based_on$r "
175 - based_on_msg="$based_on_msg$r "
176 - ;;
177 - left-p)
178 - based_on="$based_on$r "
179 - based_on_msg="${based_on_msg}#EPO-$r "
180 - ;;
181 - right)
182 - used_by="$used_by$r "
183 - ;;
184 - fork)
185 - forks="$forks$r "
186 - ;;
187 - esac
188 - done
189 - fi
190 -
191 - {
192 - format_branch "$b" "$based_on"
193 -
194 - description=
195 - test -z "$same_as" || add_desc 'is same as' $same_as
196 - test -z "$based_on" || add_desc 'uses' $based_on_msg
197 - test -z "$used_by" || add_desc 'is used by' $used_by
198 - test -z "$forks" || add_desc 'shares commits with' $forks
199 -
200 - test -z "$description" ||
201 - echo " (this branch$description.)"
202 - } >"$tmp.output.$serial"
203 - echo "$b $serial"
204 - serial=$(( $serial + 1 ))
205 -done <"$tmp.branches" >"$tmp.output.toc"
206 -
207 -eval $(date +"monthname=%b month=%m year=%Y date=%d dow=%a")
208 -
209 -incremental=$(
210 - cd Meta &&
211 - if git diff --quiet --no-ext-diff HEAD -- whats-cooking.txt >/dev/null
212 - then
213 - echo no
214 - else
215 - echo yes
216 - fi
217 -)
218 -
219 -# Find the current issue
220 -eval $(sed -ne '/^Subject: /{
221 - s/^Subject: What.s cooking in git.git (\([A-Z][a-z][a-z]\) \([0-9][0-9][0-9][0-9]\), #\([0-9][0-9]*\); [A-Z][a-z][a-z], [0-9][0-9])$/lastmon=\1 lastyear=\2 lastissue=\3/p
222 - q
223 -}' Meta/whats-cooking.txt)
224 -if test "$monthname $year" = "$lastmon $lastyear"
225 -then
226 - while case "$lastissue" in 0?*) ;; *) break ;; esac
227 - do
228 - lastissue=${lastissue#0}
229 - done
230 - if test "$incremental" = no
231 - then
232 - issue=$(( $lastissue + 1 ))
233 - else
234 - issue=$(( $lastissue + 0 ))
235 - fi
236 -else
237 - issue=1
238 -fi
239 -
240 -issue=$( printf "%02d" $issue )
241 -last=whats-cooking.txt
242 -
243 -master_at=$(git rev-parse --verify refs/heads/master)
244 -next_at=$(git rev-parse --verify refs/heads/next)
245 -cat >"$tmp.output.blurb" <<EOF
246 -To: git@vger.kernel.org
247 -Subject: What's cooking in git.git ($monthname $year, #$issue; $dow, $date)
248 -X-master-at: $master_at
249 -X-next-at: $next_at
250 -
251 -What's cooking in git.git ($monthname $year, #$issue; $dow, $date)
252 ---------------------------------------------------
253 -
254 -Here are the topics that have been cooking. Commits prefixed with '-' are
255 -only in 'pu' while commits prefixed with '+' are in 'next'. The ones
256 -marked with '.' do not appear in any of the branches, but I am still
257 -holding onto them.
258 -
259 -EOF
260 -
261 -if test -z "$NO_TEMPLATE" && test -f "Meta/$last"
262 -then
263 - template="Meta/$last"
264 -else
265 - template=/dev/null
266 -fi
267 -perl -w -e '
268 - my $section = undef;
269 - my $serial = 1;
270 - my $blurb = "b..l..u..r..b";
271 - my $branch = $blurb;
272 - my $tmp = $ARGV[0];
273 - my $incremental = $ARGV[1] eq "yes";
274 - my $last_empty = undef;
275 - my (@section, %section, @branch, %branch, %description, @leader);
276 - my $in_unedited_olde = 0;
277 -
278 - while (<STDIN>) {
279 - if ($in_unedited_olde) {
280 - if (/^>>$/) {
281 - $in_unedited_olde = 0;
282 - $_ = " | $_";
283 - }
284 - } elsif (/^<<$/) {
285 - $in_unedited_olde = 1;
286 - }
287 -
288 - if ($in_unedited_olde) {
289 - $_ = " | $_";
290 - }
291 -
292 - if (defined $section && /^-{20,}$/) {
293 - $_ = "\n";
294 - }
295 - if (/^$/) {
296 - $last_empty = 1;
297 - next;
298 - }
299 - if (/^\[(.*)\]\s*$/) {
300 - $section = $1;
301 - $branch = undef;
302 - if ($section eq "New Topics" && !$incremental) {
303 - $section = "Old New Topics";
304 - }
305 - if (!exists $section{$section}) {
306 - push @section, $section;
307 - $section{$section} = [];
308 - }
309 - next;
310 - }
311 - if (defined $section && /^\* (\S+) /) {
312 - $branch = $1;
313 - $last_empty = 0;
314 - if (!exists $branch{$branch}) {
315 - push @branch, [$branch, $section];
316 - $branch{$branch} = 1;
317 - }
318 - push @{$section{$section}}, $branch;
319 - }
320 - if (defined $branch) {
321 - my $was_last_empty = $last_empty;
322 - $last_empty = 0;
323 - if (!exists $description{$branch}) {
324 - $description{$branch} = [];
325 - }
326 - if ($was_last_empty) {
327 - push @{$description{$branch}}, "\n";
328 - }
329 - push @{$description{$branch}}, $_;
330 - }
331 - }
332 -
333 - if (open I, "<$tmp.output.toc") {
334 - $section = "New Topics";
335 - while (<I>) {
336 - my ($branch, $oldserial) = /^(\S*) (\d+)$/;
337 - next if (exists $branch{$branch});
338 - if (!exists $section{$section}) {
339 - # Have it at the beginning
340 - unshift @section, $section;
341 - $section{$section} = [];
342 - }
343 - push @{$section{$section}}, $branch;
344 - push @branch, [$branch, $section];
345 - $branch{$branch} = 1;
346 - if (!exists $description{$branch}) {
347 - $description{$branch} = [];
348 - }
349 - open II, "<$tmp.output.$oldserial";
350 - while (<II>) {
351 - push @{$description{$branch}}, $_;
352 - }
353 - close II;
354 - }
355 - close I;
356 - }
357 -
358 - while (0 <= @{$description{$blurb}}) {
359 - my $last = pop @{$description{$blurb}};
360 - if ($last =~ /^$/ || $last =~ /^-{20,}$/) {
361 - next;
362 - } else {
363 - push @{$description{$blurb}}, $last;
364 - last;
365 - }
366 - }
367 -
368 - open O, ">$tmp.template.blurb";
369 - for (@{$description{$blurb}}) {
370 - print O $_;
371 - }
372 - close O;
373 -
374 - open TOC, ">$tmp.template.toc";
375 - $serial = 1;
376 - for my $section (@section) {
377 - for my $branch (@{$section{$section}}) {
378 - print TOC "$branch $serial $section\n";
379 - open O, ">$tmp.template.$serial";
380 - for (@{$description{$branch}}) {
381 - print O $_;
382 - }
383 - close O;
384 - $serial++;
385 - }
386 - }
387 -' <"$template" "$tmp" "$incremental"
388 -
389 -tmpserial=$(
390 - tail -n 1 "$tmp.template.toc" |
391 - read branch serial section &&
392 - echo $serial
393 -)
394 -
395 -# Assemble them all
396 -
397 -if test -z "$TO_STDOUT"
398 -then
399 - exec >"Meta/whats-cooking.txt"
400 -fi
401 -
402 -if test -s "$tmp.template.blurb"
403 -then
404 - sed -e '/^---------------*$/q' <"$tmp.output.blurb"
405 - sed -e '1,/^---------------*$/d' <"$tmp.template.blurb"
406 -else
407 - cat "$tmp.output.blurb"
408 -fi
409 -
410 -current='
411 ---------------------------------------------------
412 -[Graduated to "master"]
413 -'
414 -while read branch oldserial section
415 -do
416 - test "$section" = 'Graduated to "master"' &&
417 - test "$incremental" = no && continue
418 -
419 - tip=$(git rev-parse --quiet --verify "refs/heads/$branch") || continue
420 - mb=$(git merge-base master $tip)
421 - test "$mb" = "$tip" || continue
422 - if test -n "$current"
423 - then
424 - echo "$current"
425 - current=
426 - else
427 - echo
428 - fi
429 - cat "$tmp.template.$oldserial"
430 -done <"$tmp.template.toc"
431 -
432 -current=
433 -while read branch oldserial section
434 -do
435 - found=$(grep "^$branch " "$tmp.output.toc") || continue
436 - newserial=$(expr "$found" : '[^ ]* \(.*\)')
437 - if test "$current" != "$section"
438 - then
439 - current=$section
440 - echo "
441 ---------------------------------------------------
442 -[$section]
443 -"
444 - else
445 - echo
446 - fi
447 -
448 - show_topic "$tmp.template.$oldserial" "$tmp.output.$newserial"
449 -done <"$tmp.template.toc"