]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Meta/worklog: summarize weekly progress
authorJunio C Hamano <gitster@pobox.com>
Fri, 29 Apr 2011 04:14:15 +0000 (21:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 29 Apr 2011 06:19:05 +0000 (23:19 -0700)
worklog

diff --git a/worklog b/worklog
index a102e7a8f8a608b6af1e2e5caebc99f4a95cf9ba..7b91cefd802b4039d5b2e410ff336602ebd9a969 100755 (executable)
--- a/worklog
+++ b/worklog
@@ -5,67 +5,118 @@ use warnings;
 use Getopt::Long;
 use Time::Local;
 
+################################################################
+
 sub seconds_in_a_week () {
        return 7 * 24 * 3600;
 }
 
-my $verbose = 0;
-my $week;
-my $cycles;
-my $bow = 0;
+# Convert seconds from epoch to datestring and dow
+sub seconds_to_date {
+       my $time = shift;
+       my @time = localtime($time);
+       return (sprintf("%04d-%02d-%02d",
+                       $time[5]+1900, $time[4]+1, $time[3]),
+               $time[6]);
+}
 
-sub bow {
-       my ($week) = @_;
-       my (@time, $time);
+sub date_to_seconds {
+       my $datestring = shift;
+       my ($year, $mon, $mday) = ($datestring =~ /^(\d{4})-(\d{2})-(\d{2})$/);
+       unless (defined $year && defined $mon && defined $mday &&
+               1 <= $mon && $mon <= 12 &&
+               1 <= $mday && $mday <= 31) {
+               die "Bad datestring specification: $datestring";
+       }
+       return timelocal(0, 0, 0, $mday, $mon - 1, $year - 1900);
+}
+
+sub next_date {
+       my $datestring = shift;
+       my $time = date_to_seconds($datestring);
+       return seconds_to_date($time + 3600 * 36);
+}
+
+sub prev_date {
+       my $datestring = shift;
+       my $time = date_to_seconds($datestring);
+       return seconds_to_date($time - 3600 * 12);
+}
+
+sub beginning_of_reporting_week {
+       my ($datestring, $bow) = @_;
+       my ($date, $dow) = seconds_to_date(date_to_seconds($datestring));
+       do {
+               ($date, $dow) = prev_date($date);
+       } while ($dow != $bow);
+       return $date;
+}
 
+sub bow {
+       my ($week, $bow) = @_;
+       my $time;
        if (!defined $week) {
                $time = time;
        } elsif ($week =~ /^\d+$/) {
                $time = time - seconds_in_a_week * $week;
        } else {
-               my ($year, $mon, $mday) = ($week =~ /^(\d{4})-(\d{2})-(\d{2})$/);
-               unless (defined $year && defined $mon && defined $mday &&
-                       1 <= $mon && $mon <= 12 &&
-                       1 <= $mday && $mday <= 31) {
-                       die "Bad week specification: $week";
-               }
-               $time = timelocal(0, 0, 0, $mday, $mon - 1, $year - 1900);
+               $time = date_to_seconds($week);
        }
+       my ($datestring, $dow) = seconds_to_date($time);
+       return beginning_of_reporting_week($datestring, $bow);
+}
 
-       @time = localtime($time);
-       if ($time[6] <= $bow) {
-               $time -= seconds_in_a_week;
-               @time = localtime($time);
-       }
-       $time -= $time[0] + 60 * ($time[1] + 60 * ($time[2] + 24 * ($time[6] - $bow)));
-       return $time;
+sub date_within {
+       my ($date, $bottom, $top) = @_;
+       return ($bottom le $date && $date le $top);
 }
 
+################################################################
+
+my $verbose = 0;
+my $quiet = 0;
+my $reporting_date;
+my $weeks;
+my $bow = 0;
+my $bottom_date;
+
 if (!GetOptions(
             "verbose!" => \$verbose,
-            "week=s" => \$week,
-            "cycles=i" => \$cycles,
+            "quiet!" => \$quiet,
+            "date=s" => \$reporting_date,
+            "weeks=i" => \$weeks,
             "bow=i" => \$bow,
     )) {
-       print STDERR "$0 [-v]\n";
+       print STDERR "$0 [-v|-q] [-d date] [-w weeks] [-b bow]\n";
+       exit 1;
+}
+
+if ($verbose && $quiet) {
+       print STDERR "Which? Verbose, or Quiet?\n";
        exit 1;
 }
 
-if (defined $cycles && !defined $week) {
-       $week = bow($week);
-       $week -= ($cycles - 1) * seconds_in_a_week;
-} else {
-       $week = bow($week);
+if (!defined $reporting_date) {
+       ($reporting_date, my $dow) = seconds_to_date(time);
+       while ($dow != $bow) {
+               ($reporting_date, $dow) = next_date($reporting_date);
+       }
+}
+
+$bottom_date = beginning_of_reporting_week($reporting_date, $bow);
+
+if (!defined $weeks || $weeks < 0) {
+       $weeks = 0;
 }
-if (!defined $cycles) {
-       my $ends = time();
-       my $clock = $week;
-       for ($cycles = 0; $clock < $ends; $cycles++) {
-               $clock += seconds_in_a_week;
+
+for (my $i = 0; $i < $weeks; $i++) {
+       for (my $j = 0; $j < 7; $j++) {
+               ($bottom_date, undef) = prev_date($bottom_date);
        }
 }
+($bottom_date, undef) = next_date($bottom_date);
 
-my $cull_old = "--since=" . $week;
+my $cull_old = "--since=" . date_to_seconds($bottom_date);
 
 sub plural {
        my ($number, $singular, $plural) = @_;
@@ -111,20 +162,41 @@ sub fmt_join {
        return join("", @result);
 }
 
+################################################################
+# Collection
+
+# Map sha1 to patch information [$sha1, $author, $subject]
+# or merge information [$sha1, undef, $branch].
 my %patch;
+
+# $dates{"YYYY-MM-DD"} exists iff something happened
 my %dates;
+
+# List of $sha1 of patches applied, grouped by date
 my %patch_by_date;
+
+# List of $sha1 of merges, grouped by date
 my %merge_by_branch_date;
+
+# List of tags, grouped by date
+my %tag_by_date;
+
+# List of integration branches.
 my @integrate = (['master', ', to include in the next release'],
                 ['next', ' for public testing'],
                 ['maint', ', to include in the maintenance release'],
 );
 
-open I, "-|", ("git", "log", "--pretty=%ci %H %an <%ae>\001%s",
+# Collect individial patch application
+open I, "-|", ("git", "log",
+              "--pretty=%ci %H %an <%ae>\001%s",
               $cull_old, "--glob=refs/heads",
               "--no-merges") or die;
+
 while (<I>) {
        my ($date, $sha1, $rest) = /^([-0-9]+) [:0-9]+ [-+][0-9]{4} ([0-9a-f]+) (.*)$/;
+       next unless date_within($date, $bottom_date, $reporting_date);
+
        $patch_by_date{$date} ||= [];
        push @{$patch_by_date{$date}}, $sha1;
        my ($name, $subject) = split(/\001/, $rest, 2);
@@ -141,6 +213,7 @@ for my $branch (map { $_->[0] } @integrate) {
                       $branch) or die;
        while (<I>) {
                my ($date, $sha1, $rest) = /^([-0-9]+) [:0-9]+ [-+][0-9]{4} ([0-9a-f]+) (.*)$/;
+               next unless date_within($date, $bottom_date, $reporting_date);
                my $msg = $rest;
                $msg =~ s/^Merge branch //;
                $msg =~ s/ into \Q$branch\E$//;
@@ -157,34 +230,60 @@ for my $branch (map { $_->[0] } @integrate) {
        close (I) or die;
 }
 
+open I, "-|", ("git", "for-each-ref",
+              "--format=%(refname:short) %(taggerdate:iso)",
+              "refs/tags") or die;
+while (<I>) {
+       my ($tagname, $tagdate) = /^(\S+) ([-0-9]+) [:0-9]+ [-+][0-9]{4}$/;
+       
+       if (!defined $tagdate || 
+           !date_within($tagdate, $bottom_date, $reporting_date)) {
+               next;
+       }
+       $dates{$tagdate}++;
+       $tag_by_date{$tagdate} ||= [];
+       push @{$tag_by_date{$tagdate}}, $tagname;
+}
+
+################################################################
+# Summarize
+
 my $sep = ""; 
-my %total_names = ();
-my %total_merges = ();
-my $total_patch = 0;
 my @dates = sort keys %dates;
 
-for my $date (@dates) {
-       print "$sep$date\n";
+sub day_summary {
+       my ($date, $total_names, $total_merges, $total_patches, $total_tags) = @_;
+       return if (!exists $dates{$date});
+
+       print "$sep$date\n" if (!$quiet);
+       if (exists $tag_by_date{$date}) {
+               for my $tagname (@{$tag_by_date{$date}}) {
+                       $$total_tags++;
+                       print " Tagged $tagname.\n" if (!$quiet);
+               }
+       }
+
        if (exists $patch_by_date{$date}) {
                my $count = scalar @{$patch_by_date{$date}};
                my %names = ();
                for my $patch (map { $patch{$_} } (@{$patch_by_date{$date}})) {
                        my $name = $patch->[1];
                        $names{$name}++;
-                       $total_names{$name}++;
+                       $total_names->{$name}++;
                }
                my $people = scalar @{[keys %names]};
-               $total_patch += $count;
+               $$total_patches += $count;
 
                $count = plural($count, "patch", "patches");
                $people = plural($people, "person", "people");
-               print "Queued $count from $people.\n";
+               print " Queued $count from $people.\n" if (!$quiet);
                if ($verbose) {
                        for my $patch (map { $patch{$_} } @{$patch_by_date{$date}}) {
-                               print " $patch->[2]\n";
+                               print "  $patch->[2]\n";
                        }
                }
        }
+
        for my $branch_data (@integrate) {
                my ($branch, $purpose) = @{$branch_data};
                next unless (exists $merge_by_branch_date{$branch}{$date});
@@ -192,28 +291,85 @@ for my $date (@dates) {
                my $count = scalar @$merges;
                next unless $count;
 
-               $total_merges{$branch} ||= 0;
-               $total_merges{$branch} += $count;
+               $total_merges->{$branch} ||= 0;
+               $total_merges->{$branch} += $count;
                $count = plural($count, "topic", "topics");
-               print "Merged $count to '$branch' branch$purpose.\n";
+               print " Merged $count to '$branch' branch$purpose.\n" if (!$quiet);
                if ($verbose) {
-                       print fmt_join(" ", 72, " ", (map { $patch{$_}->[2] } @$merges));
+                       my @pieces = map { $patch{$_}->[2] . "," } @$merges;
+                       $pieces[-1] =~ s/,$/./;
+                       print fmt_join("  ", 72, " ", @pieces);
                }
        }
-       $sep = "\n";
+       $sep = "\n" if (!$quiet);
 }
 
-if (1 < @dates) {
-       print "${sep}Between $dates[0]..$dates[-1]\n";
+sub range_summary {
+       my ($range, $bottom, $date, $total_n, $total_m, $total_p, $total_t) = @_;
+       (my $last_date, undef) = prev_date($date);
 
-       my $people = plural(scalar @{[keys %total_names]}, "person", "people");
-       my $count = plural($total_patch, "patch", "patches");
-       print "Queued $count from $people.\n";
+       print "$sep$range $bottom..$last_date\n";
 
+       if ($total_t) {
+               my $count = plural($total_t, "release", "releases");
+               print " Tagged $count.\n";
+       }
+       if ($total_p) {
+               my $people = plural(scalar @{[keys %{$total_n}]}, "person", "people");
+               my$count = plural($total_p, "patch", "patches");
+               print " Queued $count from $people.\n";
+       }
        for my $branch_data (@integrate) {
                my ($branch, $purpose) = @{$branch_data};
-               next unless $total_merges{$branch};
-               my $count = plural($total_merges{$branch}, "merge", "merges");
-               print "Made $count to '$branch' branch$purpose.\n";
+               next unless $total_m->{$branch};
+               my $count = plural($total_m->{$branch}, "merge", "merges");
+               print " Made $count to '$branch' branch$purpose.\n";
+       }
+       $sep = "\n";
+}
+
+sub weekly_summary {
+       my ($bottom, $total_names, $total_merges,
+           $total_patches, $total_tags) = @_;
+       my $date = $bottom;
+       my $shown = 0;
+
+       my ($total_p, $total_t, %total_n, %total_m) = (0, 0);
+       for (my $i = 0; $i < 7; $i++) {
+               day_summary($date, \%total_n, \%total_m,
+                           \$total_p, \$total_t);
+               ($date, undef) = next_date($date);
+       }
+       for my $name (keys %total_n) {
+               $total_names->{$name}++;
+               $shown++;
        }
+       for my $merge (keys %total_m) {
+               $total_merges->{$merge}++;
+               $shown++;
+       }
+       $$total_patches += $total_p;
+       $$total_tags += $total_t;
+       if ($shown) {
+               range_summary("Week of", $bottom, $date,
+                             \%total_n, \%total_m, $total_p, $total_t);
+       }
+       return $date;
+}
+
+my %total_names;
+my %total_merges;
+my $total_patches = 0;
+my $total_tags = 0;
+
+my $date;
+for ($date = $bottom_date; $date le $reporting_date; ) {
+       $date = weekly_summary($date, \%total_names, \%total_merges,
+                              \$total_patches, \$total_tags);
+}
+
+if ($weeks) {
+       range_summary("Between", $bottom_date, $date,
+                     \%total_names, \%total_merges,
+                     $total_patches, $total_tags);
 }