File Coverage

blib/lib/Git/DescribeVersion.pm
Criterion Covered Total %
statement 92 105 87.6
branch 36 44 81.8
condition 14 27 51.8
subroutine 16 16 100.0
pod 7 11 63.6
total 165 203 81.2


line stmt bran cond sub pod time code
1             # vim: set ts=2 sts=2 sw=2 expandtab smarttab:
2             #
3             # This file is part of Git-DescribeVersion
4             #
5             # This software is copyright (c) 2010 by Randy Stauner.
6             #
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             #
10 8     8   471239 use strict;
  8         19  
  8         308  
11 8     8   46 use warnings;
  8         15  
  8         505  
12              
13             package Git::DescribeVersion;
14             {
15             $Git::DescribeVersion::VERSION = '1.015';
16             }
17             # git description: v1.014-6-g81d7192
18              
19             BEGIN {
20 8     8   123 $Git::DescribeVersion::AUTHORITY = 'cpan:RWSTAUNER';
21             }
22             # ABSTRACT: Use git-describe to show a repo's version
23              
24 8     8   54 use Carp (); # core
  8         16  
  8         187  
25 8     8   1745 use version 0.82 ();
  8         6393  
  8         17357  
26              
27             our %Defaults = (
28             first_version => 'v0.1',
29             match_pattern => 'v[0-9]*',
30             format => 'decimal',
31             version_regexp => '([0-9._]+)'
32             );
33              
34             # Git::Repository is easier to install than Git::Wrapper
35             my @delegators = qw(
36             git_repository
37             git_wrapper
38             git_backticks
39             );
40              
41              
42             sub new {
43 1248     1248 1 140764 my $class = shift;
44             # accept a hash or hashref
45 1248 100       3334 my %opts = ref($_[0]) ? %{$_[0]} : @_;
  1216         4839  
46 2434         10196 my $self = {
47             %Defaults,
48             # restrict accepted arguments
49 1248         5387 map { $_ => $opts{$_} } grep { exists($opts{$_}) } keys %Defaults
  4992         11643  
50             };
51              
52 1248   50     13737 $self->{directory} = $opts{directory} || '.';
53 1248         2555 bless $self, $class;
54              
55             # accept a Git::Repository or Git::Wrapper object (or command to exec)
56             # or a simple '1' (true value) to indicate which one is desired
57 1248         2261 foreach my $mod ( @delegators ){
58 3744 100       12610 if( $opts{$mod} ){
59 1231         2739 $self->{git} = $mod;
60             # if it's just a true value leave it blank so we create later
61             # TODO: should this be checking ref?
62 1231 100       5528 $self->{$mod} = $opts{$mod}
63             unless $opts{$mod} eq '1';
64             # test that requested method "works"
65 1231         1834 eval { $self->$mod('--version') };
  1231         3608  
66 1231 50       87786 if( $@ ){
67 0         0 Carp::carp qq[Failed to execute $mod (will attempt other methods): $@];
68 0         0 delete @$self{(git => $mod)};
69             }
70             }
71             }
72 1248         5194 return $self;
73             }
74              
75              
76             sub format_version {
77 2312     2312 1 13080 my ($self, $vobject) = @_;
78 2312 100       13567 my $format = $self->{format} =~ /dot|normal|v|string/ ? 'normal' : 'numify';
79 2312         19686 my $version = $vobject->$format;
80 2312 100       11080 $version =~ s/^v// if $self->{format} =~ /no.?v/;
81 2312         85917 return $version;
82             }
83              
84              
85             # NOTE: the git* subs are called in list context
86              
87             sub git {
88 2708     2708 1 9767 my ($self) = @_;
89 2708 50       7264 unless( $self->{git} ){
90 0         0 for my $method ( @delegators ){
91 0   0     0 $self->{git} ||= eval {
92             # confirm method works (without dying)
93 0         0 $self->$method('--version');
94 0         0 $method;
95             };
96             }
97 0 0       0 Carp::croak("All git methods failed. Is `git` installed?\n".
98             "Consider installing Git::Repository or Git::Wrapper.\n")
99             unless $self->{git};
100             }
101 2708         3526 goto &{$self->{git}};
  2708         12350  
102             }
103              
104             sub git_backticks {
105 7     7 0 33 my ($self, $command, @args) = @_;
106 7 50 33     86 warn("'directory' attribute not supported when using backticks.\n" .
107             "Consider installing Git::Repository or Git::Wrapper.\n")
108             if $self->{directory} && $self->{directory} ne '.';
109              
110 7 100       33 @args = map { ref $_ ? @$_ : $_ } @args;
  10         56  
111              
112 7 50       61 @args = map { quotemeta } @args
  13         315  
113             unless $^O eq 'MSWin32';
114              
115 7   100     55 my $exec = join(' ',
116             # the external app to run
117             ($self->{git_backticks} ||= 'git'),
118             $command,
119             @args
120             );
121              
122 7         72922 return (`$exec`);
123             }
124              
125             sub git_repository {
126 3     3 0 10 my ($self, $command, @args) = @_;
127             # Git::Repository 1.22 fails with alternate $/ (rt-71621)
128 3         13 local $/ = "\n";
129             (
130             $self->{git_repository} ||=
131 4 100       24 do {
132 0         0 require Git::Repository;
133 0         0 Git::Repository->new(work_tree => $self->{directory})
134             }
135             )
136             ->run($command,
137 3   33     29 map { ref $_ ? @$_ : $_ } @args
138             );
139             }
140              
141             sub git_wrapper {
142 3929     3929 0 13677 my ($self, $command, @args) = @_;
143 3929         5990 $command =~ tr/-/_/;
144             (
145             $self->{git_wrapper} ||=
146 7204         44630 do {
147 0         0 require Git::Wrapper;
148 0         0 Git::Wrapper->new($self->{directory})
149             }
150             )
151             ->$command({
152 7204 100       23294 map { ($$_[0] =~ /^-{0,2}(.+)$/, $$_[1]) }
153 3929   33     35648 map { ref $_ ? $_ : [$_ => 1] } @args
154             });
155             }
156              
157              
158              
159             sub parse_version {
160 2306     2306 1 80285 my ($self, $prefix, $count) = @_;
161              
162             # This is unlikely as it should mean that both git commands
163             # returned unexpected output. If it does happen, don't die
164             # trying to parse it, default to first_version.
165 2306 100       4993 $prefix = $self->{first_version}
166             unless defined $prefix;
167 2306   100     5627 $count ||= 0;
168              
169             # If still undef (first_version explicitly set to undef)
170             # don't die trying to parse it, just return nothing.
171 2306 100       4338 unless( defined $prefix ){
172 3         220 warn("Version could not be determined.\n");
173 3         17 return;
174             }
175              
176             # s//$1/ requires the regexp to be anchored.
177             # Doing a match and then assigning to $1 does not.
178 2303 100 66     27098 if( $self->{version_regexp} && $prefix =~ /$self->{version_regexp}/ ){
179 2297         5979 $prefix = $1;
180             }
181              
182 2303         5439 my $vstring = "v$prefix.$count";
183              
184             # quote 'version' to reference the module and not call the local sub
185 2303         3334 my $vobject = eval {
186             # don't even try to parse it if it doesn't look like a version
187 2303 100       7319 'version'->parse($vstring)
188             if version::is_lax($vstring);
189             };
190              
191             # Don't die if it's not parseable, just return nothing.
192 2303 100 66     108698 if( my $error = $@ || !$vobject ){
193 9         23 $error = $self->prepare_warning($error);
194 9         491 warn("'$vstring' is not a valid version string.\n$error");
195 9         42 return;
196             }
197              
198 2294         10186 return $self->format_version($vobject);
199             }
200              
201             # normalize error message
202              
203             sub prepare_warning {
204 9     9 0 16 my ($self, $error) = @_;
205 9 50       19 return '' unless $error;
206 9         13 $error =~ s/ at \S+?\.pm line \d+\.?\s*$//;
207 9         19 chomp($error);
208 9         23 return $error . "\n";
209             }
210              
211              
212             sub version {
213 2252     2252 1 701787 my ($self) = @_;
214 2252   66     5712 return $self->version_from_describe() ||
215             $self->version_from_count_objects();
216             }
217              
218              
219             sub version_from_describe {
220 2255     2255 1 9751 my ($self) = @_;
221 2255         3646 my ($ver) = eval {
222 2255         9535 $self->git('describe',
223             ['--match' => $self->{match_pattern}], qw(--tags --long)
224             );
225             };
226             # usually you'll expect a tag to be found, so warn if it isn't
227 2255 50       152144 if( my $error = $@ ){
228 0         0 $error = $self->prepare_warning($error);
229 0         0 warn("git-describe: $error");
230             }
231              
232             # return nothing so we know to move on to count-objects
233 2255 100       6614 return unless $ver;
234              
235             # ignore the -gSHA
236 1805         12969 my ($tag, $count) = ($ver =~ /^(.+?)-(\d+)-(g[0-9a-f]+)$/);
237              
238 1805         5610 return $self->parse_version($tag, $count);
239             }
240              
241              
242             sub version_from_count_objects {
243 453     453 1 858 my ($self) = @_;
244 453         1189 my @counts = $self->git(qw(count-objects -v));
245 453         32751 my $count = 0;
246 453         563 local $_;
247 453         717 foreach (@counts){
248 1206 100       7325 /(count|in-pack): (\d+)/ and $count += $2;
249             }
250 453         1329 return $self->parse_version($self->{first_version}, $count);
251             }
252              
253             1;
254              
255              
256             __END__