File Coverage

lib/App/gh/Git.pm
Criterion Covered Total %
statement 31 438 7.0
branch 1 210 0.4
condition 0 43 0.0
subroutine 10 85 11.7
pod 33 33 100.0
total 75 809 9.2


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             App::gh::Git - Perl interface to the App::gh::Git version control system
4              
5             =cut
6              
7              
8             package App::gh::Git;
9              
10 2     2   63 use 5.008;
  2         8  
  2         69  
11 2     2   10 use strict;
  2         4  
  2         281  
12              
13              
14             BEGIN {
15              
16 2     2   19 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);
17              
18             # Totally unstable API.
19 2         4 $VERSION = '0.01';
20              
21              
22             =head1 SYNOPSIS
23              
24             use App::gh::Git;
25              
26             my $version = App::gh::Git::command_oneline('version');
27              
28             git_cmd_try { App::gh::Git::command_noisy('update-server-info') }
29             '%s failed w/ code %d';
30              
31             my $repo = App::gh::Git->repository (Directory => '/srv/git/cogito.git');
32              
33              
34             my @revs = $repo->command('rev-list', '--since=last monday', '--all');
35              
36             my ($fh, $c) = $repo->command_output_pipe('rev-list', '--since=last monday', '--all');
37             my $lastrev = <$fh>; chomp $lastrev;
38             $repo->command_close_pipe($fh, $c);
39              
40             my $lastrev = $repo->command_oneline( [ 'rev-list', '--all' ],
41             STDERR => 0 );
42              
43             my $sha1 = $repo->hash_and_insert_object('file.txt');
44             my $tempfile = tempfile();
45             my $size = $repo->cat_blob($sha1, $tempfile);
46              
47             =cut
48              
49              
50 2         11 require Exporter;
51              
52 2         32 @ISA = qw(Exporter);
53              
54 2         7 @EXPORT = qw(git_cmd_try);
55              
56             # Methods which can be called as standalone functions as well:
57 2         11923 @EXPORT_OK = qw(command command_oneline command_noisy
58             command_output_pipe command_input_pipe command_close_pipe
59             command_bidi_pipe command_close_bidi_pipe
60             version exec_path html_path hash_object git_cmd_try
61             remote_refs
62             temp_acquire temp_release temp_reset temp_path);
63              
64              
65             =head1 DESCRIPTION
66              
67             This module provides Perl scripts easy way to interface the App::gh::Git version control
68             system. The modules have an easy and well-tested way to call arbitrary Git
69             commands; in the future, the interface will also provide specialized methods
70             for doing easily operations which are not totally trivial to do over
71             the generic command interface.
72              
73             While some commands can be executed outside of any context (e.g. 'version'
74             or 'init'), most operations require a repository context, which in practice
75             means getting an instance of the App::gh::Git object using the repository() constructor.
76             (In the future, we will also get a new_repository() constructor.) All commands
77             called as methods of the object are then executed in the context of the
78             repository.
79              
80             Part of the "repository state" is also information about path to the attached
81             working copy (unless you work with a bare repository). You can also navigate
82             inside of the working copy using the C method. (Note that
83             the repository object is self-contained and will not change working directory
84             of your process.)
85              
86             TODO: In the future, we might also do
87              
88             my $remoterepo = $repo->remote_repository (Name => 'cogito', Branch => 'master');
89             $remoterepo ||= App::gh::Git->remote_repository ('http://git.or.cz/cogito.git/');
90             my @refs = $remoterepo->refs();
91              
92             Currently, the module merely wraps calls to external App::gh::Git tools. In the future,
93             it will provide a much faster way to interact with App::gh::Git by linking directly
94             to libgit. This should be completely opaque to the user, though (performance
95             increase notwithstanding).
96              
97             =cut
98              
99              
100 2     2   10 use Carp qw(carp croak); # but croak is bad - throw instead
  2         3  
  2         119  
101 2     2   1778 use Error qw(:try);
  2         7432  
  2         11  
102 2     2   467 use Cwd qw(abs_path cwd);
  2         4  
  2         113  
103 2     2   2007 use IPC::Open2 qw(open2);
  2         11697  
  2         181  
104 2     2   21 use Fcntl qw(SEEK_SET SEEK_CUR);
  2         3  
  2         103  
