A little git activity graphing tool
authorTony Finch <dot@dotat.at>
Fri, 27 Mar 2015 12:33:26 +0000 (12:33 +0000)
committerTony Finch <dot@dotat.at>
Fri, 27 Mar 2015 12:33:26 +0000 (12:33 +0000)
git-graph [new file with mode: 0755]

diff --git a/git-graph b/git-graph
new file mode 100755 (executable)
index 0000000..a4f269e
--- /dev/null
+++ b/git-graph
@@ -0,0 +1,99 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Getopt::Long qw{:config gnu_getopt posix_default};
+use Term::ReadKey;
+
+$0 =~ s{^.*/([^/]+)$}{$1};
+
+sub stop { exit 1; } # because 'die' uses a weird exit status
+sub wail { warn "$0: @_\n"; }
+sub fail { wail @_; stop; }
+sub fale { fail "@_: $!"; }
+
+my %opt;
+GetOptions(\%opt, qw{
+       cancel
+       overlay
+       stack
+}) or stop;
+
+$opt{stack} = 1 unless $opt{cancel} or $opt{overlay};
+
+my @log = qx{git log --pretty=format:%ai --shortstat};
+
+my %add;
+my %del;
+
+my $addmax = 0;
+my $delmax = 0;
+my $canmax = 0;
+
+while (@log) {
+       my $date = shift @log;
+       my $stat = shift @log;
+       my $blank = shift @log;
+       fail "bad blank line $blank"
+           unless not defined $blank or $blank eq "\n";
+       fail "truncated git log output"
+           unless defined $date and defined $stat;
+       chomp ($date, $stat);
+       $date =~ s{^([0-9-]{10}) [0-9:]{8} [+-][0-9]{4}$}{$1}
+           or fail "bad date $date";
+       $stat =~ m{^\ \d+\ files?\ changed
+                  (?:,\ (\d+)\ insertion[s()+]+)?
+                  (?:,\ (\d+)\ deletion[s()-]+)?$}x
+           or fail "bad stats $stat";
+       my ($add,$del) = ($1 || 0, $2 || 0);
+       $add{$date} += $add;
+       $del{$date} += $del;
+       $addmax = $add{$date} if $addmax < $add{$date};
+       $delmax = $del{$date} if $delmax < $del{$date};
+       my $can = $add{$date} > $del{$date} ?
+                 $add{$date} - $del{$date} :
+                 $del{$date} - $add{$date};
+       $canmax = $can if $canmax < $can;
+}
+
+my $max = $opt{cancel} ? $canmax :
+    $opt{stack} ? $addmax + $delmax :
+    $addmax > $delmax ? $addmax : $delmax;
+my $width = [GetTerminalSize]->[0] - 12;
+my $scale = $width / $max;
+
+for my $date (sort keys %add) {
+       my $add;
+       my $del;
+       my $mod;
+       if ($opt{cancel}) {
+               if ($add{$date} > $del{$date}) {
+                       $add = $add{$date} - $del{$date};
+                       $del = $mod = 0;
+               } else {
+                       $del = $del{$date} - $add{$date};
+                       $add = $mod = 0;
+               }
+       }
+       if ($opt{overlay}) {
+               if ($add{$date} > $del{$date}) {
+                       $add = $add{$date} - $del{$date};
+                       $del = 0;
+                       $mod = $del{$date};
+               } else {
+                       $del = $del{$date} - $add{$date};
+                       $add = 0;
+                       $mod = $add{$date};
+               }
+       }
+       if ($opt{stack}) {
+               $add = $add{$date};
+               $del = $del{$date};
+               $mod = 0;
+       }
+       printf "$date \e[33m%s\e[32m%s\e[31m%s\e[0m\n",
+           "#" x ($mod * $scale),
+           "+" x ($add * $scale),
+           "-" x ($del * $scale);
+}