git-graph: allow user to adjust horizontal scale
[u/fanf2/git-graph.git] / git-graph
index a4f269e..278e424 100755 (executable)
--- a/git-graph
+++ b/git-graph
@@ -4,25 +4,25 @@ use warnings;
 use strict;
 
 use Getopt::Long qw{:config gnu_getopt posix_default};
+use POSIX;
 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;
+       cancel|c
+       height|h=i
+       overlay|o
+       stack|s
+       width|w=i
+}) or exit 1;
 
-$opt{stack} = 1 unless $opt{cancel} or $opt{overlay};
+$opt{overlay} = 1 unless $opt{cancel} or $opt{stack};
 
-my @log = qx{git log --pretty=format:%ai --shortstat};
+my @terminal = GetTerminalSize;
+my $width = $terminal[0] - 11;
+my $height = $opt{height} // $terminal[1] - 2;
 
 my %add;
 my %del;
@@ -31,69 +31,68 @@ 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 $date;
+
+for (qx{git log --pretty=format:%ct --shortstat --since='$height days ago'}) {
+       next if m{^\s*$};
+       if (m{^(\d{10})\n$}) {
+               $date = int ($1 / 86400);
+               next;
+       }
+       m{^\ \d+\ files?\ changed
+         (?:,\ (\d+)\ insertion[s()+]+)?
+          (?:,\ (\d+)\ deletion[s()-]+)?$}x
+           or die "$0: bad stats $_";
        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};
+       my $can = abs $add{$date} - $del{$date};
        $canmax = $can if $canmax < $can;
 }
 
-my $max = $opt{cancel} ? $canmax :
+my $max = $opt{width} ? $opt{width} :
+    $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}) {
+my $today = int (time / 86400);
+
+for my $date ($today - $height .. $today) {
+       my $add = 0;
+       my $del = 0;
+       my $mod = 0;
+       $add{$date} //= 0;
+       $del{$date} //= 0;
+       if ($opt{cancel} or $opt{overlay}) {
                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};
-               }
+               $mod = $add{$date} < $del{$date} ?
+                      $add{$date} : $del{$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);
+       my $greg = strftime "%b %d %w", localtime $date * 86400;
+       $greg =~ s{(\d)$}{substr "sMTWTFs", $1, 1}e;
+       my $line = sprintf "$greg \e[33m%s\e[32m%s\e[31m%s\e[0m\n",
+           "#" x ($mod && 1 + $mod * $scale),
+           "+" x ($add && 1 + $add * $scale),
+           "-" x ($del && 1 + $del * $scale);
+       if (length $line > $width) {
+               $line = sprintf "## %d ##\e[32m++ %d ++\e[31m-- %d --\e[0m\n",
+                   $mod, $add, $del;
+               $line = "$greg \e[33m" . "#"x($width + 16 - length $line) . $line;
+       }
+       print $line;
 }
+printf "         \e[33m#\e[0m <- %-8.1f %s %8d ->\n",
+    1/$scale, " " x ($width - 25), $max;