-
Notifications
You must be signed in to change notification settings - Fork 1
/
git-graphs
executable file
·253 lines (189 loc) · 6.53 KB
/
git-graphs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use List::Util qw{ max };
use Getopt::Long qw( :config no_ignore_case_always bundling );
use File::Temp qw{ tempfile };
use Time::Piece;
use Pod::Usage qw{ pod2usage };
my ($weekdayfile, $dayfile, $monthfile, $weekfile, $linesfile)
= qw( weekday.png day_commits.png month_commits.png week_commits.png
lines.png
);
my $user = '^';
my $prefix = q();
GetOptions(
'weekday|W' => \ my $skip_weekday,
'day|d' => \ my $skip_day,
'month|m' => \ my $skip_month,
'week|w' => \ my $skip_week,
'lines|l' => \ my $skip_lines,
'loc-limit|L=i' => \ my $loc_limit,
'author|a=s' => \$user,
'prefix|p=s' => \$prefix,
'help|h' => \ my $help,
'man|M' => \ my $man,
) or die "Invalid command line option.\n";
pod2usage(-verbose => 1,
-exitval => 0) if $help;
pod2usage(-verbose => 2,
-exitval => 0) if $man;
if ($skip_weekday and $skip_day and $skip_month and $skip_week and $skip_lines) {
pod2usage('All graphs skipped!');
}
if ($skip_lines and defined $loc_limit) {
warn "WARNING: loc-limit has no meaning when lines are skipped.\n";
}
$user = qr/$user/;
substr $_, 0, 0, $prefix
for $weekdayfile, $dayfile, $monthfile, $weekfile, $linesfile;
my (%weekday, %day_commits, %month_commits, %week_commits, %lines);
load();
draw_circ() unless $skip_weekday;
draw_bar($dayfile, 'day', '%Y-%m-%d', 1, \%day_commits) unless $skip_day;
draw_bar($monthfile, 'month', '%Y-%m', 2e6, \%month_commits) unless $skip_month;
draw_bar($weekfile, 'week', '%Y-%j', 5e5, \%week_commits) unless $skip_week;
draw_lines($linesfile, \%lines) unless $skip_lines;
exit;
sub load {
open my $GIT, '-|', qw{ git log --numstat} or die $!;
my $include;
my $date;
while (<$GIT>) {
$include = /$user/ if s/^Author: //;
if (s/^Date:\s+// and $include) {
s/ [-+][0-9]{4}\n//; # Remove DST info.
chomp;
my $time = 'Time::Piece'->strptime($_, '%a %b %d %T %Y');
$date = $time->ymd;
my $dow = $time->day_of_week;
++$weekday{$dow}{ $time->hour }
unless $skip_weekday;
++$day_commits{ $time->ymd } unless $skip_day;
if (not($skip_week and $skip_month)) {
my $year = $time->year;
my $month = $time->mon;
++$month_commits{ sprintf "$year-%02d", $month }
unless $skip_month;
next if $skip_week;
# Rewind to nearest Monday.
$dow--;
$dow = 6 if -1 == $dow;
$time -= $dow * 24 * 60 * 60;
$year = $time->year;
my $monday = 1 + $time->yday;
++$week_commits{ "$year-$monday" };
}
}
if ($include && $date && ! $skip_lines
&& (my ($plus, $minus) = /^([0-9]+)\s*([0-9]+)/)
) {
$lines{plus}{$date} += $plus;
$lines{minus}{$date} += $minus;
}
}
}
sub draw_circ {
my $max = max(map values %$_, values %weekday);
open my $GNUPLOT, '|-', 'gnuplot' or die $!;
print {$GNUPLOT} << "__GNUPLOT__";
set term pngcairo size 1024, 600
set output "$weekdayfile"
set title "Commits per weekday"
set xrange [-1:24]
set yrange [0.5:7.5]
set ytics("Sun" 7, "Mon" 6, "Tue" 5, "Wed" 4, "Thu" 3, "Fri" 2, "Sat" 1)
set xtics 0, 1, 23
plot '-' using 1:(7-\$2):3 with circles fs solid lc 3 notitle
__GNUPLOT__
for my $day (keys %weekday) {
for my $hour (keys %{ $weekday{$day} }) {
say {$GNUPLOT} "$hour\t$day\t", $weekday{$day}{$hour} / 2 / $max;
}
}
close $GNUPLOT;
}
sub draw_bar {
my ($file, $period, $format, $width, $hash) = @_;
open my $GNUPLOT, '|-', 'gnuplot' or die $!;
print {$GNUPLOT} << "__GNUPLOT__";
set term pngcairo size 1800, 600
set output "$file"
set title "Commits per $period"
set yrange [0:]
set xdata time
set timefmt "$format"
set xtics format "%y/%m/%d"
plot '-' using 1:2:($width) with boxes fs solid notitle
__GNUPLOT__
for my $date (keys %$hash) {
say {$GNUPLOT} "$date\t$hash->{$date}";
}
close $GNUPLOT;
}
sub draw_lines {
my ($file, $per_day) = @_;
my ($TMP, $tmpfile) = tempfile();
say {$TMP} "$_\t$per_day->{plus}{$_}" for sort keys %{ $per_day->{plus} };
say {$TMP} "\n";
say {$TMP} "$_\t$per_day->{minus}{$_}" for sort keys %{ $per_day->{minus} };
close $TMP or die $!;
my $data_lines = defined $loc_limit
? "\$2 > $loc_limit ? $loc_limit : \$2"
: '$2';
my $title = 'Lines of code';
$title .= " (limit $loc_limit)" if defined $loc_limit;
open my $GNUPLOT, '|-', 'gnuplot' or die $!;
print {$GNUPLOT} << "__GNUPLOT__";
set term pngcairo size 1800, 600
set output "$file"
set title "$title"
set xdata time
set timefmt "%Y-%m-%d"
set xtics format "%y/%m/%d"
set xzeroaxis lt 1 lc 0
plot '$tmpfile' index 0 using 1:($data_lines) with steps lc 2 title "+", \\
'' index 1 using 1:(-($data_lines)) with steps lc 1 title "-"
__GNUPLOT__
close $GNUPLOT;
}
=head1 NAME
git-graphs - Show git repo activity
=head1 SYNOPSIS
git-graphs [-a AUTHOR-REGEX] [-p PREFIX] [-dwmWl] [-L LIMIT]
git-graphs [ -h | -M ]
=head1 DESCRIPTION
B<cd> to your git repository directory and run the script.
B<git-graphs> can draw 5 types of activity graphs:
=over 4
=item Weekday
For each weekday, show how many commits appear in each hour.
=item Daily, Weekly, Monthly
Show how many commits there are for each day, week, or month.
=item Lines
Shows how many lines were added and/or deleted.
=back
=head1 OPTIONS
=over 4
=item B<-a AUTHOR-REGEX>
Specify what regex the author must match to be included in the
graphs. Default: C<^>, i.e. all authors.
=item B<-p PREFIX>
A string to be prepended to each output file name.
=item B<-dwmWl>
What type of graph B<not> to draw: C<d> stands for daily, C<w> work
weekly, C<m> for monthly, C<W> for weekday, C<l> for lines. It's an
error to draw no graph.
=item B<-L LIMIT>
In the "lines" graph, don't show values greater then LIMIT.
=item B<-h>, B<-M>
Show help or the manual page.
=back
You can also use long options: --author, --prefix, --day, --week,
--month, --weekday, --lines, --loc-limit, --help, --man.
=head1 DEPENDENCY
git-graphs needs B<gnuplot> to draw the graphs.
=head1 AUTHOR
(c) E. Choroba 2015
=cut