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.005';
3 4     4   290812 use strict;
  4         48  
  4         118  
4 4     4   36 use warnings;
  4         7  
  4         92  
5 4     4   28 use Exporter;
  4         13  
  4         119  
6 4     4   29 use Carp ();
  4         8  
  4         2947  
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 172     172 1 20171 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             (?:[.-]?[a-z]+[0-9]+)? # rc or vendor specific suffixes
66             (?:[.-](GIT|[1-9][0-9]*[.-]g[A-Fa-f0-9]+))? # devel
67             (?:\ .*)? # comment
68             $/x;
69             }
70              
71             sub _normalize {
72 108     108   13383 my ($v) = @_;
73 108 100       244 return undef if !defined $v;
74              
75             # minimal consistency check
76 107 100       192 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 106         627 $v =~ s/^v|^git version |\.[a-zA-Z]+\..*|[\012\015]+\z//g;
80 106         236 $v =~ y/-/./;
81 106         225 $v =~ s/0rc/0.rc/;
82 106         268 ($v) = split / /, $v; # drop anything after the version
83              
84             # can't use exists() because the assignment in the @ops created the slot
85 106 100       362 return $version_alias{$v} if defined $version_alias{$v};
86              
87             # split the dotted version string
88 96         224 my @v = split /\./, $v;
89 96         174 my ( $r, $c ) = ( 0, 0 );
90              
91             # commit count since the previous tag
92 96 100       211 ($c) = ( 1, splice @v, -1 ) if $v[-1] eq 'GIT'; # before 1.4
93 96 100       230 ($c) = splice @v, -2 if substr( $v[-1], 0, 1 ) eq 'g'; # after 1.4
94              
95             # release candidate number
96 96 100       215 ($r) = splice @v, -1 if substr( $v[-1], 0, 2 ) eq 'rc';
97 96   66     221 $r &&= do { $r =~ s/rc//; sprintf '-%02d', $r };
  21         60  
  21         95  
98              
99             # compute and cache normalized string
100 96   100     1346 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   32 no strict 'refs';
  4         9  
  4         654  
108 1935   33 1935   5731 *{"${op}_git"} = eval << "OP";
  1935   33     8845  
  1935   33     9226  
  1935   100     5368  
  1935   33     8517  
  1935   33     9288  
  1935         5695  
  1935         8586  
  1935         8866  
  1936         558159  
  1936         10198  
  1935         9462  
  1935         5509  
  1935         8549  
  1935         9313  
  1935         5554  
  1935         9221  
  1935         9056  
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 2752     2752 1 7756 my ( $v1, $v2 ) = @_;
119 2752   66     10949 $_ = $version_alias{$_} ||= _normalize( $_ ) for $v1, $v2;
120 2752         9824 return $v1 cmp $v2;
121             }
122              
123             1;
124              
125             __END__