File Coverage

blib/lib/Git/Version/Compare.pm
Criterion Covered Total %
statement 54 54 100.0
branch 12 12 100.0
condition 14 26 53.8
subroutine 9 9 100.0
pod 2 2 100.0
total 91 103 88.3


line stmt bran cond sub pod time code
1             package Git::Version::Compare;
2             $Git::Version::Compare::VERSION = '1.004';
3 4     4   56952 use strict;
  4         5  
  4         102  
4 4     4   11 use warnings;
  4         5  
  4         68  
5 4     4   12 use Exporter;
  4         4  
  4         91  
6 4     4   15 use Carp ();
  4         8  
  4         1764  
7              
8             my @ops = qw( lt gt le ge eq ne );
9              
10             our @ISA = qw(Exporter);
11             our @EXPORT_OK = ( looks_like_git => map "${_}_git", cmp => @ops );
12             our %EXPORT_TAGS = ( ops => [ map "${_}_git", @ops ], all => \@EXPORT_OK );
13              
14             # A few versions have two tags, or non-standard numbering:
15             # - the left-hand side is what `git --version` reports
16             # - the right-hand side is an internal canonical name
17             #
18             # We turn versions into strings, so we can use the fast `eq` and `gt`.
19             # The 6 elements are integers padded with 0:
20             # - the 4 parts of the dotted version (padded with as many .0 as needed)
21             # - '.000' if not an RC, or '-xxx' if an RC (- sorts before . in ascii)
22             # - the number of commits since the previous tag (for dev versions)
23             #
24             # The special cases are pre-computed below, the rest is computed as needed.
25             my %version_alias = (
26             '0.99.7a' => '00.99.07.01.00.0000',
27             '0.99.7b' => '00.99.07.02.00.0000',
28             '0.99.7c' => '00.99.07.03.00.0000',
29             '0.99.7d' => '00.99.07.04.00.0000',
30             '0.99.8a' => '00.99.08.01.00.0000',
31             '0.99.8b' => '00.99.08.02.00.0000',
32             '0.99.8c' => '00.99.08.03.00.0000',
33             '0.99.8d' => '00.99.08.04.00.0000',
34             '0.99.8e' => '00.99.08.05.00.0000',
35             '0.99.8f' => '00.99.08.06.00.0000',
36             '0.99.8g' => '00.99.08.07.00.0000',
37             '0.99.9a' => '00.99.09.01.00.0000',
38             '0.99.9b' => '00.99.09.02.00.0000',
39             '0.99.9c' => '00.99.09.03.00.0000',
40             '0.99.9d' => '00.99.09.04.00.0000',
41             '0.99.9e' => '00.99.09.05.00.0000',
42             '0.99.9f' => '00.99.09.06.00.0000',
43             '0.99.9g' => '00.99.09.07.00.0000',
44             '0.99.9h' => '00.99.09.08.00.0000', # 1.0.rc1
45             '1.0.rc1' => '00.99.09.08.00.0000',
46             '0.99.9i' => '00.99.09.09.00.0000', # 1.0.rc2
47             '1.0.rc2' => '00.99.09.09.00.0000',
48             '0.99.9j' => '00.99.09.10.00.0000', # 1.0.rc3
49             '1.0.rc3' => '00.99.09.10.00.0000',
50             '0.99.9k' => '00.99.09.11.00.0000',
51             '0.99.9l' => '00.99.09.12.00.0000', # 1.0.rc4
52             '1.0.rc4' => '00.99.09.12.00.0000',
53             '0.99.9m' => '00.99.09.13.00.0000', # 1.0.rc5
54             '1.0.rc5' => '00.99.09.13.00.0000',
55             '0.99.9n' => '00.99.09.14.00.0000', # 1.0.rc6
56             '1.0.rc6' => '00.99.09.14.00.0000',
57             '1.0.0a' => '01.00.01.00.00.0000',
58             '1.0.0b' => '01.00.02.00.00.0000',
59             );
60              
61             sub looks_like_git {
62 169     169 1 18396 return scalar $_[0] =~
63             /^(?:v|git\ version\ )? # prefix
64             [0-9]+(?:[.-](?:0[ab]?|[1-9][0-9a-z]*|[a-zA-Z]+))* # x.y.z.*
65             (?:[.-]?rc[0-9]+)? # rc
66             (?:[.-](GIT|[1-9][0-9]*[.-]g[A-Fa-f0-9]+))? # devel
67             (?:\ .*)? # comment
68             $/x;
69             }
70              
71             sub _normalize {
72 106     106   8478 my ($v) = @_;
73 106 100       162 return undef if !defined $v;
74              
75             # minimal consistency check
76 105 100       112 Carp::croak "$v does not look like a Git version" if !looks_like_git($v);
77              
78             # reformat git.git tag names, output of `git --version`
79 104         358 $v =~ s/^v|^git version |\.[a-zA-Z]+\..*|[\012\015]+\z//g;
80 104         116 $v =~ y/-/./;
81 104         95 $v =~ s/0rc/0.rc/;
82 104         178 ($v) = split / /, $v; # drop anything after the version
83              
84             # can't use exists() because the assignment in the @ops created the slot
85 104 100       279 return $version_alias{$v} if defined $version_alias{$v};
86              
87             # split the dotted version string
88 94         150 my @v = split /\./, $v;
89 94         80 my ( $r, $c ) = ( 0, 0 );
90              
91             # commit count since the previous tag
92 94 100       121 ($c) = ( 1, splice @v, -1 ) if $v[-1] eq 'GIT'; # before 1.4
93 94 100       154 ($c) = splice @v, -2 if substr( $v[-1], 0, 1 ) eq 'g'; # after 1.4
94              
95             # release candidate number
96 94 100       140 ($r) = splice @v, -1 if substr( $v[-1], 0, 2 ) eq 'rc';
97 94   66     122 $r &&= do { $r =~ s/rc//; sprintf '-%02d', $r };
  21         37  
  21         64  
98              
99             # compute and cache normalized string
100 94   100     1078 return $version_alias{$v} =
101             join( '.', map sprintf( '%02d', $_ ), ( @v, 0, 0, 0 )[ 0 .. 3 ] )
102             . ( $r || '.00' )
103             . sprintf( '.%04d', $c );
104             }
105              
106             for my $op (@ops) {
107 4     4   17 no strict 'refs';
  4         4  
  4         438  
108 1899   33 1899   2356 *{"${op}_git"} = eval << "OP";
  1899   33     7085  
  1899   66     6545  
  1899   33     2155  
  1899   33     6810  
  1899   66     6690  
  1900         2575  
  1900         6843  
  1899         6449  
  1899         2237  
  1899         7203  
  1899         6533  
  1899         2203  
  1899         7221  
  1899         6704  
  1899         266791  
  1899         7660  
  1899         7039  
109             sub {
110             my ( \$v1, \$v2 ) = \@_;
111             \$_ = \$version_alias{\$_} ||= _normalize( \$_ ) for \$v1, \$v2;
112             return \$v1 $op \$v2;
113             }
114             OP
115             }
116              
117             sub cmp_git ($$) {
118 2701     2701 1 3720 my ( $v1, $v2 ) = @_;
119 2701   66     9154 $_ = $version_alias{$_} ||= _normalize( $_ ) for $v1, $v2;
120 2701         7371 return $v1 cmp $v2;
121             }
122              
123             1;
124              
125             __END__