]> git.ipfire.org Git - thirdparty/git.git/blame - worklog
What's cooking (2023/08 #04)
[thirdparty/git.git] / worklog
CommitLineData
d1d05d1d
JH
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5use Getopt::Long;
6use Time::Local;
7
9015711e
JH
8################################################################
9
d1d05d1d
JH
10sub seconds_in_a_week () {
11 return 7 * 24 * 3600;
12}
13
9015711e
JH
14# Convert seconds from epoch to datestring and dow
15sub seconds_to_date {
16 my $time = shift;
17 my @time = localtime($time);
18 return (sprintf("%04d-%02d-%02d",
19 $time[5]+1900, $time[4]+1, $time[3]),
20 $time[6]);
21}
d1d05d1d 22
9015711e
JH
23sub date_to_seconds {
24 my $datestring = shift;
25 my ($year, $mon, $mday) = ($datestring =~ /^(\d{4})-(\d{2})-(\d{2})$/);
26 unless (defined $year && defined $mon && defined $mday &&
27 1 <= $mon && $mon <= 12 &&
28 1 <= $mday && $mday <= 31) {
29 die "Bad datestring specification: $datestring";
30 }
31 return timelocal(0, 0, 0, $mday, $mon - 1, $year - 1900);
32}
33
34sub next_date {
35 my $datestring = shift;
36 my $time = date_to_seconds($datestring);
37 return seconds_to_date($time + 3600 * 36);
38}
39
40sub prev_date {
41 my $datestring = shift;
42 my $time = date_to_seconds($datestring);
43 return seconds_to_date($time - 3600 * 12);
44}
45
46sub beginning_of_reporting_week {
47 my ($datestring, $bow) = @_;
48 my ($date, $dow) = seconds_to_date(date_to_seconds($datestring));
49 do {
50 ($date, $dow) = prev_date($date);
51 } while ($dow != $bow);
52 return $date;
53}
d1d05d1d 54
9015711e
JH
55sub bow {
56 my ($week, $bow) = @_;
57 my $time;
d1d05d1d
JH
58 if (!defined $week) {
59 $time = time;
60 } elsif ($week =~ /^\d+$/) {
61 $time = time - seconds_in_a_week * $week;
62 } else {
9015711e 63 $time = date_to_seconds($week);
d1d05d1d 64 }
9015711e
JH
65 my ($datestring, $dow) = seconds_to_date($time);
66 return beginning_of_reporting_week($datestring, $bow);
67}
d1d05d1d 68
9015711e
JH
69sub date_within {
70 my ($date, $bottom, $top) = @_;
71 return ($bottom le $date && $date le $top);
d1d05d1d
JH
72}
73
9015711e
JH
74################################################################
75
76my $verbose = 0;
77my $quiet = 0;
78my $reporting_date;
79my $weeks;
80my $bow = 0;
81my $bottom_date;
82
d1d05d1d
JH
83if (!GetOptions(
84 "verbose!" => \$verbose,
9015711e
JH
85 "quiet!" => \$quiet,
86 "date=s" => \$reporting_date,
87 "weeks=i" => \$weeks,
d1d05d1d
JH
88 "bow=i" => \$bow,
89 )) {
9015711e
JH
90 print STDERR "$0 [-v|-q] [-d date] [-w weeks] [-b bow]\n";
91 exit 1;
92}
93
94if ($verbose && $quiet) {
95 print STDERR "Which? Verbose, or Quiet?\n";
d1d05d1d
JH
96 exit 1;
97}
98
9015711e
JH
99if (!defined $reporting_date) {
100 ($reporting_date, my $dow) = seconds_to_date(time);
101 while ($dow != $bow) {
102 ($reporting_date, $dow) = next_date($reporting_date);
103 }
104}
105
106$bottom_date = beginning_of_reporting_week($reporting_date, $bow);
107
108if (!defined $weeks || $weeks < 0) {
109 $weeks = 0;
d1d05d1d 110}
9015711e
JH
111
112for (my $i = 0; $i < $weeks; $i++) {
113 for (my $j = 0; $j < 7; $j++) {
114 ($bottom_date, undef) = prev_date($bottom_date);
d1d05d1d
JH
115 }
116}
9015711e 117($bottom_date, undef) = next_date($bottom_date);
d1d05d1d 118
9015711e 119my $cull_old = "--since=" . date_to_seconds($bottom_date);
d1d05d1d
JH
120
121sub plural {
122 my ($number, $singular, $plural) = @_;
123 return ($number == 1) ? "$number $singular": "$number $plural";
124}
125
126sub fmt_join {
127 my ($leader, $limit, $joiner, @ary) = @_;
128 my @result = ();
129 my $width = 0;
130 my $wj = length($joiner);
131 my $wl = length($leader);
132
133 for my $item (@ary) {
134 my $need_joiner;
135 if ($width == 0) {
136 $width += $wl;
137 push @result, $leader;
138 $need_joiner = 0;
139 } else {
140 $need_joiner = 1;
141 }
142 my $len = length($item);
143 if (($need_joiner ? $wj : 0) + $len + $width < $limit) {
144 if ($need_joiner) {
145 $width += $wj;
146 push @result, $joiner;
147 }
148 $width += $len;
149 push @result, $item;
150 } else {
151 if ($width) {
152 push @result, "\n";
153 $width = 0;
154 }
155 $width += $wl;
156 push @result, $leader;
157 $width += $len;
158 push @result, $item;
159 }
160 }
161 push @result, "\n" unless ($result[-1] eq "\n");
162 return join("", @result);
163}
164
9015711e
JH
165################################################################
166# Collection
167
168# Map sha1 to patch information [$sha1, $author, $subject]
169# or merge information [$sha1, undef, $branch].
d1d05d1d 170my %patch;
9015711e
JH
171
172# $dates{"YYYY-MM-DD"} exists iff something happened
d1d05d1d 173my %dates;
9015711e
JH
174
175# List of $sha1 of patches applied, grouped by date
d1d05d1d 176my %patch_by_date;
9015711e
JH
177
178# List of $sha1 of merges, grouped by date
d1d05d1d 179my %merge_by_branch_date;
9015711e
JH
180
181# List of tags, grouped by date
182my %tag_by_date;
183
184# List of integration branches.
d1d05d1d
JH
185my @integrate = (['master', ', to include in the next release'],
186 ['next', ' for public testing'],
187 ['maint', ', to include in the maintenance release'],
188);
189
9015711e
JH
190# Collect individial patch application
191open I, "-|", ("git", "log",
192 "--pretty=%ci %H %an <%ae>\001%s",
d1d05d1d
JH
193 $cull_old, "--glob=refs/heads",
194 "--no-merges") or die;
9015711e 195
d1d05d1d
JH
196while (<I>) {
197 my ($date, $sha1, $rest) = /^([-0-9]+) [:0-9]+ [-+][0-9]{4} ([0-9a-f]+) (.*)$/;
9015711e
JH
198 next unless date_within($date, $bottom_date, $reporting_date);
199
d1d05d1d
JH
200 $patch_by_date{$date} ||= [];
201 push @{$patch_by_date{$date}}, $sha1;
202 my ($name, $subject) = split(/\001/, $rest, 2);
203 $patch{$sha1} = [$sha1, $name, $subject];
204 $dates{$date}++;
205}
206close (I) or die;
207
208for my $branch (map { $_->[0] } @integrate) {
209 open I, "-|", ("git", "log", "--pretty=%ci %H %s",
210 $cull_old,
211 "--first-parent",
212 "--merges",
213 $branch) or die;
214 while (<I>) {
215 my ($date, $sha1, $rest) = /^([-0-9]+) [:0-9]+ [-+][0-9]{4} ([0-9a-f]+) (.*)$/;
9015711e 216 next unless date_within($date, $bottom_date, $reporting_date);
d1d05d1d
JH
217 my $msg = $rest;
218 $msg =~ s/^Merge branch //;
219 $msg =~ s/ into \Q$branch\E$//;
220 $msg =~ s/^'(.*)'$/$1/;
221
222 next if (grep { $_ eq $msg } map { $_->[0] } @integrate);
223
224 $merge_by_branch_date{$branch} ||= {};
225 $merge_by_branch_date{$branch}{$date} ||= [];
226 push @{$merge_by_branch_date{$branch}{$date}}, $sha1;
227 $patch{$sha1} = [$sha1, undef, $msg];
228 $dates{$date}++;
229 }
230 close (I) or die;
231}
232
9015711e
JH
233open I, "-|", ("git", "for-each-ref",
234 "--format=%(refname:short) %(taggerdate:iso)",
235 "refs/tags") or die;
236while (<I>) {
237 my ($tagname, $tagdate) = /^(\S+) ([-0-9]+) [:0-9]+ [-+][0-9]{4}$/;
238
239 if (!defined $tagdate ||
240 !date_within($tagdate, $bottom_date, $reporting_date)) {
241 next;
242 }
243 $dates{$tagdate}++;
244 $tag_by_date{$tagdate} ||= [];
245 push @{$tag_by_date{$tagdate}}, $tagname;
246}
247
248################################################################
249# Summarize
250
d1d05d1d 251my $sep = "";
d1d05d1d
JH
252my @dates = sort keys %dates;
253
9015711e
JH
254sub day_summary {
255 my ($date, $total_names, $total_merges, $total_patches, $total_tags) = @_;
256 return if (!exists $dates{$date});
257
258 print "$sep$date\n" if (!$quiet);
259 if (exists $tag_by_date{$date}) {
260 for my $tagname (@{$tag_by_date{$date}}) {
261 $$total_tags++;
ca2a4aa4 262 print "Tagged $tagname.\n" if (!$quiet);
9015711e
JH
263 }
264 }
265
d1d05d1d
JH
266 if (exists $patch_by_date{$date}) {
267 my $count = scalar @{$patch_by_date{$date}};
268 my %names = ();
269 for my $patch (map { $patch{$_} } (@{$patch_by_date{$date}})) {
270 my $name = $patch->[1];
271 $names{$name}++;
9015711e 272 $total_names->{$name}++;
d1d05d1d
JH
273 }
274 my $people = scalar @{[keys %names]};
9015711e 275 $$total_patches += $count;
d1d05d1d
JH
276
277 $count = plural($count, "patch", "patches");
278 $people = plural($people, "person", "people");
43990219 279 print "Queued $count from $people.\n" if (!$quiet);
d1d05d1d
JH
280 if ($verbose) {
281 for my $patch (map { $patch{$_} } @{$patch_by_date{$date}}) {
9015711e 282 print " $patch->[2]\n";
d1d05d1d
JH
283 }
284 }
285 }
9015711e 286
d1d05d1d
JH
287 for my $branch_data (@integrate) {
288 my ($branch, $purpose) = @{$branch_data};
289 next unless (exists $merge_by_branch_date{$branch}{$date});
290 my $merges = $merge_by_branch_date{$branch}{$date};
291 my $count = scalar @$merges;
292 next unless $count;
293
9015711e
JH
294 $total_merges->{$branch} ||= 0;
295 $total_merges->{$branch} += $count;
d1d05d1d 296 $count = plural($count, "topic", "topics");
43990219 297 print "Merged $count to '$branch' branch$purpose.\n" if (!$quiet);
d1d05d1d 298 if ($verbose) {
9015711e
JH
299 my @pieces = map { $patch{$_}->[2] . "," } @$merges;
300 $pieces[-1] =~ s/,$/./;
301 print fmt_join(" ", 72, " ", @pieces);
d1d05d1d
JH
302 }
303 }
9015711e 304 $sep = "\n" if (!$quiet);
d1d05d1d
JH
305}
306
9015711e
JH
307sub range_summary {
308 my ($range, $bottom, $date, $total_n, $total_m, $total_p, $total_t) = @_;
309 (my $last_date, undef) = prev_date($date);
d1d05d1d 310
9015711e 311 print "$sep$range $bottom..$last_date\n";
d1d05d1d 312
9015711e
JH
313 if ($total_t) {
314 my $count = plural($total_t, "release", "releases");
43990219 315 print "Tagged $count.\n";
9015711e
JH
316 }
317 if ($total_p) {
318 my $people = plural(scalar @{[keys %{$total_n}]}, "person", "people");
319 my$count = plural($total_p, "patch", "patches");
43990219 320 print "Queued $count from $people.\n";
9015711e 321 }
d1d05d1d
JH
322 for my $branch_data (@integrate) {
323 my ($branch, $purpose) = @{$branch_data};
9015711e
JH
324 next unless $total_m->{$branch};
325 my $count = plural($total_m->{$branch}, "merge", "merges");
43990219 326 print "Made $count to '$branch' branch$purpose.\n";
9015711e
JH
327 }
328 $sep = "\n";
329}
330
331sub weekly_summary {
332 my ($bottom, $total_names, $total_merges,
333 $total_patches, $total_tags) = @_;
334 my $date = $bottom;
335 my $shown = 0;
336
337 my ($total_p, $total_t, %total_n, %total_m) = (0, 0);
338 for (my $i = 0; $i < 7; $i++) {
339 day_summary($date, \%total_n, \%total_m,
340 \$total_p, \$total_t);
341 ($date, undef) = next_date($date);
342 }
343 for my $name (keys %total_n) {
344 $total_names->{$name}++;
345 $shown++;
d1d05d1d 346 }
9015711e
JH
347 for my $merge (keys %total_m) {
348 $total_merges->{$merge}++;
349 $shown++;
350 }
351 $$total_patches += $total_p;
352 $$total_tags += $total_t;
353 if ($shown) {
354 range_summary("Week of", $bottom, $date,
355 \%total_n, \%total_m, $total_p, $total_t);
356 }
357 return $date;
358}
359
360my %total_names;
361my %total_merges;
362my $total_patches = 0;
363my $total_tags = 0;
364
365my $date;
366for ($date = $bottom_date; $date le $reporting_date; ) {
367 $date = weekly_summary($date, \%total_names, \%total_merges,
368 \$total_patches, \$total_tags);
369}
370
371if ($weeks) {
372 range_summary("Between", $bottom_date, $date,
373 \%total_names, \%total_merges,
374 $total_patches, $total_tags);
d1d05d1d 375}