File Coverage

blib/lib/App/Git/Workflow/Command/BranchAge.pm
Criterion Covered Total %
statement 30 115 26.0
branch 0 48 0.0
condition 0 12 0.0
subroutine 10 13 76.9
pod 3 3 100.0
total 43 191 22.5


line stmt bran cond sub pod time code
1              
2             # Created on: 2014-03-11 20:58:59
3             # Create by: Ivan Wills
4             # $Id$
5             # $Revision$, $HeadURL$, $Date$
6             # $Revision$, $Source$, $Date$
7              
8             use strict;
9 1     1   1010 use warnings;
  1         2  
  1         25  
10 1     1   4 use version;
  1         8  
  1         25  
11 1     1   5 use English qw/ -no_match_vars /;
  1         2  
  1         6  
12 1     1   70 use List::MoreUtils qw/zip/;
  1         2  
  1         7  
13 1     1   751 use Term::ANSIColor qw/colored/;
  1         10126  
  1         6  
14 1     1   857 use App::Git::Workflow;
  1         3  
  1         53  
15 1     1   5 use App::Git::Workflow::Command qw/get_options/;
  1         1  
  1         36  
16 1     1   29 use DateTime::Format::HTTP;
  1         14  
  1         38  
17 1     1   449 use Data::Dumper qw/Dumper/;
  1         413015  
  1         33  
18 1     1   9  
  1         2  
  1         970  
19             our $VERSION = version->new(1.1.20);
20             our $workflow = App::Git::Workflow->new;
21             our ($name) = $PROGRAM_NAME =~ m{^.*/(.*?)$}mxs;
22             our %option = (
23             master => 'origin/master',
24             );
25              
26             get_options(
27             \%option,
28 0     0 1   'all|a',
29             'remote|r',
30             'reverse|R',
31             'unmerged|u!',
32             'master|m=s',
33             'limit|n=i',
34             'format|f=s',
35             'files|F!',
36             'quiet|q',
37             );
38             my $fmt = join "-%09-%09-", qw/
39             %(authordate)
40 0           %(authoremail)
41             %(authorname)
42             %(body)
43             %(HEAD)
44             %(objectname)
45             %(objecttype)
46             %(refname)
47             %(refname:short)
48             %(subject)
49             /;
50             my @headings = qw/
51             authordate
52 0           authoremail
53             authorname
54             body
55             HEAD
56             objectname
57             objecttype
58             refname
59             short
60             subject
61             /;
62              
63             my $arg = '';
64             if ( $option{remote} ) {
65 0           $arg .= ' -r';
66 0 0         }
67 0           my $match = '';
68             my $files;
69 0           if (@ARGV) {
70 0           if ($option{files}) {
71 0 0         $files = join ' ', @ARGV;
72 0 0         }
73 0           else {
74             $match = @ARGV ? shift @ARGV : '';
75             }
76 0 0         }
77              
78             my @branches = `git branch $arg --format='$fmt'`;
79             my $i = 0;
80 0           my $last = '';
81 0           my @data;
82 0            
83 0           for my $branch (@branches) {
84             chomp $branch;
85 0           if ($last) {
86 0           $last .= "\n";
87 0 0         }
88 0           $last .= $branch;
89             my @cols = split /-\t-\t-/, $last;
90 0           if (@cols < @headings) {
91 0           next;
92 0 0         }
93 0            
94             $last = '';
95             $branch = { zip @headings, @cols };
96 0           next if $match && $branch->{short} !~ /$match/;
97 0           warn 'bad head' if !$branch->{HEAD};
98 0 0 0       next if !$branch->{HEAD};
99 0 0         if ( defined $option{unmerged} ) {
100 0 0         next if unmerged($branch->{short}, $option{master});
101 0 0         }
102 0 0          
103             if ($files) {
104             my $date = `git log -n 1 --format=format:%ai $branch->{short} -- $files`;
105 0 0         chomp $date;
106 0            
107 0           if ($date) {
108             $date = $branch->{authordate};
109 0 0         }
110 0           }
111              
112             my ($date, $tz) = $branch->{authordate} =~ /^(.*)\s+([+-]\d{4})$/;
113             if ($date && $tz) {
114 0           $branch->{age} = DateTime::Format::HTTP->parse_datetime($date, $tz)->iso8601;
115 0 0 0       }
116 0           else {
117             $Data::Dumper::Sortkeys = 1;
118             $Data::Dumper::Indent = 1;
119 0           die Dumper $branch;
120 0           }
121 0           push @data, $branch;
122             }
123 0            
124             my %max = map {$_ => length $_} @headings;
125             for my $branch (@data) {
126 0           for my $key (keys %{$branch}) {
  0            
127 0           $max{$key} = length $branch->{$key} if !$max{$key} || $max{$key} < length $branch->{$key};
128 0           }
  0            
129 0 0 0       }
130              
131             @data = sort {$a->{age} cmp $b->{age}} @data;
132             if ($option{reverse}) {
133 0           @data = reverse @data;
  0            
134 0 0         }
135 0            
136             my $count = 1;
137             my $fmt_out = $option{verbose} ? "%-age\t%-authorname\t%-short"
138 0           : $option{quiet} ? '%short'
139             : $option{format} ? $option{format}
140             : "%age\t%short";
141             my ($format, @fields) = formatted($fmt_out, \%max);
142 0 0          
    0          
    0          
