File Coverage

blib/lib/App/Git/Workflow/Command/Committers.pm
Criterion Covered Total %
statement 80 119 67.2
branch 30 58 51.7
condition 5 18 27.7
subroutine 10 14 71.4
pod 6 6 100.0
total 131 215 60.9


line stmt bran cond sub pod time code
1              
2             # Created on: 2014-06-11 10:00:36
3             # Create by: Ivan Wills
4             # $Id$
5             # $Revision$, $HeadURL$, $Date$
6             # $Revision$, $Source$, $Date$
7              
8             use strict;
9 2     2   85865 use warnings;
  2         13  
  2         53  
10 2     2   9 use version;
  2         2  
  2         54  
11 2     2   376 use English qw/ -no_match_vars /;
  2         1639  
  2         10  
12 2     2   546 use Time::Piece;
  2         2871  
  2         29  
13 2     2   1070 use App::Git::Workflow;
  2         10459  
  2         11  
14 2     2   537 use App::Git::Workflow::Command qw/get_options/;
  2         10  
  2         90  
15 2     2   438 use utf8;
  2         4  
  2         114  
16 2     2   510  
  2         15  
  2         18  
17             our $VERSION = version->new(1.1.20);
18             our $workflow = App::Git::Workflow->new;
19             our ($name) = $PROGRAM_NAME =~ m{^.*/(.*?)$}mxs;
20             our %option;
21              
22             my ($self) = @_;
23             %option = (
24 9     9 1 20 period => 'day',
25 9         22 );
26             get_options(
27             \%option,
28 9         37 'remote|r',
29             'all|a',
30             'fmt|format|f=s',
31             'changes|c',
32             'commits|C',
33             'min|min-commits|M=i',
34             'since|s=s',
35             'until|u=s',
36             'period|p=s',
37             'periods|P=i',
38             'merges|m!',
39             );
40              
41             my @stats;
42             my $total_commits = 0;
43 9         12 my $since = $option{since};
44 9         17  
45 9         23 if (!$since) {
46             my $now = localtime;
47 9 100       20 my $period
48 8         29 = $option{period} eq 'day' ? 1
49             : $option{period} eq 'week' ? 7
50             : $option{period} eq 'month' ? 30
51             : $option{period} eq 'year' ? 365
52             : die "Unknown period '$option{period}' please choose one of day, week, month or year\n";
53 8 100       568 $since
    100          
    100          
    100          
54             = $now->wday == 1 ? localtime(time - 3 * $period * 24 * 60 * 60)->ymd
55 7 50       31 : $now->wday == 7 ? localtime(time - 2 * $period * 24 * 60 * 60)->ymd
    50          
56             : localtime(time - 1 * $period * 24 * 60 * 60)->ymd;
57             }
58              
59             my @options;
60             push @options, '-r' if $option{remote};
61 8         583 push @options, '-a' if $option{all};
62 8 100       43 my @log = (
63 8 100       18 '--format=format:%h %an',
64             ($option{merges} ? () : '--no-merges'),
65             );
66 8 100       22  
67             my $periods = $option{periods} || 1;
68             while ($periods--) {
69 8   50     30 my $commits = 0;
70 8         18 my %users;
71 8         11 my @dates;
72 8         20 if ($option{periods}) {
73             @dates = $self->dates($option{period}, $option{periods}--);
74 8 50       22 }
75 0         0 else {
76             @dates = (
77             "--since=$since",
78             ($option{until} ? "--until=$option{until}" : ()),
79             );
80 8 50       26 }
81              
82             for my $branch ($workflow->git->branch(@options)) {
83             next if $branch =~ / -> /;
84 8         30 $branch =~ s/^[*]?\s*//;
85 9 100       24 for my $log ( $workflow->git->log( @log, @dates, $branch, '--' ) ) {
86 8         26 my ($hash, $name) = split /\s/, $log, 2;
87 8         23 $users{$name}{$hash} = 1;
88 24         68 $commits++;
89 24         57 }
90 24         39 }
91              
92             for my $user (keys %users) {
93             my $commits = $users{$user};
94 8         22 $users{$user} = {
95 16         23 commit_count => scalar keys %{ $users{$user} },
96             $option{commits} ? (commits => [keys %{ $users{$user} }]) : (),
97 16         77 $option{changes} ? (changes => $self->changes($commits)) : (),
98 0         0 };
99 16 50       30 }
    50          
100             my $dates = join ' - ',
101             map {/=(.*)$/; $1}
102             @dates;
103 8         17 push @stats, {
  8         43  
  8         34  
104             period => $dates,
105 8 50       43 ( %users ? (commits => $commits) : () ),
    50          
106             ( %users ? (users => \%users ) : () ),
107             };
108             $total_commits += $commits;
109             }
110 8         23  
111             my $fmt = 'fmt_' . ($option{fmt} || 'table');
112             if ($self->can($fmt)) {
113 8   50     31 $self->$fmt(\@stats, $total_commits);
114 8 50       53 }
115 8         24  
116             return;
117             }
118 8         47  
119             my ($self, $period, $count) = @_;
120              
121             my $now = localtime;
122 0     0 1 0 $period
123             = $period eq 'day' ? 1
124 0         0 : $period eq 'week' ? 7 - $now->wdaygg
125 0 0       0 : $period eq 'month' ? 30
    0          
    0          
    0          
