File Coverage

blib/lib/Git/Repository/Command.pm
Criterion Covered Total %
statement 91 91 100.0
branch 56 58 96.5
condition 25 32 78.1
subroutine 19 19 100.0
pod 2 2 100.0
total 193 202 95.5


line stmt bran cond sub pod time code
1             package Git::Repository::Command;
2             $Git::Repository::Command::VERSION = '1.323';
3 16     16   107 use strict;
  16         33  
  16         457  
4 16     16   82 use warnings;
  16         45  
  16         466  
5 16     16   286 use 5.006;
  16         55  
6              
7 16     16   121 use Carp;
  16         35  
  16         1311  
8 16     16   100 use Cwd qw( cwd );
  16         30  
  16         665  
9 16     16   9001 use IO::Handle;
  16         102968  
  16         887  
10 16     16   128 use Scalar::Util qw( blessed );
  16         38  
  16         1138  
11 16     16   110 use File::Spec;
  16         29  
  16         323  
12 16     16   8590 use System::Command;
  16         226330  
  16         72  
13              
14             our @ISA = qw( System::Command );
15              
16             # a few simple accessors
17             for my $attr (qw( pid stdin stdout stderr exit signal core )) {
18 16     16   1108 no strict 'refs';
  16         38  
  16         1065  
19 2798     2798   913409 *$attr = sub { return $_[0]{$attr} };
20             }
21             for my $attr (qw( cmdline )) {
22 16     16   102 no strict 'refs';
  16         35  
  16         20991  
23 2     2   71 *$attr = sub { return @{ $_[0]{$attr} } };
  2         99  
24             }
25              
26             sub _which {
27 38     38   400 my @pathext = ('');
28             push @pathext,
29             $^O eq 'MSWin32' ? split ';', $ENV{PATHEXT}
30 38 50       785 : $^O eq 'cygwin' ? qw( .com .exe .bat )
    50          
31             : ();
32              
33 38         1093 for my $path ( File::Spec->path ) {
34 190         660 for my $ext (@pathext) {
35 190         2421 my $binary = File::Spec->catfile( $path, $_[0] . $ext );
36 190 100 100     3738 return $binary if -x $binary && !-d _;
37             }
38             }
39              
40             # not found
41 10         51 return undef;
42             }
43              
44             # CAN I HAS GIT?
45             my %binary; # cache calls to _is_git
46             sub _is_git {
47 368     368   33790 my ( $binary, @args ) = @_;
48 368         1226 my $args = join "\0", @args;
49              
50             # git option might be an arrayref containing an executable with arguments
51             # Best that can be done is to check if the first part is executable
52             # and use the arguments as part of the cache key
53              
54             # compute cache key:
55             # - filename (path-rel): $CWD \0 $PATH
56             # - filename (path): $PATH
57             # - absolute path (abs): empty string
58             # - relative path (rel): dirname
59 368 100 66     5469 my $path = defined $ENV{PATH} && length( $ENV{PATH} ) ? $ENV{PATH} : '';
60 368 100       854060 my ( $type, $key ) =
    100          
    100          
61             ( File::Spec->splitpath($binary) )[2] eq $binary
62             ? grep( !File::Spec->file_name_is_absolute($_), File::Spec->path )
63             ? ( 'path-rel', join "\0", cwd(), $path )
64             : ( 'path', $path )
65             : File::Spec->file_name_is_absolute($binary) ? ( 'abs', '' )
66             : ( 'rel', cwd() );
67              
68             # This relatively complex cache key scheme allows PATH or cwd to change
69             # during the life of a program using Git::Repository, which is likely
70             # to happen. On the other hand, it completely ignores the possibility
71             # that any part of the cached path to a git binary could be a symlink
72             # which target may also change during the life of the program.
73              
74             # check the cache
75             return $binary{$type}{$key}{$binary}{$args}
76 368 100       18861 if exists $binary{$type}{$key}{$binary}{$args};
77              
78             # compute a list of candidate files (look in PATH if needed)
79 49 100       1816 my $git = $type =~ /^path/
80             ? _which($binary)
81             : File::Spec->rel2abs($binary);
82 49 100 100     1758 $git = File::Spec->rel2abs($git)
83             if defined $git && $type eq 'path-rel';
84              
85             # if we can't find any, we're done
86 49 100 100     1351 return $binary{$type}{$key}{$binary} = undef
87             if !( defined $git && -x $git );
88              
89             # try to run it
90 35         914 my $cmd = System::Command->new( $git, @args, '--version' );
91 35   50     221229 my $version = do { local $/ = "\n"; $cmd->stdout->getline; } || '';
92 35         29159 $cmd->close;
93              
94             # does it really look like git?
95 35 100       10365 return $binary{$type}{$key}{$binary}{$args}
    100          
96             = $version =~ /^git version \d/
97             ? $type eq 'path'
98             ? $binary # leave the shell figure it out itself too
99             : $git
100             : undef;
101             }
102              
103             sub new {
104 350     350 1 1566 my ( $class, @cmd ) = @_;
105              
106             # split the args
107 350         983 my (@r, @o);
108 350 100 100     6882 @cmd = # take out the first Git::Repository in $r, and options in @o
    100          
109             grep !( blessed $_ && $_->isa('Git::Repository') ? push @r, $_ : 0 ),
110             grep !( ref eq 'HASH' ? push @o, $_ : 0 ),
111             @cmd;
112              
113             # wouldn't know what to do with more than one Git::Repository object
114 350 100       1595 croak "Too many Git::Repository objects given: @r" if @r > 1;
115 349         903 my $r = shift @r;
116              
117             # keep changes to the environment local
118 349         31861 local %ENV = %ENV;
119              
120             # a Git::Repository object will give more context
121 349 100       2538 if ($r) {
122              
123             # pick up repository options
124 141         1841 unshift @o, $r->options;
125              
126             # get some useful paths
127 141         1148 my ( $git_dir, $work_tree ) = ( $r->git_dir, $r->work_tree );
128 141 100 66     1515 unshift @o, { cwd => $work_tree }
129             if defined $work_tree && length $work_tree;
130              
131             # setup our %ENV
132 141         2038 delete @ENV{qw( GIT_DIR GIT_WORK_TREE )};
133 141         838 $ENV{GIT_DIR} = $git_dir;
134 141 100       959 $ENV{GIT_WORK_TREE} = $work_tree
135             if defined $work_tree;
136             }
137              
138             # pick up the modified PATH, if any
139             exists $_->{env} and exists $_->{env}{PATH} and $ENV{PATH} = $_->{env}{PATH}
140 349   66     2197 for @o;
      33        
141              
142             # extract and process the 'fatal' option
143             push @o, {
144             fatal => {
145             128 => 1, # fatal
146             129 => 1, # usage
147             map s/^-// ? ( $_ => '' ) : ( $_ => 1 ),
148             map /^!0$/ ? ( 1 .. 255 ) : $_,
149             map ref() ? @$_ : $_, grep defined, map $_->{fatal}, @o
150 349 100       7204 }
    100          
    100          
151             };
152              
153             # get and check the git command
154 349 100       1359 my $git_cmd = ( map { exists $_->{git} ? $_->{git} : () } @o )[-1];
  769         2158  
155              
156             # git option might be an arrayref containing an executable with arguments
157             # (e.g. [ qw( /usr/bin/sudo -u nobody git ) ] )
158 349 100       1363 ( $git_cmd, my @args )
    100          
159             = defined $git_cmd ? ref $git_cmd ? @$git_cmd : ($git_cmd) : ('git');
160 349         1898 my $git = _is_git($git_cmd, @args);
161              
162 349 100       7228 croak sprintf "git binary '%s' not available or broken",
163             join( ' ', $git_cmd, @args ) # show the full command given
164             if !defined $git;
165              
166             # turn us into a dumb terminal
167 342         6899 delete $ENV{TERM};
168              
169             # spawn the command and re-bless the object in our class
170 342         9243 return bless System::Command->new( $git, @args, @cmd, @o ), $class;
171             }
172              
173             sub final_output {
174 337     337 1 1757 my ($self, @cb) = @_;
175              
176             # get output / errput
177 337         1324 my ( @output, @errput );
178             $self->loop_on(
179             input_record_separator => "\n",
180 311     311   14350 stdout => sub { chomp( my $o = shift ); push @output, $o; },
  311         2055  
181 117     117   2134 stderr => sub { chomp( my $e = shift ); push @errput, $e; },
  117         372  
182 337         12086 );
183              
184             # done with it
185 337         2882 $self->close;
186              
187             # fatal exit codes set by the 'fatal' option
188             # when working with fatal => '!0' it's helpful to be able to show the exit status
189             # so that specific exit codes can be made non-fatal if desired.
190 337 100       17561 if ( $self->options->{fatal}{ $self->exit } ) {
191 18   66     5105 croak join( "\n", @errput ) || 'fatal: unknown git error, exit status '.$self->exit;
192             }
193              
194             # something else's wrong
195 319 100 100     1968 if ( @errput && !$self->options->{quiet} ) { carp join "\n", @errput; }
  7         2942  
196              
197             # process the output with the optional callbacks
198 319         3639 for my $cb (@cb) {
199 3         63 @output = map $cb->($_), @output;
200             }
201              
202             # return the output
203 319 100       12317 return wantarray ? @output : join "\n", @output;
204             }
205              
206             1;
207              
208             __END__