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*/'