File Coverage

blib/lib/Git/Database/Backend/Git.pm
Criterion Covered Total %
statement 72 87 82.7
branch 15 20 75.0
condition n/a
subroutine 15 15 100.0
pod 0 9 0.0
total 102 131 77.8


line stmt bran cond sub pod time code
1             package Git::Database::Backend::Git;
2             $Git::Database::Backend::Git::VERSION = '0.011';
3 7     7   215291 use Git::Version::Compare qw( ge_git );
  7         101  
  7         1847  
4 7     7   140 use Sub::Quote;
  7         65  
  7         1299  
5              
6 7     7   97 use Moo;
  7         57  
  7         251  
7 7     7   32065 use namespace::clean;
  7         47  
  7         170  
8              
9             with
10             'Git::Database::Role::Backend',
11             'Git::Database::Role::ObjectReader',
12             'Git::Database::Role::ObjectWriter',
13             'Git::Database::Role::RefReader',
14             'Git::Database::Role::RefWriter',
15             ;
16              
17             has '+store' => (
18             isa => quote_sub( q{
19             die 'store is not a Git object'
20             if !eval { $_[0]->isa('Git') }
21             } ),
22             );
23              
24             has object_factory => (
25             is => 'lazy',
26             init_arg => undef,
27 37     37   1107 builder => sub { [ $_[0]->store->command_bidi_pipe( 'cat-file', '--batch' ) ] },
28             predicate => 1,
29             clearer => 1,
30             );
31              
32             has object_checker => (
33             is => 'lazy',
34             init_arg => undef,
35 11     11   508 builder => sub { [ $_[0]->store->command_bidi_pipe( 'cat-file', '--batch-check' ) ] },
36             predicate => 1,
37             clearer => 1,
38             );
39              
40             # Git::Database::Role::Backend
41             sub hash_object {
42 95     95 0 29891 my ( $self, $object ) = @_;
43 95         827 my ( $pid, $in, $out, $ctx ) =
44             $self->store->command_bidi_pipe( 'hash-object', '-t', $object->kind,
45             '--stdin' );
46 95         1547921 print {$out} $object->content;
  95         12461  
47 95         4501 close $out;
48 95         52217 chomp( my $digest = <$in> );
49 95         2221 $self->store->command_close_bidi_pipe( $pid, $in, undef, $ctx ); # $out closed
50 95         21009 return $digest;
51             }
52              
53             # Git::Database::Role::ObjectReader
54             sub get_object_meta {
55 57     57 0 684 my ( $self, $digest ) = @_;
56 57         2208 my $checker = $self->object_checker;
57              
58             # request the object
59 57         208313 print { $checker->[2] } $digest, "\n";
  57         3384  
60              
61             # process the reply
62 57         784 local $/ = "\012";
63 57         3331 chomp( my $reply = $checker->[1]->getline );
64              
65             # protect against weird cases like if $digest contains a space
66 57         20473 my @parts = split / /, $reply;
67 57 100       1351 return ( $digest, 'missing', undef ) if $parts[-1] eq 'missing';
68              
69 24         219 my ( $kind, $size ) = splice @parts, -2;
70 24         694 return join( ' ', @parts ), $kind, $size;
71             }
72              
73             sub get_object_attributes {
74 156     156 0 1490 my ( $self, $digest ) = @_;
75 156         6018 my $factory = $self->object_factory;
76              
77             # request the object
78 156         570885 print { $factory->[2]} $digest, "\n";
  156         9913  
79              
80             # process the reply
81 156         1034 my $out = $factory->[1];
82 156         1939 local $/ = "\012";
83 156         66655 chomp( my $reply = <$out> );
84              
85             # protect against weird cases like if $sha1 contains a space
86 156         3042 my ( $sha1, $kind, $size ) = my @parts = split / /, $reply;
87              
88             # git versions >= 2.11.0.rc0 throw more verbose errors
89 156 50       1803 if ( $parts[0] =~ /^(?:symlink|dangling|loop|notdir)$/ ) {
90 0         0 <$out>; # eat the next line
91 0         0 return undef;
92             }
93              
94             # object does not exist in the git object database
95 156 100       1696 return undef if $parts[-1] eq 'missing';
96              
97             # read the whole content in memory at once
98 96         991 my $res = read $out, (my $content), $size;
99 96 50       684 if( $res != $size ) {
100 0         0 $factory->close; # in case the exception is trapped
101 0         0 $self->clear_object_factory;
102 0         0 die "Read $res/$size of content from git";
103             }
104              
105             # read the last byte
106 96         643 $res = read $out, (my $junk), 1;
107 96 50       452 if( $res != 1 ) {
108 0         0 $factory->close; # in case the exception is trapped
109 0         0 $self->clear_object_factory;
110 0         0 die "Unable to finish reading content from git";
111             }
112              
113             # careful with utf-8!
114             # create a new hash with kind, digest, content and size
115             return {
116 96         2542 kind => $kind,
117             size => $size,
118             content => $content,
119             digest => $sha1
120             };
121             }
122              
123             sub all_digests {
124 11     11 0 287256 my ( $self, $kind ) = @_;
125 11         134 my $store = $self->store;
126 11 100       539 my $re = $kind ? qr/ \Q$kind\E / : qr/ /;
127              
128             # the --batch-all-objects option appeared in v2.6.0-rc0
129 11 50       142 if ( ge_git $store->version, '2.6.0.rc0' ) {
130 11         73903 return map +( split / / )[0],
131             grep /$re/,
132             $store->command(qw( cat-file --batch-check --batch-all-objects ));
133             }
134             else { # this won't return unreachable objects
135 0         0 my ( $pid, $in, $out, $ctx ) =
136             $store->command_bidi_pipe(qw( cat-file --batch-check ));
137              
138             my @digests =
139             map +( split / / )[0], grep /$re/,
140 0         0 map { print {$out} ( split / / )[0], "\n"; $in->getline }
  0         0  
  0         0  
  0         0  
141             sort $store->command(qw( rev-list --all --objects ));
142 0         0 $store->command_close_bidi_pipe( $pid, $in, $out, $ctx );
143 0         0 return @digests;
144             }
145             }
146              
147             # Git::Database::Role::ObjectWriter
148             sub put_object {
149 13     13 0 75 my ( $self, $object ) = @_;
150 13         178 my ( $pid, $in, $out, $ctx ) =
151             $self->store->command_bidi_pipe( 'hash-object', '-t', $object->kind,
152             '-w', '--stdin' );
153 13         210265 print {$out} $object->content;
  13         1619  
154 13         968 close $out;
155 13         20516 chomp( my $digest = <$in> );
156 13         411 $self->store->command_close_bidi_pipe( $pid, $in, undef, $ctx ); # $out closed
157 13         2833 return $digest;
158             }
159              
160             # Git::Database::Role::RefReader
161             sub refs {
162 20     20 0 101127 my ($self) = @_;
163             return {
164 20         234 reverse map +( split / / ),
165             $self->store->command(qw( show-ref --head ))
166             };
167             }
168              
169             # Git::Database::Role::RefWriter
170             sub put_ref {
171 2     2 0 22354 my ($self, $refname, $digest ) = @_;
172 2         61 $self->store->command( 'update-ref', $refname, $digest );
173             }
174              
175             sub delete_ref {
176 2     2 0 20545 my ($self, $refname ) = @_;
177 2         76 $self->store->command( 'update-ref', '-d', $refname );
178             }
179              
180             sub DEMOLISH {
181 46     46 0 253192 my ( $self, $in_global_destruction ) = @_;
182 46 50       363 return if $in_global_destruction; # why bother?
183              
184 46 100       554 $self->store->command_close_bidi_pipe( @{ $self->object_factory } )
  37         861  
185             if $self->has_object_factory;
186 46 100       20584 $self->store->command_close_bidi_pipe( @{ $self->object_checker } )
  11         398  
187             if $self->has_object_checker;
188             }
189              
190             1;
191              
192             __END__
193              
194             =pod
195              
196             =for Pod::Coverage
197             has_object_checker
198             has_object_factory
199             DEMOLISH
200             hash_object
201             get_object_attributes
202             get_object_meta
203             all_digests
204             put_object
205             refs
206             put_ref
207             delete_ref
208              
209             =head1 NAME
210              
211             Git::Database::Backend::Git - A Git::Database backend based on Git
212              
213             =head1 VERSION
214              
215             version 0.011
216              
217             =head1 SYNOPSIS
218              
219             # get a store
220             my $r = Git->new();
221              
222             # let Git::Database produce the backend
223             my $db = Git::Database->new( store => $r );
224              
225             =head1 DESCRIPTION
226              
227             This backend reads and writes data from a Git repository using the
228             L<Git> Git wrapper.
229              
230             =head2 Git Database Roles
231              
232             This backend does the following roles
233             (check their documentation for a list of supported methods):
234             L<Git::Database::Role::Backend>,
235             L<Git::Database::Role::ObjectReader>,
236             L<Git::Database::Role::ObjectWriter>,
237             L<Git::Database::Role::RefReader>,
238             L<Git::Database::Role::RefWriter>.
239              
240             =head1 AUTHOR
241              
242             Philippe Bruhat (BooK) <book@cpan.org>
243              
244             =head1 COPYRIGHT
245              
246             Copyright 2016-2017 Philippe Bruhat (BooK), all rights reserved.
247              
248             =head1 LICENSE
249              
250             This program is free software; you can redistribute it and/or modify it
251             under the same terms as Perl itself.
252              
253             =cut