What's cooking
[git] / cook.sh
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         count=$(git rev-list "master..$1" | wc -l)
31         mcount=$(git rev-list "maint..$1" | wc -l)
32         if test $mcount = $count
33         then
34             label="$label."
35         fi
36
37         echo "$label"
38         lasttimelabel=
39         lastfoundmerge=
40         while read commit
41         do
42                 merged= merged_with=
43                 while read merge at
44                 do
45                         if test -n "$lastfoundmerge"
46                         then
47                                 if test "$lastfoundmerge" = "$merge"
48                                 then
49                                         lastfoundmerge=
50                                 else
51                                         continue
52                                 fi
53                         fi
54                         mb=$(git merge-base $merge $commit)
55                         if test "$mb" = "$commit"
56                         then
57                                 merged=$at merged_with=$merge
58                         else
59                                 break
60                         fi
61                 done <"$tmp.next"
62
63                 lastfoundmerge=$merged_with
64                 thistimelabel=
65                 if test -n "$merged"
66                 then
67                         thistimelabel=$merged
68                         commitlabel="+"
69                 elif grep "$commit" "$tmp.commits.in.pu" >/dev/null
70                 then
71                         commitlabel="-"
72                 else
73                         commitlabel="."
74                 fi
75                 if test "$lasttimelabel" != "$thistimelabel"
76                 then
77                         with=$(git rev-parse --short $merged_with)
78                         echo "  (merged to 'next' on $thistimelabel at $with)"
79                         lasttimelabel=$thistimelabel
80                 fi
81                 git show -s --format=" $commitlabel %s" $commit
82         done <"$tmp.list"
83 }
84
85 add_desc () {
86         kind=$1
87         shift
88         test -z "$description" || description="$description;"
89         others=
90         while :
91         do
92                 other=$1
93                 shift
94                 case "$#,$others" in
95                 0,)
96                         others="$other"
97                         break ;;
98                 0,?*)
99                         others="$others and $other"
100                         break ;;
101                 *,)
102                         others="$other"
103                         ;;
104                 *,?*)
105                         others="$others, $other"
106                         ;;
107                 esac
108         done
109         description="$description $kind $others"
110 }
111
112 show_topic () {
113         old=$1 new=$2
114
115         sed -n -e '/^ ..*/p' -e '/^\* /p' "$old" >"$tmp.old.nc"
116         sed -n -e '/^ ..*/p' -e '/^\* /p' "$new" >"$tmp.new.nc"
117         if cmp "$tmp.old.nc" "$tmp.new.nc" >/dev/null
118         then
119                 cat "$old"
120         else
121                 cat "$new"
122                 echo "<<"
123                 cat "$old"
124                 echo ">>"
125         fi
126 }
127
128 while read b
129 do
130         git rev-list --no-merges "master..$b"
131 done <"$tmp.branches" | sort | uniq -d >"$tmp.shared"
132
133 while read shared
134 do
135         b=$(git branch --contains "$shared" | sed -n -e 's/^..//' -e '/\//p')
136         echo "" $b ""
137 done <"$tmp.shared" | sort -u >"$tmp.related"
138
139 serial=1
140 while read b
141 do
142         related=$(grep " $b " "$tmp.related" | tr ' ' '\012' | sort -u | sed -e '/^$/d')
143
144         based_on=
145         used_by=
146         forks=
147         same_as=
148         if test -n "$related"
149         then
150                 for r in $related
151                 do
152                         test "$b" = "$r" && continue
153                         based=$(git rev-list --no-merges $b..$r | wc -l | tr -d ' ')
154                         bases=$(git rev-list --no-merges $r..$b | wc -l | tr -d ' ')
155                         case "$based,$bases" in
156                         0,0)
157                                 same_as="$same_as$r "
158                                 ;;
159                         0,*)
160                                 based_on="$based_on$r "
161                                 ;;
162                         *,0)
163                                 used_by="$used_by$r "
164                                 ;;
165                         *,*)
166                                 forks="$forks$r "
167                                 ;;
168                         esac
169                 done
170         fi
171
172         {
173                 format_branch "$b" "$based_on"
174
175                 description=
176                 test -z "$same_as" || add_desc 'is same as' $same_as
177                 test -z "$based_on" || add_desc 'uses' $based_on
178                 test -z "$used_by" || add_desc 'is used by' $used_by
179                 test -z "$forks" || add_desc 'is related to' $forks
180
181                 test -z "$description" ||
182                 echo " (this branch$description.)"
183         } >"$tmp.output.$serial"
184         echo "$b $serial"
185         serial=$(( $serial + 1 ))
186 done <"$tmp.branches" >"$tmp.output.toc"
187
188 eval $(date +"monthname=%b month=%m year=%Y date=%d dow=%a")
189 lead="whats/cooking/$year/$month"
190 issue=$(
191         cd Meta &&
192         git ls-tree -r --name-only HEAD "$lead"  | tail -n 1
193 )
194 if test -n "$issue"
195 then
196         issue=$( expr "$issue" : '.*/0*\([1-9][0-9]*\)\.txt$' )
197         issue=$(( $issue + 1 ))
198 else
199         issue=1
200 fi
201 issue=$( printf "%02d" $issue )
202 mkdir -p "Meta/$lead"
203
204 last=$(
205         cd Meta &&
206         git ls-tree -r --name-only HEAD "whats/cooking"  | tail -n 1
207 )
208
209 # We may have a half-written one already.
210 incremental=no
211 if test -f "Meta/$lead/$issue.txt"
212 then
213         last="$lead/$issue.txt"
214         incremental=yes
215 fi
216
217 master_at=$(git rev-parse --verify refs/heads/master)
218 next_at=$(git rev-parse --verify refs/heads/next)
219 cat >"$tmp.output.blurb" <<EOF
220 To: git@vger.kernel.org
221 Subject: What's cooking in git.git ($monthname $year, #$issue; $dow, $date)
222 X-master-at: $master_at
223 X-next-at: $next_at
224
225 What's cooking in git.git ($monthname $year, #$issue; $dow, $date)
226 --------------------------------------------------
227
228 Here are the topics that have been cooking.  Commits prefixed with '-' are
229 only in 'pu' while commits prefixed with '+' are in 'next'.  The ones
230 marked with '.' do not appear in any of the branches, but I am still
231 holding onto them.
232
233 EOF
234
235 if test -z "$NO_TEMPLATE" && test -f "Meta/$last"
236 then
237         template="Meta/$last"
238 else
239         template=/dev/null
240 fi
241 perl -w -e '
242         my $section = undef;
243         my $serial = 1;
244         my $blurb = "b..l..u..r..b";
245         my $branch = $blurb;
246         my $tmp = $ARGV[0];
247         my $incremental = $ARGV[1] eq "yes";
248         my $last_empty = undef;
249         my (@section, %section, @branch, %branch, %description, @leader);
250         my $in_unedited_olde = 0;
251
252         while (<STDIN>) {
253                 if ($in_unedited_olde) {
254                         if (/^>>$/) {
255                                 $in_unedited_olde = 0;
256                                 $_ = " | $_";
257                         }
258                 } elsif (/^<<$/) {
259                         $in_unedited_olde = 1;
260                 }
261
262                 if ($in_unedited_olde) {
263                         $_ = " | $_";
264                 }
265
266                 if (defined $section && /^-{20,}$/) {
267                         $_ = "\n";
268                 }
269                 if (/^$/) {
270                         $last_empty = 1;
271                         next;
272                 }
273                 if (/^\[(.*)\]\s*$/) {
274                         $section = $1;
275                         $branch = undef;
276                         if ($section eq "New Topics" && !$incremental) {
277                                 $section = "Old New Topics";
278                         }
279                         if (!exists $section{$section}) {
280                                 push @section, $section;
281                                 $section{$section} = [];
282                         }
283                         next;
284                 }
285                 if (defined $section && /^\* (\S+) /) {
286                         $branch = $1;
287                         $last_empty = 0;
288                         if (!exists $branch{$branch}) {
289                                 push @branch, [$branch, $section];
290                                 $branch{$branch} = 1;
291                         }
292                         push @{$section{$section}}, $branch;
293                 }
294                 if (defined $branch) {
295                         my $was_last_empty = $last_empty;
296                         $last_empty = 0;
297                         if (!exists $description{$branch}) {
298                                 $description{$branch} = [];
299                         }
300                         if ($was_last_empty) {
301                                 push @{$description{$branch}}, "\n";
302                         }
303                         push @{$description{$branch}}, $_;
304                 }
305         }
306
307         if (open I, "<$tmp.output.toc") {
308                 $section = "New Topics";
309                 while (<I>) {
310                         my ($branch, $oldserial) = /^(\S*) (\d+)$/;
311                         next if (exists $branch{$branch});
312                         if (!exists $section{$section}) {
313                                 # Have it at the beginning
314                                 unshift @section, $section;
315                                 $section{$section} = [];
316                         }
317                         push @{$section{$section}}, $branch;
318                         push @branch, [$branch, $section];
319                         $branch{$branch} = 1;
320                         if (!exists $description{$branch}) {
321                                 $description{$branch} = [];
322                         }
323                         open II, "<$tmp.output.$oldserial";
324                         while (<II>) {
325                                 push @{$description{$branch}}, $_;
326                         }
327                         close II;
328                 }
329                 close I;
330         }
331
332         while (0 <= @{$description{$blurb}}) {
333                 my $last = pop @{$description{$blurb}};
334                 if ($last =~ /^$/ || $last =~ /^-{20,}$/) {
335                         next;
336                 } else {
337                         push @{$description{$blurb}}, $last;
338                         last;
339                 }
340         }
341
342         open O, ">$tmp.template.blurb";
343         for (@{$description{$blurb}}) {
344                 print O $_;
345         }
346         close O;
347
348         open TOC, ">$tmp.template.toc";
349         $serial = 1;
350         for my $section (@section) {
351                 for my $branch (@{$section{$section}}) {
352                         print TOC "$branch $serial $section\n";
353                         open O, ">$tmp.template.$serial";
354                         for (@{$description{$branch}}) {
355                                 print O $_;
356                         }
357                         close O;
358                         $serial++;
359                 }
360         }
361 ' <"$template" "$tmp" "$incremental"
362
363 tmpserial=$(
364         tail -n 1 "$tmp.template.toc" | read branch serial section && echo $serial
365 )
366
367 # Assemble them all
368
369 if test -z "$TO_STDOUT"
370 then
371         exec >"Meta/$lead/$issue.txt"
372 fi
373
374 if test -s "$tmp.template.blurb"
375 then
376         sed -e '/^---------------*$/q' <"$tmp.output.blurb"
377         sed -e '1,/^---------------*$/d' <"$tmp.template.blurb"
378 else
379         cat "$tmp.output.blurb"
380 fi
381
382 current='
383 --------------------------------------------------
384 [Graduated to "master"]
385 '
386 while read branch oldserial section
387 do
388         test "$section" = 'Graduated to "master"' &&
389         test "$incremental" = no && continue
390
391         tip=$(git rev-parse --quiet --verify "refs/heads/$branch") || continue
392         mb=$(git merge-base master $tip)
393         test "$mb" = "$tip" || continue
394         if test -n "$current"
395         then
396                 echo "$current"
397                 current=
398         else
399                 echo
400         fi
401         cat "$tmp.template.$oldserial"
402 done <"$tmp.template.toc"
403
404 current=
405 while read branch oldserial section
406 do
407         found=$(grep "^$branch " "$tmp.output.toc") || continue
408         newserial=$(expr "$found" : '[^ ]* \(.*\)')
409         if test "$current" != "$section"
410         then
411                 current=$section
412                 echo "
413 --------------------------------------------------
414 [$section]
415 "
416         else
417                 echo
418         fi
419
420         show_topic "$tmp.template.$oldserial" "$tmp.output.$newserial"
421 done <"$tmp.template.toc"