File Coverage

blib/lib/Git/Repository/Log/Iterator.pm
Criterion Covered Total %
statement 58 64 90.6
branch 14 18 77.7
condition 7 8 87.5
subroutine 10 12 83.3
pod 4 4 100.0
total 93 106 87.7


line stmt bran cond sub pod time code
1             package Git::Repository::Log::Iterator;
2             $Git::Repository::Log::Iterator::VERSION = '1.313';
3 2     2   6 use strict;
  2         3  
  2         42  
4 2     2   5 use warnings;
  2         2  
  2         32  
5 2     2   28 use 5.006;
  2         6  
6 2     2   5 use Carp;
  2         2  
  2         85  
7 2     2   8 use Scalar::Util qw( blessed );
  2         2  
  2         84  
8              
9 2     2   7 use Git::Repository;
  2         2  
  2         20  
10 2     2   37 use Git::Repository::Command;
  2         2  
  2         14  
11 2     2   768 use Git::Repository::Log;
  2         2  
  2         1044  
12              
13             sub new {
14 22     22 1 66 my ( $class, @cmd ) = @_;
15              
16             # pick up unsupported log options
17 22         33 my @badopts = do {
18 22         25 my $options = 1;
19 62         166 grep {/^--(?:(?:pretty|format)=(?!raw).*|graph|oneline)$/}
20 22 100       43 grep { $options = 0 if $_ eq '--'; $options } @cmd;
  64         130  
  64         86  
21             };
22 22 100       594 croak "log() cannot parse @badopts. "
23             . 'Use run( log => ... ) to parse the output yourself'
24             if @badopts;
25              
26             # note: there is no --color option to git log before 1.5.3.3
27 18   66     215 my ($r) = grep blessed $_ && $_->isa('Git::Repository'), @cmd;
28 18   100     50 $r ||= 'Git::Repository'; # no Git::Repository object given
29 18 50       707 unshift @cmd, '--no-color' if $r->version_ge('1.5.3.3');
30              
31             # enforce the format
32 18         120700 @cmd = ( 'log', '--pretty=raw', @cmd );
33              
34             # run the command (@cmd may hold a Git::Repository instance)
35 18         79 my $cmd = Git::Repository::Command->new(@cmd);
36 18         100569 bless { cmd => $cmd, fh => $cmd->stdout }, $class;
37             }
38              
39             sub new_from_fh {
40 0     0 1 0 my ( $class, $fh ) = @_;
41 0         0 bless { fh => $fh }, $class;
42             }
43              
44             sub new_from_file {
45 0     0 1 0 my ( $class, $file ) = @_;
46 0 0       0 open my $fh, '<', $file or die "Can't open $file: $!";
47 0         0 bless { fh => $fh }, $class;
48             }
49              
50             sub next {
51 57     57 1 5280 my ($self) = @_;
52 57         73 my $fh = $self->{fh};
53              
54             # get records
55 57 100       205 my @records = defined $self->{record} ? ( delete $self->{record} ) : ();
56             {
57 57         56 local $/ = "\n\n";
  57         473  
58 57         36711 while (<$fh>) {
59 90 100 100     595 $self->{record} = $_, last if /\Acommit / && @records;
60 65         1370 push @records, $_;
61             }
62             }
63              
64             # EOF
65 57 100       123 if ( !@records ) {
66 17 50       44 if ( $self->{cmd} ) { # might catch some git errors
67 17         66 $self->{cmd}->final_output();
68             }
69             else { # just close the filehandle
70 0         0 $self->{fh}->close;
71             }
72 15         2037 return;
73             }
74              
75             # the first two records are always the same, with --pretty=raw
76 40         112 local $/ = "\n";
77 40         82 my ( $header, $message, $extra ) = ( @records, '', '' );
78 40         71 chomp $header;
79 40         397 my @headers = map { chomp; split / /, $_, 2 } split /^(?=\S)/m, $header;
  242         180  
  242         468  
80 40         446 s/\n /\n/g for @headers;
81 40 100       123 chomp( $message, $extra ) if exists $self->{record};
82              
83             # create the log object
84 40         261 return Git::Repository::Log->new(
85             @headers,
86             message => $message,
87             extra => $extra,
88             );
89             }
90              
91             1;
92              
93             __END__
94              
95             =pod
96              
97             =head1 NAME
98              
99             Git::Repository::Log::Iterator - Split a git log stream into records
100              
101             =head1 SYNOPSIS
102              
103             use Git::Repository::Log::Iterator;
104              
105             # use a default Git::Repository context
106             my $iter = Git::Repository::Log::Iterator->new('HEAD~10..');
107              
108             # or provide an existing instance
109             my $iter = Git::Repository::Log::Iterator->new( $r, 'HEAD~10..' );
110              
111             # get the next log record
112             while ( my $log = $iter->next ) {
113             ...;
114             }
115              
116             =head1 DESCRIPTION
117              
118             C<Git::Repository::Log::Iterator> initiates a B<git log> command
119             from a list of paramaters and parses its output to produce
120             L<Git::Repository::Log> objects represening each log item.
121              
122             =head1 METHODS
123              
124             =head2 new
125              
126             my $iter = Git::Repository::Log::Iterator->new( @args );
127              
128             Create a new B<git log> stream from the parameter list in C<@args>
129             and return a iterator on it.
130              
131             C<new()> will happily accept any parameters, but note that
132             C<Git::Repository::Log::Iterator> expects the output to look like that
133             of C<--pretty=raw>, and so will force the the C<--pretty> option
134             (in case C<format.pretty> is defined in the Git configuration).
135             It will also forcibly remove colored output (using C<--color=never>).
136              
137             Extra output (like patches) will be stored in the C<extra> parameter of
138             the L<Git::Repository::Log> object. Decorations will be lost.
139              
140             When unsupported options are recognized in the parameter list, C<new()>
141             will C<croak()> with a message advising to use C<< run( 'log' => ... ) >>
142             to parse the output yourself.
143              
144             The object is really a blessed hash reference, with only two keys:
145              
146             =over 4
147              
148             =item cmd
149              
150             The L<Git::Repository::Command> object running the actual B<git log>
151             command. It might not be defined in some cases (see below L</new_from_fh>
152             and L</new_from_file>).
153              
154             =item fh
155              
156             The filehandle from which the output of B<git log> is actually read.
157             This is the only attribute needed to run the L</next> method.
158              
159             =back
160              
161             =head2 new_from_fh
162              
163             This constructor makes it possible to provide the filehandle directly.
164              
165             The C<cmd> key is not defined when using this constructor.
166              
167             =head2 new_from_file
168              
169             This constructor makes it possible to provide a filename that will be
170             C<open()>ed to produce a filehandle to read the log stream from.
171              
172             The C<cmd> key is not defined when using this constructor.
173              
174             =head2 next
175              
176             my $log = $iter->next;
177              
178             Return the next log item as a L<Git::Repository::Log> object,
179             or nothing if the stream has ended.
180              
181             =head1 COPYRIGHT
182              
183             Copyright 2010-2016 Philippe Bruhat (BooK), all rights reserved.
184              
185             =head1 LICENSE
186              
187             This program is free software; you can redistribute it and/or modify it
188             under the same terms as Perl itself.
189              
190             =cut