143 0           if ($option{limit} && @data > $option{limit}) {
144             @data = splice @data, @data - $option{limit}, $option{limit};
145 0 0 0       }
146 0           for my $branch (@data) {
147             printf $format, map {$branch->{$_}} @fields;
148 0           }
149 0           }
  0            
150              
151             my ($format, $max) = @_;
152             my @fields;
153             my $fmt = '';
154 0     0 1   my @fmt_parts = split /%([+-]?)(\((?:[a-z]+)\)|[a-z]+)/, $format;
155 0            
156 0           while (defined (my $fixed = shift @fmt_parts)) {
157 0           my $align = shift @fmt_parts;
158             my $name = shift @fmt_parts;
159 0           $name =~ s/^[(]|[)]$//g;
160 0           push @fields, $name;
161 0           $fmt .= $fixed . ( $align ? "%$align$max->{$name}s" : "%s" );
162 0           }
163 0            
164 0 0         return ("$fmt\n", @fields);
165             }
166              
167 0           my %dest;
168             my ($source, $dest) = @_;
169              
170             if ( ! $dest{$dest} ) {
171             @{$dest{$dest}} = map {/^(.*)\n/; $1} `git log --format=format:%H $dest`;
172 0     0 1   die "No destination branch commits for '$dest'" if !@{$dest{$dest}};
173             }
174 0 0          
175 0           my $source_sha = `git log --format=format:%H -n 1 $source`;
  0            
  0            
  0            
176 0 0         chomp $source_sha;
  0            
177              
178             return scalar grep {$_ && $_ eq $source_sha} @{$dest{$dest}};
179 0           }
180 0            
181             1;
182 0 0          
  0            
  0            
183              
184             =head1 NAME
185              
186             git-branch-age - grep tags
187              
188             =head1 VERSION
189              
190             This documentation refers to git-branch-age version 1.1.20
191              
192             =head1 SYNOPSIS
193              
194             git-branch-age [option] regex
195              
196             OPTIONS:
197             regex grep's perl (-P) regular expression
198             -a --all All branches (remote and local
199             -r --remote Remote branches only
200             -R --reverse Reverse the branch sort order
201             -u --unmerged
202             Only show branches not merged to --master
203             --no-unmerged
204             Only show branches merged to master
205             -m --master[=]str
206             Branch to check against for --unmerged and --no-unmerged
207             (Default origin/master)
208             -n --limit[=]int
209             Limit the out put to this number
210             -f --format[=]str
211             Specify a format for the output
212             eg normal format would be --format="%age %short"
213             verbose format would be --format="%age %authorname $short"
214             format keys:
215             - authordate
216             - authoremail
217             - authorname
218             - body
219             - HEAD
220             - objectname
221             - objecttype
222             - refname
223             - short
224             - subject
225              
226             -v --verbose Show more detailed option
227             --version Prints the version information
228             --help Prints this help information
229             --man Prints the full documentation for git-branch-age
230              
231             =head1 DESCRIPTION
232              
233             Short hand for running
234              
235             C<git branch | grep -P 'regex'>
236              
237             =head1 SUBROUTINES/METHODS
238              
239             =head2 C<run ()>
240              
241             Executes the git workflow command
242              
243             =head2 C<unmerged ($source, $dest)>
244              
245             Check if there are any commits in C<$source> that are not in C<$dest>
246              
247             =head2 C<formatted ($format, $max)>
248              
249             Creates a format for printf to output a line
250              
251             =head1 DIAGNOSTICS
252              
253             =head1 CONFIGURATION AND ENVIRONMENT
254              
255             =head1 DEPENDENCIES
256              
257             =head1 INCOMPATIBILITIES
258              
259             =head1 BUGS AND LIMITATIONS
260              
261             There are no known bugs in this module.
262              
263             Please report problems to Ivan Wills (ivan.wills@gmail.com).
264              
265             Patches are welcome.
266              
267             =head1 AUTHOR
268              
269             Ivan Wills - (ivan.wills@gmail.com)
270              
271             =head1 LICENSE AND COPYRIGHT
272              
273             Copyright (c) 2014 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
274             All rights reserved.
275              
276             This module is free software; you can redistribute it and/or modify it under
277             the same terms as Perl itself. See L<perlartistic>. This program is
278             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
279             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
280             PARTICULAR PURPOSE.
281              
282             =cut