Add UWC script to help maintaining What's Cooking messages

Junio C Hamano committed Nov 13, 2007 at 01:22 UTC 5a3846f699f9a16b2e2ff4ad5b86cdd522c0f418
2 files changed +220 -1
UWC new
+219
@@ -0,0 +1,219 @@
1 +#!/usr/bin/perl -w
2 +#
3 +# Update an older edition of What's Cooking with the latest data.
4 +#
5 +# Usage: UWC [ old [ new ] ]
6 +#
7 +# Giving no parameter is the same as giving a single "-" to the command.
8 +#
9 +# The command reads the old edition of (annotated) "What's Cooking"
10 +# message from "old", and "new". If "old" is "-", it is read from
11 +# the standard input. If "new" is not specified, WC script is run
12 +# and its output is used.
13 +#
14 +# An annotated "What's Cooking" message can have group header (a line
15 +# that has the group name enclosed in "[" and "]"), and annotatation
16 +# paragraphs after each topic's commit list, in addition to the bare
17 +# "WC" output.
18 +#
19 +# The group headers, topics in each group and their order in the group,
20 +# and annotation to topics are preserved from the "old" message. The
21 +# list of commits in each topic is replaced with the one taken from the
22 +# "new" message. Any topic in "new" that did not exist in "old" appear
23 +# in "New Topics" group. Also, topics that do not appear in the "new"
24 +# message are marked with <<deleted>>, topics whose commit list are
25 +# different from "old" are marked with <<updated from...>>>.
26 +#
27 +# Typically the maintainer would place the What's Cooking message
28 +# previously sent in a buffer in Emacs, and filter the buffer contents
29 +# with this script, to prepare an up-to-date message.
30 +
31 +sub parse_whats_cooking {
32 + my ($fh) = @_;
33 + my $head = undef;
34 + my $group = undef;
35 + my %wc = ("group list" => [], "topic hash" => {});
36 + my $topic;
37 + my $skipping_comment = 0;
38 +
39 + while (<$fh>) {
40 + if (/^-{40,}$/) {
41 + # Group separator
42 + next;
43 + }
44 +
45 + if (!defined $head) {
46 + if (/^Here are the topics that have been cooking\./) {
47 + $head = $_;
48 + }
49 + next;
50 + }
51 +
52 + if (/^<<.*>>$/) {
53 + next;
54 + }
55 +
56 + if ($skipping_comment) {
57 + if (/^>>$/) {
58 + $skipping_comment = 0;
59 + }
60 + next;
61 + }
62 +
63 + if (!$skipping_comment && /^<</) {
64 + $skipping_comment = 1;
65 + next;
66 + }
67 +
68 + if (/^\[(.*)\]$/) {
69 + $group = $1;
70 + push @{$wc{"group list"}}, $group;
71 + $wc{" $group"} = [];
72 + $topic = undef;
73 + next;
74 + }
75 +
76 + if (!defined $group) {
77 + if (/^\* (\S+) (\(.*\) \d+ commits?)$/) {
78 + # raw output
79 + $group = "Misc";
80 + push @{$wc{"group list"}}, $group;
81 + $wc{" $group"} = [];
82 + } else {
83 + $head .= $_;
84 + next;
85 + }
86 + }
87 +
88 + if (/^\* (\S+) (\(.*\) \d+ commits?)$/) {
89 + $topic = +{
90 + topic => $1,
91 + head => $_,
92 + names => "",
93 + text => "",
94 + };
95 + $wc{"topic hash"}{$topic->{"topic"}} = $topic;
96 + push @{$wc{" $group"}}, $topic;
97 + next;
98 + }
99 +
100 + if (/^ [-+.?] / || /^ \S/) {
101 + $topic->{"names"} .= $_;
102 + next;
103 + }
104 + $topic->{"text"} .= $_;
105 + }
106 +
107 + for ($head) {
108 + s/\A\s+//s;
109 + s/\s+\Z//s;
110 + }
111 + $wc{"head text"} = $head;
112 + for $topic (values %{$wc{"topic hash"}}) {
113 + for ($topic->{"text"}) {
114 + s/\A\s+//s;
115 + s/\s+\Z//s;
116 + }
117 + }
118 + return \%wc;
119 +}
120 +
121 +sub print_whats_cooking {
122 + my ($wc) = @_;
123 +
124 + print $wc->{"head text"}, "\n";
125 +
126 + for my $group (@{$wc->{"group list"}}) {
127 + print "\n", "-" x 64, "\n";
128 + print "[$group]\n";
129 + for my $topic (@{$wc->{" $group"}}) {
130 + print "\n", $topic->{"head"};
131 + print $topic->{"names"};
132 + if ($topic->{"text"} ne '') {
133 + print "\n", $topic->{"text"}, "\n";
134 + }
135 + }
136 + }
137 +}
138 +
139 +sub delete_topic {
140 + my ($wc, $topic) = @_;
141 + $topic->{"status"} = "deleted";
142 +}
143 +
144 +sub merge_whats_cooking {
145 + my ($old_wc, $new_wc) = @_;
146 + my $group;
147 +
148 + for $group (@{$old_wc->{"group list"}}) {
149 + for my $topic (@{$old_wc->{" $group"}}) {
150 + my $name = $topic->{"topic"};
151 + my $newtopic = delete $new_wc->{"topic hash"}{$name};
152 +
153 + if (!defined $newtopic) {
154 + $topic->{"names"} = "";
155 + $topic->{"text"} = "<<deleted>>";
156 + next;
157 + }
158 + if (($newtopic->{"names"} ne $topic->{"names"}) ||
159 + ($newtopic->{"head"} ne $topic->{"head"})) {
160 + my $text = ("<<updated from\n" .
161 + $topic->{"head"} .
162 + $topic->{"names"} . ">>");
163 +
164 + if ($topic->{"text"} ne '') {
165 + $text .= "\n\n" . $topic->{"text"};
166 + }
167 + for ($text) {
168 + s/\A\s+//s;
169 + s/\s+\Z//s;
170 + }
171 + $topic->{"text"} = $text;
172 + $topic->{"names"} = $newtopic->{"names"};
173 + $topic->{"head"} = $newtopic->{"head"};
174 + }
175 + }
176 + }
177 +
178 + if (%{$new_wc->{"topic hash"}}) {
179 + $group = "New Topics";
180 + if (!exists $old_wc->{" $group"}) {
181 + unshift @{$old_wc->{"group list"}}, $group;
182 + $old_wc->{" $group"} = [];
183 + }
184 + for my $topic (values %{$new_wc->{"topic hash"}}) {
185 + my $name = $topic->{"topic"};
186 + $old_wc->{"topic hash"}{$name} = $topic;
187 + push @{$old_wc->{" $group"}}, $topic;
188 + $topic->{"text"} = $topic->{"text"};
189 + }
190 + }
191 +}
192 +
193 +if (@ARGV == 0) {
194 + @ARGV = ('-');
195 +}
196 +if (@ARGV != 2 && @ARGV != 1) {
197 + die "Usage: $0 old [new]\n";
198 +}
199 +
200 +my ($old_wc, $new_wc);
201 +
202 +if ($ARGV[0] eq '-') {
203 + *FH = *STDIN;
204 +} else {
205 + open FH, "$ARGV[0]";
206 +}
207 +$old_wc = parse_whats_cooking(\*FH);
208 +close FH;
209 +
210 +if (@ARGV > 1) {
211 + open FH, "$ARGV[1]";
212 +} else {
213 + open FH, "Meta/WC |";
214 +}
215 +$new_wc = parse_whats_cooking(\*FH);
216 +close FH;
217 +
218 +merge_whats_cooking($old_wc, $new_wc);
219 +print_whats_cooking($old_wc);
WC
+1 -1
@@ -17,4 +17,4 @@ order.
17
18 EOF
19
20 -Meta/git-topic.perl --base=master | sed -e 's/^\*./*/'
20 +Meta/git-topic.perl --base=master | sed -e 's/^\*./\n*/'