126             : $period eq 'year' ? 365
127             : die "Unknown period '$option{period}' please choose one of day, week, month or year\n";
128              
129             my $until = localtime(time - ($count - 1) * $period * 24 * 60 * 60);
130             my $since
131             = $until->wday == 1 ? localtime(time - 3 * $count * $period * 24 * 60 * 60)
132 0         0 : $until->wday == 7 ? localtime(time - 2 * $count * $period * 24 * 60 * 60)
133 0 0       0 : localtime(time - 1 * $count * $period * 24 * 60 * 60);
    0          
134              
135             return (
136             "--since=" . $since->ymd,
137             "--until=" . $until->ymd,
138             );
139 0         0 }
140              
141             my ($self, $commits) = @_;
142             my %changes = (
143             lines_added => 0,
144             lines_removed => 0,
145 0     0 1 0 files => {},
146 0         0 files_added => 0,
147             files_removed => 0,
148             );
149              
150             for my $commit (keys %$commits) {
151             # get the stats from each commit
152             my @show = $workflow->git->show($commit);
153             $changes{lines_added} += grep {/^[+](?:[^+]|[+][^+]|[+][+]\s|$)/} @show;
154 0         0 $changes{lines_removed} += grep {/^[-](?:[^-]|[-][^-]|[-][-]\s|$)/} @show;
155             $changes{files} = {
156 0         0 %{ $changes{files} || {} },
157 0         0 map {/^[+]{3}\s+b\/(.*)$/; ($1 || "" => 1) }
  0         0  
158 0         0 grep {/^[+]{3}\s/}
  0         0  
159             @show
160 0 0       0 };
161 0   0     0 $changes{total}++;
  0         0  
162 0         0 }
  0         0  
163             $changes{files} = keys %{ $changes{files} || {} };
164              
165 0         0 return \%changes;
166             }
167 0 0       0  
  0         0  
