9 my $SECS_PER_DAY = 24*3600;
13 daily => $SECS_PER_DAY,
14 weekly => 7*$SECS_PER_DAY,
15 monthly => 30*$SECS_PER_DAY,
16 yearly => 12*30*$SECS_PER_DAY,
20 print "Usage: git chart\n";
27 print "Gathering data ...\n";
32 my $step=$options->{step};
34 open my $fd, '-|', qw(git log --date=raw), '--pretty=%ad %s', @{$options->{cmdline}};
35 die "failed to get revs" unless $fd;
39 my ($date, $tz, $sub) = split ' ', $_, 3;
42 my $key = $date - ($date % $step);
43 if (exists $dataset{$key}) {
52 print "...done, $commits commits in $groups groups.\n";
54 # fill missing steps and find max
55 my @keys = sort keys %dataset;
58 while (my $key = shift @keys) {
59 $max = $dataset{$key} if $max < $dataset{$key};
60 my $next = $key + $step;
61 while (! exists $dataset{$next}) {
64 last if $next >= $last;
68 $options->{max} = $max;
72 # functions to plot the datasets.
73 # each function can be called with either one or two parameters.
74 # when called with two parameters, the first is assumed to be the dataset, and the second the options
75 # (array and hash ref respectively).
76 # when called with a single parameter, it is assumed to be an options hash ref, and the dataset is
77 # created by calling gather_data with the passed options.
80 # TODO needs a lot of customization
81 sub google_chart($;$) {
84 if (! defined $options) {
86 $dataset = gather_data($options);
89 my $height=$options->{chart_height};
90 my $max = $options->{max};
92 my @keys = sort keys %$dataset;
94 my $to = $keys[@keys-1];
97 while (my $key = shift @keys) {
98 push @data, $dataset->{$key};
101 my $width=ceil(4*$height/3);
103 my $url="https://chart.googleapis.com/chart?chs=${width}x${height}&cht=bvg&chd=t:%s&chds=0,$max&chbh=a&chxt=y&chxr=0,0,$max";
105 my $launch = sprintf $url, join(",",@data);
107 # `git web--browse "$launch"`
111 sub gnuplot_chart($;$) {
114 if (! defined $options) {
116 $dataset = gather_data($options);
119 my @keys = sort keys %$dataset;
120 my $step=$options->{step};
122 my $to = $keys[@keys-1];
124 while (my $key = shift @keys) {
125 $data .= "$key $dataset->{$key}\n";
127 my $max = $options->{max};
129 # TODO allow customization
130 # in particular, detect (lack of) display and set term to dumb accordingly
131 my $termcmd = $options->{gnuplot_term};
133 my $plotsetup = $options->{gnuplot_setup};
134 $plotsetup .= "\nset yrange [0:$max]\n";
135 $plotsetup .= "set xrange ['$from':'$to']\n";
136 my ($formatx, $ticks);
137 if ($to - $from > $steps{yearly}) {
139 $ticks = $steps{yearly};
140 } elsif ($to - $from > $steps{monthly}) {
141 $formatx = "%b\\n%Y";
142 $ticks = $steps{monthly};
143 } elsif ($to - $from > 2*$steps{daily}) {
144 $formatx = "%d\\n%b";
145 $ticks = $steps{daily};
148 $ticks = $steps{hourly};
150 $plotsetup .= "set format x \"$formatx\"\n";
151 $plotsetup .= "set xtics $ticks\n";
152 my $plotstyle = $options->{gnuplot_style};
153 my $plotoptions = $options->{gnuplot_plotwith};
155 open my $gp, "|gnuplot -persist";
157 my $gp_script = <<GPCMD
163 plot "-" using 1:2 $plotoptions
168 print STDOUT $gp_script;
169 print $gp $gp_script;
175 step => $SECS_PER_DAY,
177 # charting/plotting options
178 plotter => \&gnuplot_chart,
181 gnuplot_setup => "set nokey",
182 gnuplot_style => 'set style data histeps',
183 gnuplot_plotwith => '',
189 if (exists $steps{$key}) {
190 $options{step} = $steps{$key};
193 die "this can't happen ($key)" unless $key eq 'step';
195 if ($step =~/^\d+$/) {
196 $options{step} = 0 + $step;
199 if (exists $steps{$step}) {
200 $options{step} = $steps{$step};
203 die "unknown step $step";
207 # read our options first
208 Getopt::Long::Configure('pass_through');
210 'hourly' => \&parse_step,
211 'daily' => \&parse_step,
212 'weekly' => \&parse_step,
213 'monthly' => \&parse_step,
214 'yearly' => \&parse_step,
215 'step=s' => sub { parse_step(@_) },
216 'chart-height=i' => sub { $options{chart_height} = $_[1]},
217 google => sub { $options{plotter} = \&google_chart },
218 gnuplot => sub { $options{plotter} = \&gnuplot_chart },
221 # if anything was left, check for log options
223 $options{cmdline} = \@ARGV;
226 die "step must be strictly positive!" unless $options{step} > 0;
228 $options{plotter}->(\%options);