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"