168             my ($self, $stats) = @_;
169 0         0 my $fmt = "%-25s % 7d";
170             my $max = 1;
171             my $users = $stats->[0]{users} || {};
172             my %totals = (
173 8     8 1 19 commit_count => 0,
174 8         11 lines_added => 0,
175 8         11 lines_removed => 0,
176 8   50     17 files => 0,
177 8         34 );
178              
179             if ($option{changes}) {
180             $fmt .= " % 9d % 9d % 5d";
181             my $fmt2 = $fmt;
182             $fmt2 =~ s/d/s/g;
183             printf "$fmt2\n", qw/Name Commits Added Removed Files/;
184 8 50       17 $max = 4;
185 0         0 }
186 0         0  
187 0         0 my @users =
188 0         0 reverse sort {$users->{$a}{commit_count} <=> $users->{$b}{commit_count}}
189 0         0 grep { $users->{$_}{commit_count} >= ($option{min} || 0) }
190             keys %$users;
191              
192             for my $user (@users) {
193 8         33 %totals = (
194 8   50     20 commit_count => ($totals{commit_count} + $users->{$user}{commit_count} || 0),
  16         84  
195             $users->{$user}{changes} ? (
196             lines_added => ($totals{lines_added} + ($users->{$user}{changes}{lines_added} || 0)),
197 8         15 lines_removed => ($totals{lines_removed} + ($users->{$user}{changes}{lines_removed} || 0)),
198             files => ($totals{files} + ($users->{$user}{changes}{files} || 0)),
199             ) : ()
200             );
201             my @out = (
202             $user,
203 16 50 50     84 $users->{$user}{commit_count},
      0        
      0        
      0        
204             $users->{$user}{changes}{lines_added},
205             $users->{$user}{changes}{lines_removed},
206             $users->{$user}{changes}{files},
207             );
208             printf "$fmt\n", @out[0..$max];
209             }
210             if ($option{changes}) {
211             my @out = (
212 16         56 'Total',
213 16         543 $totals{commit_count},
214             $totals{lines_added},
215 8 50       32 $totals{lines_removed},
216             $totals{files},
217             );
218             printf "\n$fmt\n", @out[0..$max];
219             }
220             else {
221             print "Total commits = $totals{commit_count}\n";
222 0         0 }
223 0         0  
224             return;
225             }
226 8         96  
227             my ($self, $users, $total) = @_;
228             require JSON;
229 8         34  
230             print JSON::encode_json({ total => $total, users => $users });
231             }
232              
233 0     0 1   my ($self, $users, $total) = @_;
234 0           require Data::Dumper;
235              
236 0           local $Data::Dumper::Indent = 1;
237             print Data::Dumper::Dumper({ total => $total, users => $users });
238             }
239              
240 0     0 1   1;
241 0            
242              
243 0           =head1 NAME
244 0            
245             git-committers - Stats on the number of commits by committer
246              
247             =head1 VERSION
248              
249             This documentation refers to git-committers version 1.1.20
250              
251             =head1 SYNOPSIS
252              
253             git-committers [option]
254              
255             OPTIONS:
256             -r --remote Committers to remote branches
257             -a --all Committers to any branch (remote or local)
258             -c --changes Add stats for lines added/removed
259             -C --commits Output the individual commits (with --format json)
260             -s --since[=]YYYY-MM-DD
261             Only commits since this date
262             -u --until[=]YYYY-MM-DD
263             Only commits up until this date
264             -f --format[=](table|json|csv)
265             Change how the data is presented
266             - table : shows the data in a simple table
267             - json : returns the raw data as a json object
268             - perl : Dump the data structure
269             -p --period=[day|week|month|year]
270             If --since is not specified this works out the date for the
271             last day/week/month/year
272             -P --periods[=]int
273             Generate stats for more than one period.
274             -M --min-commit[=]int
275             Only show stats for users with at least this number of commits
276             -m --merges Count merge commits
277             --no-merges
278             Don't count merge commits
279              
280             -v --verbose Show more detailed option
281             --version Prints the version information
282             --help Prints this help information
283             --man Prints the full documentation for git-committers
284              
285             =head1 DESCRIPTION
286              
287             The C<git-committers> command allows to get statistics on who is committing
288             to the git repository.
289              
290             =head1 SUBROUTINES/METHODS
291              
292             =head2 C<run ()>
293              
294             Executes the git workflow command
295              
296             =head2 C<dates ($period, $count)>
297              
298             Returns the C<--since> and C<--until> dates for the C<$period> specified
299              
300             =head2 C<changes ($commits)>
301              
302             Calculates the changes for C<$commits>.
303              
304             =head2 C<fmt_table ()>
305              
306             Output a table
307              
308             =head2 C<fmt_json ()>
309              
310             Output JSON
311              
312             =head2 C<fmt_perl ()>
313              
314             Output a Perl object
315              
316             =head1 DIAGNOSTICS
317              
318             =head1 CONFIGURATION AND ENVIRONMENT
319              
320             =head1 DEPENDENCIES
321              
322             =head1 INCOMPATIBILITIES
323              
324             =head1 BUGS AND LIMITATIONS
325              
326             There are no known bugs in this module.
327              
328             Please report problems to Ivan Wills (ivan.wills@gmail.com).
329              
330             Patches are welcome.
331              
332             =head1 AUTHOR
333              
334             Ivan Wills - (ivan.wills@gmail.com)
335              
336             =head1 LICENSE AND COPYRIGHT
337              
338             Copyright (c) 2014 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
339             All rights reserved.
340              
341             This module is free software; you can redistribute it and/or modify it under
342             the same terms as Perl itself. See L<perlartistic>. This program is
343             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
344             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
345             PARTICULAR PURPOSE.
346              
347             =cut