105             }
106              
107              
108             =head1 CONSTRUCTORS
109              
110             =over 4
111              
112             =item repository ( OPTIONS )
113              
114             =item repository ( DIRECTORY )
115              
116             =item repository ()
117              
118             Construct a new repository object.
119             C are passed in a hash like fashion, using key and value pairs.
120             Possible options are:
121              
122             B - Path to the App::gh::Git repository.
123              
124             B - Path to the associated working copy; not strictly required
125             as many commands will happily crunch on a bare repository.
126              
127             B - Subdirectory in the working copy to work inside.
128             Just left undefined if you do not want to limit the scope of operations.
129              
130             B - Path to the App::gh::Git working directory in its usual setup.
131             The C<.git> directory is searched in the directory and all the parent
132             directories; if found, C is set to the directory containing
133             it and C to the C<.git> directory itself. If no C<.git>
134             directory was found, the C is assumed to be a bare repository,
135             C is set to point at it and C is left undefined.
136             If the C<$GIT_DIR> environment variable is set, things behave as expected
137             as well.
138              
139             You should not use both C and either of C and
140             C - the results of that are undefined.
141              
142             Alternatively, a directory path may be passed as a single scalar argument
143             to the constructor; it is equivalent to setting only the C option
144             field.
145              
146             Calling the constructor with no options whatsoever is equivalent to
147             calling it with C<< Directory => '.' >>. In general, if you are building
148             a standard porcelain command, simply doing C<< App::gh::Git->repository() >> should
149             do the right thing and setup the object to reflect exactly where the user
150             is right now.
151              
152             =cut
153              
154             sub repository {
155 0     0 1   my $class = shift;
156 0           my @args = @_;
157 0           my %opts = ();
158 0           my $self;
159              
160 0 0         if (defined $args[0]) {
161 0 0         if ($#args % 2 != 1) {
162             # Not a hash.
163 0 0         $#args == 0 or throw Error::Simple("bad usage");
164 0           %opts = ( Directory => $args[0] );
165             } else {
166 0           %opts = @args;
167             }
168             }
169              
170 0 0 0       if (not defined $opts{Repository} and not defined $opts{WorkingCopy}
      0        
171             and not defined $opts{Directory}) {
172 0           $opts{Directory} = '.';
173             }
174              
175 0 0         if (defined $opts{Directory}) {
176 0 0         -d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!");
177              
178 0           my $search = App::gh::Git->repository(WorkingCopy => $opts{Directory});
179 0           my $dir;
180             try {
181 0     0     $dir = $search->command_oneline(['rev-parse', '--git-dir'],
182             STDERR => 0);
183             } catch App::gh::Git::Error::Command with {
184 0     0     $dir = undef;
185 0           };
186              
187 0 0         if ($dir) {
188 0 0         $dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir;
189 0           $opts{Repository} = abs_path($dir);
190              
191             # If --git-dir went ok, this shouldn't die either.
192 0           my $prefix = $search->command_oneline('rev-parse', '--show-prefix');
193 0           $dir = abs_path($opts{Directory}) . '/';
194 0 0         if ($prefix) {
195 0 0         if (substr($dir, -length($prefix)) ne $prefix) {
196 0           throw Error::Simple("rev-parse confused me - $dir does not have trailing $prefix");
197             }
198 0           substr($dir, -length($prefix)) = '';
199             }
200 0           $opts{WorkingCopy} = $dir;
201 0           $opts{WorkingSubdir} = $prefix;
202              
203             } else {
204             # A bare repository? Let's see...
205 0           $dir = $opts{Directory};
206              
207 0 0 0       unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
      0        
208             # Mimic git-rev-parse --git-dir error message:
209 0           throw Error::Simple("fatal: Not a git repository: $dir");
210             }
211 0           my $search = App::gh::Git->repository(Repository => $dir);
212             try {
213 0     0     $search->command('symbolic-ref', 'HEAD');
214             } catch App::gh::Git::Error::Command with {
215             # Mimic git-rev-parse --git-dir error message:
216 0     0     throw Error::Simple("fatal: Not a git repository: $dir");
217             }
218              
219 0           $opts{Repository} = abs_path($dir);
220             }
221              
222 0           delete $opts{Directory};
223             }
224              
225 0           $self = { opts => \%opts };
226 0           bless $self, $class;
227             }
228              
229             =back
230              
231             =head1 METHODS
232              
233             =over 4
234              
235             =item command ( COMMAND [, ARGUMENTS... ] )
236              
237             =item command ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
238              
239             Execute the given App::gh::Git C (specify it without the 'git-'
240             prefix), optionally with the specified extra C.
241              
242             The second more elaborate form can be used if you want to further adjust
243             the command execution. Currently, only one option is supported:
244              
245             B - How to deal with the command's error output. By default (C)
246             it is delivered to the caller's C. A false value (0 or '') will cause
247             it to be thrown away. If you want to process it, you can get it in a filehandle
248             you specify, but you must be extremely careful; if the error output is not
249             very short and you want to read it in the same process as where you called
250             C, you are set up for a nice deadlock!
251              
252             The method can be called without any instance or on a specified App::gh::Git repository
253             (in that case the command will be run in the repository context).
254              
255             In scalar context, it returns all the command output in a single string
256             (verbatim).
257              
258             In array context, it returns an array containing lines printed to the
259             command's stdout (without trailing newlines).
260              
261             In both cases, the command's stdin and stderr are the same as the caller's.
262              
263             =cut
264              
265             sub command {
266 0     0 1   my ($fh, $ctx) = command_output_pipe(@_);
267              
268 0 0         if (not defined wantarray) {
    0          
269             # Nothing to pepper the possible exception with.
270 0           _cmd_close($fh, $ctx);
271              
272             } elsif (not wantarray) {
273 0           local $/;
274 0           my $text = <$fh>;
275             try {
276 0     0     _cmd_close($fh, $ctx);
277             } catch App::gh::Git::Error::Command with {
278             # Pepper with the output:
279 0     0     my $E = shift;
280 0           $E->{'-outputref'} = \$text;
281 0           throw $E;
282 0           };
283 0           return $text;
284              
285             } else {
286 0           my @lines = <$fh>;
287 0   0       defined and chomp for @lines;
288             try {
289 0     0     _cmd_close($fh, $ctx);
290             } catch App::gh::Git::Error::Command with {
291 0     0     my $E = shift;
292 0           $E->{'-outputref'} = \@lines;
293 0           throw $E;
294 0           };
295 0           return @lines;
296             }
297             }
298              
299              
300             =item command_oneline ( COMMAND [, ARGUMENTS... ] )
301              
302             =item command_oneline ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
303              
304             Execute the given C in the same way as command()
305             does but always return a scalar string containing the first line
306             of the command's standard output.
307              
308             =cut
309              
310             sub command_oneline {
311 0     0 1   my ($fh, $ctx) = command_output_pipe(@_);
312              
313 0           my $line = <$fh>;
314 0 0         defined $line and chomp $line;
315             try {
316 0     0     _cmd_close($fh, $ctx);
317             } catch App::gh::Git::Error::Command with {
318             # Pepper with the output:
319 0     0     my $E = shift;
320 0           $E->{'-outputref'} = \$line;
321 0           throw $E;
322 0           };
323 0           return $line;
324             }
325              
326              
327             =item command_output_pipe ( COMMAND [, ARGUMENTS... ] )
328              
329             =item command_output_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
330              
331             Execute the given C in the same way as command()
332             does but return a pipe filehandle from which the command output can be
333             read.
334              
335             The function can return C<($pipe, $ctx)> in array context.
336             See C for details.
337              
338             =cut
339              
340             sub command_output_pipe {
341 0     0 1   _command_common_pipe('-|', @_);
342             }
343              
344              
345             =item command_input_pipe ( COMMAND [, ARGUMENTS... ] )
346              
347             =item command_input_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
348              
349             Execute the given C in the same way as command_output_pipe()
350             does but return an input pipe filehandle instead; the command output
351             is not captured.
352              
353             The function can return C<($pipe, $ctx)> in array context.
354             See C for details.
355              
356             =cut
357              
358             sub command_input_pipe {
359 0     0 1   _command_common_pipe('|-', @_);
360             }
361              
362              
363             =item command_close_pipe ( PIPE [, CTX ] )
364              
365             Close the C as returned from C, checking
366             whether the command finished successfully. The optional C argument
367             is required if you want to see the command name in the error message,
368             and it is the second value returned by C when
369             called in array context. The call idiom is:
370              
371             my ($fh, $ctx) = $r->command_output_pipe('status');
372             while (<$fh>) { ... }
373             $r->command_close_pipe($fh, $ctx);
374              
375             Note that you should not rely on whatever actually is in C;
376             currently it is simply the command name but in future the context might
377             have more complicated structure.
378              
379             =cut
380              
381             sub command_close_pipe {
382 0     0 1   my ($self, $fh, $ctx) = _maybe_self(@_);
383 0   0       $ctx ||= '';
384 0           _cmd_close($fh, $ctx);
385             }
386              
387             =item command_bidi_pipe ( COMMAND [, ARGUMENTS... ] )
388              
389             Execute the given C in the same way as command_output_pipe()
390             does but return both an input pipe filehandle and an output pipe filehandle.
391              
392             The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>.
393             See C for details.
394              
395             =cut
396              
397             sub command_bidi_pipe {
398 0     0 1   my ($pid, $in, $out);
399 0           my ($self) = _maybe_self(@_);
400 0           local %ENV = %ENV;
401 0           my $cwd_save = undef;
402 0 0         if ($self) {
403 0           shift;
404 0           $cwd_save = cwd();
405 0           _setup_git_cmd_env($self);
406             }
407 0           $pid = open2($in, $out, 'git', @_);
408 0 0         chdir($cwd_save) if $cwd_save;
409 0           return ($pid, $in, $out, join(' ', @_));
410             }
411              
412             =item command_close_bidi_pipe ( PID, PIPE_IN, PIPE_OUT [, CTX] )
413              
414             Close the C and C as returned from C,
415             checking whether the command finished successfully. The optional C
416             argument is required if you want to see the command name in the error message,
417             and it is the fourth value returned by C. The call idiom
418             is:
419              
420             my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check');
421             print "000000000\n" $out;
422             while (<$in>) { ... }
423             $r->command_close_bidi_pipe($pid, $in, $out, $ctx);
424              
425             Note that you should not rely on whatever actually is in C;
426             currently it is simply the command name but in future the context might
427             have more complicated structure.
428              
429             =cut
430              
431             sub command_close_bidi_pipe {
432 0     0 1   local $?;
433 0           my ($pid, $in, $out, $ctx) = @_;
434 0           foreach my $fh ($in, $out) {
435 0 0         unless (close $fh) {
436 0 0         if ($!) {
    0          
437 0           carp "error closing pipe: $!";
438             } elsif ($? >> 8) {
439 0           throw App::gh::Git::Error::Command($ctx, $? >>8);
440             }
441             }
442             }
443              
444 0           waitpid $pid, 0;
445              
446 0 0         if ($? >> 8) {
447 0           throw App::gh::Git::Error::Command($ctx, $? >>8);
448             }
449             }
450              
451              
452             =item command_noisy ( COMMAND [, ARGUMENTS... ] )
453              
454             Execute the given C in the same way as command() does but do not
455             capture the command output - the standard output is not redirected and goes
456             to the standard output of the caller application.
457              
458             While the method is called command_noisy(), you might want to as well use
459             it for the most silent App::gh::Git commands which you know will never pollute your
460             stdout but you want to avoid the overhead of the pipe setup when calling them.
461              
462             The function returns only after the command has finished running.
463              
464             =cut
465              
466             sub command_noisy {
467 0     0 1   my ($self, $cmd, @args) = _maybe_self(@_);
468 0           _check_valid_cmd($cmd);
469              
470 0           my $pid = fork;
471 0 0         if (not defined $pid) {
    0          
472 0           throw Error::Simple("fork failed: $!");
473             } elsif ($pid == 0) {
474 0           _cmd_exec($self, $cmd, @args);
475             }
476 0 0 0       if (waitpid($pid, 0) > 0 and $?>>8 != 0) {
477 0           throw App::gh::Git::Error::Command(join(' ', $cmd, @args), $? >> 8);
478             }
479             }
480              
481              
482             =item version ()
483              
484             Return the App::gh::Git version in use.
485              
486             =cut
487              
488             sub version {
489 0     0 1   my $verstr = command_oneline('--version');
490 0           $verstr =~ s/^git version //;
491 0           $verstr;
492             }
493              
494              
495             =item exec_path ()
496              
497             Return path to the App::gh::Git sub-command executables (the same as
498             C). Useful mostly only internally.
499              
500             =cut
501              
502 0     0 1   sub exec_path { command_oneline('--exec-path') }
503              
504              
505             =item html_path ()
506              
507             Return path to the App::gh::Git html documentation (the same as
508             C). Useful mostly only internally.
509              
510             =cut
511              
512 0     0 1   sub html_path { command_oneline('--html-path') }
513              
514              
515             =item repo_path ()
516              
517             Return path to the git repository. Must be called on a repository instance.
518              
519             =cut
520              
521 0     0 1   sub repo_path { $_[0]->{opts}->{Repository} }
522              
523              
524             =item wc_path ()
525              
526             Return path to the working copy. Must be called on a repository instance.
527              
528             =cut
529              
530 0     0 1   sub wc_path { $_[0]->{opts}->{WorkingCopy} }
531              
532              
533             =item wc_subdir ()
534              
535             Return path to the subdirectory inside of a working copy. Must be called
536             on a repository instance.
537              
538             =cut
539              
540 0   0 0 1   sub wc_subdir { $_[0]->{opts}->{WorkingSubdir} ||= '' }
541              
542              
543             =item wc_chdir ( SUBDIR )
544              
545             Change the working copy subdirectory to work within. The C is
546             relative to the working copy root directory (not the current subdirectory).
547             Must be called on a repository instance attached to a working copy
548             and the directory must exist.
549              
550             =cut
551              
552             sub wc_chdir {
553 0     0 1   my ($self, $subdir) = @_;
554 0 0         $self->wc_path()
555             or throw Error::Simple("bare repository");
556              
557 0 0         -d $self->wc_path().'/'.$subdir
558             or throw Error::Simple("subdir not found: $subdir $!");
559             # Of course we will not "hold" the subdirectory so anyone
560             # can delete it now and we will never know. But at least we tried.
561              
562 0           $self->{opts}->{WorkingSubdir} = $subdir;
563             }
564              
565              
566             =item config ( VARIABLE )
567              
568             Retrieve the configuration C in the same manner as C
569             does. In scalar context requires the variable to be set only one time
570             (exception is thrown otherwise), in array context returns allows the
571             variable to be set multiple times and returns all the values.
572              
573             This currently wraps command('config') so it is not so fast.
574              
575             =cut
576              
577             sub config {
578 0     0 1   my ($self, $var) = _maybe_self(@_);
579              
580             try {
581 0     0     my @cmd = ('config');
582 0 0         unshift @cmd, $self if $self;
583 0 0         if (wantarray) {
584 0           return command(@cmd, '--get-all', $var);
585             } else {
586 0           return command_oneline(@cmd, '--get', $var);
587             }
588             } catch App::gh::Git::Error::Command with {
589 0     0     my $E = shift;
590 0 0         if ($E->value() == 1) {
591             # Key not found.
592 0           return;
593             } else {
594 0           throw $E;
595             }
596 0           };
597             }
598              
599              
600             =item config_bool ( VARIABLE )
601              
602             Retrieve the bool configuration C. The return value
603             is usable as a boolean in perl (and C if it's not defined,
604             of course).
605              
606             This currently wraps command('config') so it is not so fast.
607              
608             =cut
609              
610             sub config_bool {
611 0     0 1   my ($self, $var) = _maybe_self(@_);
612              
613             try {
614 0     0     my @cmd = ('config', '--bool', '--get', $var);
615 0 0         unshift @cmd, $self if $self;
616 0           my $val = command_oneline(@cmd);
617 0 0         return undef unless defined $val;
618 0           return $val eq 'true';
619             } catch App::gh::Git::Error::Command with {
620 0     0     my $E = shift;
621 0 0         if ($E->value() == 1) {
622             # Key not found.
623 0           return undef;
624             } else {
625 0           throw $E;
626             }
627 0           };
628             }
629              
630              
631             =item config_path ( VARIABLE )
632              
633             Retrieve the path configuration C. The return value
634             is an expanded path or C if it's not defined.
635              
636             This currently wraps command('config') so it is not so fast.
637              
638             =cut
639              
640             sub config_path {
641 0     0 1   my ($self, $var) = _maybe_self(@_);
642              
643             try {
644 0     0     my @cmd = ('config', '--path');
645 0 0         unshift @cmd, $self if $self;
646 0 0         if (wantarray) {
647 0           return command(@cmd, '--get-all', $var);
648             } else {
649 0           return command_oneline(@cmd, '--get', $var);
650             }
651             } catch App::gh::Git::Error::Command with {
652 0     0     my $E = shift;
653 0 0         if ($E->value() == 1) {
654             # Key not found.
655 0           return undef;
656             } else {
657 0           throw $E;
658             }
659 0           };
660             }
661              
662             =item config_int ( VARIABLE )
663              
664             Retrieve the integer configuration C. The return value
665             is simple decimal number. An optional value suffix of 'k', 'm',
666             or 'g' in the config file will cause the value to be multiplied
667             by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
668             It would return C if configuration variable is not defined,
669              
670             This currently wraps command('config') so it is not so fast.
671              
672             =cut
673              
674             sub config_int {
675 0     0 1   my ($self, $var) = _maybe_self(@_);
676              
677             try {
678 0     0     my @cmd = ('config', '--int', '--get', $var);
679 0 0         unshift @cmd, $self if $self;
680 0           return command_oneline(@cmd);
681             } catch App::gh::Git::Error::Command with {
682 0     0     my $E = shift;
683 0 0         if ($E->value() == 1) {
684             # Key not found.
685 0           return undef;
686             } else {
687 0           throw $E;
688             }
689 0           };
690             }
691              
692             =item get_colorbool ( NAME )
693              
694             Finds if color should be used for NAMEd operation from the configuration,
695             and returns boolean (true for "use color", false for "do not use color").
696              
697             =cut
698              
699             sub get_colorbool {
700 0     0 1   my ($self, $var) = @_;
701 0 0         my $stdout_to_tty = (-t STDOUT) ? "true" : "false";
702 0           my $use_color = $self->command_oneline('config', '--get-colorbool',
703             $var, $stdout_to_tty);
704 0           return ($use_color eq 'true');
705             }
706              
707             =item get_color ( SLOT, COLOR )
708              
709             Finds color for SLOT from the configuration, while defaulting to COLOR,
710             and returns the ANSI color escape sequence:
711              
712             print $repo->get_color("color.interactive.prompt", "underline blue white");
713             print "some text";
714             print $repo->get_color("", "normal");
715              
716             =cut
717              
718             sub get_color {
719 0     0 1   my ($self, $slot, $default) = @_;
720 0           my $color = $self->command_oneline('config', '--get-color', $slot, $default);
721 0 0         if (!defined $color) {
722 0           $color = "";
723             }
724 0           return $color;
725             }
726              
727             =item remote_refs ( REPOSITORY [, GROUPS [, REFGLOBS ] ] )
728              
729             This function returns a hashref of refs stored in a given remote repository.
730             The hash is in the format C hash>. For tags, the C entry
731             contains the tag object while a C entry gives the tagged objects.
732              
733             C has the same meaning as the appropriate C
734             argument; either an URL or a remote name (if called on a repository instance).
735             C is an optional arrayref that can contain 'tags' to return all the
736             tags and/or 'heads' to return all the heads. C is an optional array
737             of strings containing a shell-like glob to further limit the refs returned in
738             the hash; the meaning is again the same as the appropriate C
739             argument.
740              
741             This function may or may not be called on a repository instance. In the former
742             case, remote names as defined in the repository are recognized as repository
743             specifiers.
744              
745             =cut
746              
747             sub remote_refs {
748 0     0 1   my ($self, $repo, $groups, $refglobs) = _maybe_self(@_);
749 0           my @args;
750 0 0         if (ref $groups eq 'ARRAY') {
751 0           foreach (@$groups) {
752 0 0         if ($_ eq 'heads') {
    0          
753 0           push (@args, '--heads');
754             } elsif ($_ eq 'tags') {
755 0           push (@args, '--tags');
756             } else {
757             # Ignore unknown groups for future
758             # compatibility
759             }
760             }
761             }
762 0           push (@args, $repo);
763 0 0         if (ref $refglobs eq 'ARRAY') {
764 0           push (@args, @$refglobs);
765             }
766              
767 0 0         my @self = $self ? ($self) : (); # Ultra trickery
768 0           my ($fh, $ctx) = App::gh::Git::command_output_pipe(@self, 'ls-remote', @args);
769 0           my %refs;
770 0           while (<$fh>) {
771 0           chomp;
772 0           my ($hash, $ref) = split(/\t/, $_, 2);
773 0           $refs{$ref} = $hash;
774             }
775 0           App::gh::Git::command_close_pipe(@self, $fh, $ctx);
776 0           return \%refs;
777             }
778              
779              
780             =item ident ( TYPE | IDENTSTR )
781              
782             =item ident_person ( TYPE | IDENTSTR | IDENTARRAY )
783              
784             This suite of functions retrieves and parses ident information, as stored
785             in the commit and tag objects or produced by C (thus
786             C can be either I or I; case is insignificant).
787              
788             The C method retrieves the ident information from C
789             and either returns it as a scalar string or as an array with the fields parsed.
790             Alternatively, it can take a prepared ident string (e.g. from the commit
791             object) and just parse it.
792              
793             C returns the person part of the ident - name and email;
794             it can take the same arguments as C or the array returned by C.
795              
796             The synopsis is like:
797              
798             my ($name, $email, $time_tz) = ident('author');
799             "$name <$email>" eq ident_person('author');
800             "$name <$email>" eq ident_person($name);
801             $time_tz =~ /^\d+ [+-]\d{4}$/;
802              
803             =cut
804              
805             sub ident {
806 0     0 1   my ($self, $type) = _maybe_self(@_);
807 0           my $identstr;
808 0 0 0       if (lc $type eq lc 'committer' or lc $type eq lc 'author') {
809 0           my @cmd = ('var', 'GIT_'.uc($type).'_IDENT');
810 0 0         unshift @cmd, $self if $self;
811 0           $identstr = command_oneline(@cmd);
812             } else {
813 0           $identstr = $type;
814             }
815 0 0         if (wantarray) {
816 0           return $identstr =~ /^(.*) <(.*)> (\d+ [+-]\d{4})$/;
817             } else {
818 0           return $identstr;
819             }
820             }
821              
822             sub ident_person {
823 0     0 1   my ($self, @ident) = _maybe_self(@_);
824 0 0         $#ident == 0 and @ident = $self ? $self->ident($ident[0]) : ident($ident[0]);
    0          
825 0           return "$ident[0] <$ident[1]>";
826             }
827              
828              
829             =item hash_object ( TYPE, FILENAME )
830              
831             Compute the SHA1 object id of the given C considering it is
832             of the C object type (C, C, C).
833              
834             The method can be called without any instance or on a specified App::gh::Git repository,
835             it makes zero difference.
836              
837             The function returns the SHA1 hash.
838              
839             =cut
840              
841             # TODO: Support for passing FILEHANDLE instead of FILENAME
842             sub hash_object {
843 0     0 1   my ($self, $type, $file) = _maybe_self(@_);
844 0           command_oneline('hash-object', '-t', $type, $file);
845             }
846              
847              
848             =item hash_and_insert_object ( FILENAME )
849              
850             Compute the SHA1 object id of the given C and add the object to the
851             object database.
852              
853             The function returns the SHA1 hash.
854              
855             =cut
856              
857             # TODO: Support for passing FILEHANDLE instead of FILENAME
858             sub hash_and_insert_object {
859 0     0 1   my ($self, $filename) = @_;
860              
861 0 0         carp "Bad filename \"$filename\"" if $filename =~ /[\r\n]/;
862              
863 0           $self->_open_hash_and_insert_object_if_needed();
864 0           my ($in, $out) = ($self->{hash_object_in}, $self->{hash_object_out});
865              
866 0 0         unless (print $out $filename, "\n") {
867 0           $self->_close_hash_and_insert_object();
868 0           throw Error::Simple("out pipe went bad");
869             }
870              
871 0           chomp(my $hash = <$in>);
872 0 0         unless (defined($hash)) {
873 0           $self->_close_hash_and_insert_object();
874 0           throw Error::Simple("in pipe went bad");
875             }
876              
877 0           return $hash;
878             }
879              
880             sub _open_hash_and_insert_object_if_needed {
881 0     0     my ($self) = @_;
882              
883 0 0         return if defined($self->{hash_object_pid});
884              
885 0           ($self->{hash_object_pid}, $self->{hash_object_in},
886             $self->{hash_object_out}, $self->{hash_object_ctx}) =
887             $self->command_bidi_pipe(qw(hash-object -w --stdin-paths --no-filters));
888             }
889              
890             sub _close_hash_and_insert_object {
891 0     0     my ($self) = @_;
892              
893 0 0         return unless defined($self->{hash_object_pid});
894              
895 0           my @vars = map { 'hash_object_' . $_ } qw(pid in out ctx);
  0            
896              
897 0           command_close_bidi_pipe(@$self{@vars});
898 0           delete @$self{@vars};
899             }
900              
901             =item cat_blob ( SHA1, FILEHANDLE )
902              
903             Prints the contents of the blob identified by C to C and
904             returns the number of bytes printed.
905              
906             =cut
907              
908             sub cat_blob {
909 0     0 1   my ($self, $sha1, $fh) = @_;
910              
911 0           $self->_open_cat_blob_if_needed();
912 0           my ($in, $out) = ($self->{cat_blob_in}, $self->{cat_blob_out});
913              
914 0 0         unless (print $out $sha1, "\n") {
915 0           $self->_close_cat_blob();
916 0           throw Error::Simple("out pipe went bad");
917             }
918              
919 0           my $description = <$in>;
920 0 0         if ($description =~ / missing$/) {
921 0           carp "$sha1 doesn't exist in the repository";
922 0           return -1;
923             }
924              
925 0 0         if ($description !~ /^[0-9a-fA-F]{40} \S+ (\d+)$/) {
926 0           carp "Unexpected result returned from git cat-file";
927 0           return -1;
928             }
929              
930 0           my $size = $1;
931              
932 0           my $blob;
933 0           my $bytesRead = 0;
934              
935 0           while (1) {
936 0           my $bytesLeft = $size - $bytesRead;
937 0 0         last unless $bytesLeft;
938              
939 0 0         my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024;
940 0           my $read = read($in, $blob, $bytesToRead, $bytesRead);
941 0 0         unless (defined($read)) {
942 0           $self->_close_cat_blob();
943 0           throw Error::Simple("in pipe went bad");
944             }
945              
946 0           $bytesRead += $read;
947             }
948              
949             # Skip past the trailing newline.
950 0           my $newline;
951 0           my $read = read($in, $newline, 1);
952 0 0         unless (defined($read)) {
953 0           $self->_close_cat_blob();
954 0           throw Error::Simple("in pipe went bad");
955             }
956 0 0 0       unless ($read == 1 && $newline eq "\n") {
957 0           $self->_close_cat_blob();
958 0           throw Error::Simple("didn't find newline after blob");
959             }
960              
961 0 0         unless (print $fh $blob) {
962 0           $self->_close_cat_blob();
963 0           throw Error::Simple("couldn't write to passed in filehandle");
964             }
965              
966 0           return $size;
967             }
968              
969             sub _open_cat_blob_if_needed {
970 0     0     my ($self) = @_;
971              
972 0 0         return if defined($self->{cat_blob_pid});
973              
974 0           ($self->{cat_blob_pid}, $self->{cat_blob_in},
975             $self->{cat_blob_out}, $self->{cat_blob_ctx}) =
976             $self->command_bidi_pipe(qw(cat-file --batch));
977             }
978              
979             sub _close_cat_blob {
980 0     0     my ($self) = @_;
981              
982 0 0         return unless defined($self->{cat_blob_pid});
983              
984 0           my @vars = map { 'cat_blob_' . $_ } qw(pid in out ctx);
  0            
985              
986 0           command_close_bidi_pipe(@$self{@vars});
987 0           delete @$self{@vars};
988             }
989              
990              
991             { # %TEMP_* Lexical Context
992              
993             my (%TEMP_FILEMAP, %TEMP_FILES);
994              
995             =item temp_acquire ( NAME )
996              
997             Attempts to retreive the temporary file mapped to the string C. If an
998             associated temp file has not been created this session or was closed, it is
999             created, cached, and set for autoflush and binmode.
1000              
1001             Internally locks the file mapped to C. This lock must be released with
1002             C when the temp file is no longer needed. Subsequent attempts
1003             to retrieve temporary files mapped to the same C while still locked will
1004             cause an error. This locking mechanism provides a weak guarantee and is not
1005             threadsafe. It does provide some error checking to help prevent temp file refs
1006             writing over one another.
1007              
1008             In general, the L returned should not be closed by consumers as
1009             it defeats the purpose of this caching mechanism. If you need to close the temp
1010             file handle, then you should use L or another temp file faculty
1011             directly. If a handle is closed and then requested again, then a warning will
1012             issue.
1013              
1014             =cut
1015              
1016             sub temp_acquire {
1017 0     0 1   my $temp_fd = _temp_cache(@_);
1018              
1019 0           $TEMP_FILES{$temp_fd}{locked} = 1;
1020 0           $temp_fd;
1021             }
1022              
1023             =item temp_release ( NAME )
1024              
1025             =item temp_release ( FILEHANDLE )
1026              
1027             Releases a lock acquired through C. Can be called either with
1028             the C mapping used when acquiring the temp file or with the C
1029             referencing a locked temp file.
1030              
1031             Warns if an attempt is made to release a file that is not locked.
1032              
1033             The temp file will be truncated before being released. This can help to reduce
1034             disk I/O where the system is smart enough to detect the truncation while data
1035             is in the output buffers. Beware that after the temp file is released and
1036             truncated, any operations on that file may fail miserably until it is
1037             re-acquired. All contents are lost between each release and acquire mapped to
1038             the same string.
1039              
1040             =cut
1041              
1042             sub temp_release {
1043 0     0 1   my ($self, $temp_fd, $trunc) = _maybe_self(@_);
1044              
1045 0 0         if (exists $TEMP_FILEMAP{$temp_fd}) {
1046 0           $temp_fd = $TEMP_FILES{$temp_fd};
1047             }
1048 0 0         unless ($TEMP_FILES{$temp_fd}{locked}) {
1049 0           carp "Attempt to release temp file '",
1050             $temp_fd, "' that has not been locked";
1051             }
1052 0 0 0       temp_reset($temp_fd) if $trunc and $temp_fd->opened;
1053              
1054 0           $TEMP_FILES{$temp_fd}{locked} = 0;
1055 0           undef;
1056             }
1057              
1058             sub _temp_cache {
1059 0     0     my ($self, $name) = _maybe_self(@_);
1060              
1061 0           _verify_require();
1062              
1063 0           my $temp_fd = \$TEMP_FILEMAP{$name};
1064 0 0 0       if (defined $$temp_fd and $$temp_fd->opened) {
1065 0 0         if ($TEMP_FILES{$$temp_fd}{locked}) {
1066 0           throw Error::Simple("Temp file with moniker '" .
1067             $name . "' already in use");
1068             }
1069             } else {
1070 0 0         if (defined $$temp_fd) {
1071             # then we're here because of a closed handle.
1072 0           carp "Temp file '", $name,
1073             "' was closed. Opening replacement.";
1074             }
1075 0           my $fname;
1076              
1077             my $tmpdir;
1078 0 0         if (defined $self) {
1079 0           $tmpdir = $self->repo_path();
1080             }
1081              
1082 0 0         ($$temp_fd, $fname) = File::Temp->tempfile(
1083             'Git_XXXXXX', UNLINK => 1, DIR => $tmpdir,
1084             ) or throw Error::Simple("couldn't open new temp file");
1085              
1086 0           $$temp_fd->autoflush;
1087 0           binmode $$temp_fd;
1088 0           $TEMP_FILES{$$temp_fd}{fname} = $fname;
1089             }
1090 0           $$temp_fd;
1091             }
1092              
1093             sub _verify_require {
1094 0     0     eval { require File::Temp; require File::Spec; };
  0            
  0            
1095 0 0         $@ and throw Error::Simple($@);
1096             }
1097              
1098             =item temp_reset ( FILEHANDLE )
1099              
1100             Truncates and resets the position of the C.
1101              
1102             =cut
1103              
1104             sub temp_reset {
1105 0     0 1   my ($self, $temp_fd) = _maybe_self(@_);
1106              
1107 0 0         truncate $temp_fd, 0
1108             or throw Error::Simple("couldn't truncate file");
1109 0 0 0       sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET)
1110             or throw Error::Simple("couldn't seek to beginning of file");
1111 0 0 0       sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0
1112             or throw Error::Simple("expected file position to be reset");
1113             }
1114              
1115             =item temp_path ( NAME )
1116              
1117             =item temp_path ( FILEHANDLE )
1118              
1119             Returns the filename associated with the given tempfile.
1120              
1121             =cut
1122              
1123             sub temp_path {
1124 0     0 1   my ($self, $temp_fd) = _maybe_self(@_);
1125              
1126 0 0         if (exists $TEMP_FILEMAP{$temp_fd}) {
1127 0           $temp_fd = $TEMP_FILEMAP{$temp_fd};
1128             }
1129 0           $TEMP_FILES{$temp_fd}{fname};
1130             }
1131              
1132             sub END {
1133 2 50   2   2374 unlink values %TEMP_FILEMAP if %TEMP_FILEMAP;
1134             }
1135              
1136             } # %TEMP_* Lexical Context
1137              
1138             =back
1139              
1140             =head1 ERROR HANDLING
1141              
1142             All functions are supposed to throw Perl exceptions in case of errors.
1143             See the L module on how to catch those. Most exceptions are mere
1144             L instances.
1145              
1146             However, the C, C and C
1147             functions suite can throw C exceptions as well: those are
1148             thrown when the external command returns an error code and contain the error
1149             code as well as access to the captured command's output. The exception class
1150             provides the usual C and C (command's exit code) methods and
1151             in addition also a C method that returns either an array or a
1152             string with the captured command output (depending on the original function
1153             call context; C returns C) and $ which
1154             returns the command and its arguments (but without proper quoting).
1155              
1156             Note that the C functions cannot throw this exception since
1157             it has no idea whether the command failed or not. You will only find out
1158             at the time you C the pipe; if you want to have that automated,
1159             use C, which can throw the exception.
1160              
1161             =cut
1162              
1163             {
1164             package App::gh::Git::Error::Command;
1165              
1166             @App::gh::Git::Error::Command::ISA = qw(Error);
1167              
1168             sub new {
1169 0     0     my $self = shift;
1170 0           my $cmdline = '' . shift;
1171 0           my $value = 0 + shift;
1172 0           my $outputref = shift;
1173 0           my(@args) = ();
1174              
1175 0           local $Error::Depth = $Error::Depth + 1;
1176              
1177 0           push(@args, '-cmdline', $cmdline);
1178 0           push(@args, '-value', $value);
1179 0           push(@args, '-outputref', $outputref);
1180              
1181 0           $self->SUPER::new(-text => 'command returned error', @args);
1182             }
1183              
1184             sub stringify {
1185 0     0     my $self = shift;
1186 0           my $text = $self->SUPER::stringify;
1187 0           $self->cmdline() . ': ' . $text . ': ' . $self->value() . "\n";
1188             }
1189              
1190             sub cmdline {
1191 0     0     my $self = shift;
1192 0           $self->{'-cmdline'};
1193             }
1194              
1195             sub cmd_output {
1196 0     0     my $self = shift;
1197 0           my $ref = $self->{'-outputref'};
1198 0 0         defined $ref or undef;
1199 0 0         if (ref $ref eq 'ARRAY') {
1200 0           return @$ref;
1201             } else { # SCALAR
1202 0           return $$ref;
1203             }
1204             }
1205             }
1206              
1207             =over 4
1208              
1209             =item git_cmd_try { CODE } ERRMSG
1210              
1211             This magical statement will automatically catch any C
1212             exceptions thrown by C and make your program die with C
1213             on its lips; the message will have %s substituted for the command line
1214             and %d for the exit status. This statement is useful mostly for producing
1215             more user-friendly error messages.
1216              
1217             In case of no exception caught the statement returns C's return value.
1218              
1219             Note that this is the only auto-exported function.
1220              
1221             =cut
1222              
1223             sub git_cmd_try(&$) {
1224 0     0 1   my ($code, $errmsg) = @_;
1225 0           my @result;
1226             my $err;
1227 0           my $array = wantarray;
1228             try {
1229 0 0   0     if ($array) {
1230 0           @result = &$code;
1231             } else {
1232 0           $result[0] = &$code;
1233             }
1234             } catch App::gh::Git::Error::Command with {
1235 0     0     my $E = shift;
1236 0           $err = $errmsg;
1237 0           $err =~ s/\%s/$E->cmdline()/ge;
  0            
1238 0           $err =~ s/\%d/$E->value()/ge;
  0            
1239             # We can't croak here since Error.pm would mangle
1240             # that to Error::Simple.
1241 0           };
1242 0 0         $err and croak $err;
1243 0 0         return $array ? @result : $result[0];
1244             }
1245              
1246              
1247             =back
1248              
1249             =head1 COPYRIGHT
1250              
1251             Copyright 2006 by Petr Baudis Epasky@suse.czE.
1252              
1253             This module is free software; it may be used, copied, modified
1254             and distributed under the terms of the GNU General Public Licence,
1255             either version 2, or (at your option) any later version.
1256              
1257             =cut
1258              
1259              
1260             # Take raw method argument list and return ($obj, @args) in case
1261             # the method was called upon an instance and (undef, @args) if
1262             # it was called directly.
1263             sub _maybe_self {
1264 0 0   0     UNIVERSAL::isa($_[0], 'App::gh::Git') ? @_ : (undef, @_);
1265             }
1266              
1267             # Check if the command id is something reasonable.
1268             sub _check_valid_cmd {
1269 0     0     my ($cmd) = @_;
1270 0 0         $cmd =~ /^[a-z0-9A-Z_-]+$/ or throw Error::Simple("bad command: $cmd");
1271             }
1272              
1273             # Common backend for the pipe creators.
1274             sub _command_common_pipe {
1275 0     0     my $direction = shift;
1276 0           my ($self, @p) = _maybe_self(@_);
1277 0           my (%opts, $cmd, @args);
1278 0 0         if (ref $p[0]) {
1279 0           ($cmd, @args) = @{shift @p};
  0            
1280 0 0         %opts = ref $p[0] ? %{$p[0]} : @p;
  0            
1281             } else {
1282 0           ($cmd, @args) = @p;
1283             }
1284 0           _check_valid_cmd($cmd);
1285              
1286 0           my $fh;
1287 0 0         if ($^O eq 'MSWin32') {
1288             # ActiveState Perl
1289             #defined $opts{STDERR} and
1290             # warn 'ignoring STDERR option - running w/ ActiveState';
1291 0 0         $direction eq '-|' or
1292             die 'input pipe for ActiveState not implemented';
1293             # the strange construction with *ACPIPE is just to
1294             # explain the tie below that we want to bind to
1295             # a handle class, not scalar. It is not known if
1296             # it is something specific to ActiveState Perl or
1297             # just a Perl quirk.
1298 0           tie (*ACPIPE, 'App::gh::Git::activestate_pipe', $cmd, @args);
1299 0           $fh = *ACPIPE;
1300              
1301             } else {
1302 0           my $pid = open($fh, $direction);
1303 0 0         if (not defined $pid) {
    0          
1304 0           throw Error::Simple("open failed: $!");
1305             } elsif ($pid == 0) {
1306 0 0         if (defined $opts{STDERR}) {
1307 0           close STDERR;
1308             }
1309 0 0         if ($opts{STDERR}) {
1310 0 0         open (STDERR, '>&', $opts{STDERR})
1311             or die "dup failed: $!";
1312             }
1313 0           _cmd_exec($self, $cmd, @args);
1314             }
1315             }
1316 0 0         return wantarray ? ($fh, join(' ', $cmd, @args)) : $fh;
1317             }
1318              
1319             # When already in the subprocess, set up the appropriate state
1320             # for the given repository and execute the git command.
1321             sub _cmd_exec {
1322 0     0     my ($self, @args) = @_;
1323 0           _setup_git_cmd_env($self);
1324 0           _execv_git_cmd(@args);
1325 0           die qq[exec "@args" failed: $!];
1326             }
1327              
1328             # set up the appropriate state for git command
1329             sub _setup_git_cmd_env {
1330 0     0     my $self = shift;
1331 0 0         if ($self) {
1332 0 0         $self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path();
1333 0 0 0       $self->repo_path() and $self->wc_path()
1334             and $ENV{'GIT_WORK_TREE'} = $self->wc_path();
1335 0 0         $self->wc_path() and chdir($self->wc_path());
1336 0 0         $self->wc_subdir() and chdir($self->wc_subdir());
1337             }
1338             }
1339              
1340             # Execute the given App::gh::Git command ($_[0]) with arguments ($_[1..])
1341             # by searching for it at proper places.
1342 0     0     sub _execv_git_cmd { exec('git', @_); }
1343              
1344             # Close pipe to a subprocess.
1345             sub _cmd_close {
1346 0     0     my ($fh, $ctx) = @_;
1347 0 0         if (not close $fh) {
1348 0 0         if ($!) {
    0          
1349             # It's just close, no point in fatalities
1350 0           carp "error closing pipe: $!";
1351             } elsif ($? >> 8) {
1352             # The caller should pepper this.
1353 0           throw App::gh::Git::Error::Command($ctx, $? >> 8);
1354             }
1355             # else we might e.g. closed a live stream; the command
1356             # dying of SIGPIPE would drive us here.
1357             }
1358             }
1359              
1360              
1361             sub DESTROY {
1362 0     0     my ($self) = @_;
1363 0           $self->_close_hash_and_insert_object();
1364 0           $self->_close_cat_blob();
1365             }
1366              
1367              
1368             # Pipe implementation for ActiveState Perl.
1369              
1370             package App::gh::Git::activestate_pipe;
1371 2     2   22 use strict;
  2         4  
  2         576  
1372              
1373             sub TIEHANDLE {
1374 0     0     my ($class, @params) = @_;
1375             # FIXME: This is probably horrible idea and the thing will explode
1376             # at the moment you give it arguments that require some quoting,
1377             # but I have no ActiveState clue... --pasky
1378             # Let's just hope ActiveState Perl does at least the quoting
1379             # correctly.
1380 0           my @data = qx{git @params};
1381 0           bless { i => 0, data => \@data }, $class;
1382             }
1383              
1384             sub READLINE {
1385 0     0     my $self = shift;
1386 0 0         if ($self->{i} >= scalar @{$self->{data}}) {
  0            
1387 0           return undef;
1388             }
1389 0           my $i = $self->{i};
1390 0 0         if (wantarray) {
1391 0           $self->{i} = $#{$self->{'data'}} + 1;
  0            
1392 0           return splice(@{$self->{'data'}}, $i);
  0            
1393             }
1394 0           $self->{i} = $i + 1;
1395 0           return $self->{'data'}->[ $i ];
1396             }
1397              
1398             sub CLOSE {
1399 0     0     my $self = shift;
1400 0           delete $self->{data};
1401 0           delete $self->{i};
1402             }
1403              
1404             sub EOF {
1405 0     0     my $self = shift;
1406 0           return ($self->{i} >= scalar @{$self->{data}});
  0            
1407             }
1408              
1409              
1410             1; # Famous last words