File Coverage

blib/lib/Git/Database/Backend/Git/Repository.pm
Criterion Covered Total %
statement 66 82 80.4
branch 18 24 75.0
condition n/a
subroutine 15 15 100.0
pod 0 9 0.0
total 99 130 76.1


line stmt bran cond sub pod time code
1             package Git::Database::Backend::Git::Repository;
2             $Git::Database::Backend::Git::Repository::VERSION = '0.011';
3 7     7   228418 use IO::Select;
  7         18536  
  7         1056  
4 7     7   146 use Sub::Quote;
  7         53  
  7         1209  
5              
6 7     7   111 use Moo;
  7         49  
  7         271  
7 7     7   26585 use namespace::clean;
  7         48  
  7         181  
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::Repository object'
20             if !eval { $_[0]->isa('Git::Repository') }
21             # die version check
22             } ),
23             default => sub { Git::Repository->new },
24             );
25              
26             has object_factory => (
27             is => 'lazy',
28             init_arg => undef,
29 37     37   1197 builder => sub { $_[0]->store->command( 'cat-file', '--batch' ); },
30             predicate => 1,
31             clearer => 1,
32             );
33              
34             has object_checker => (
35             is => 'lazy',
36             init_arg => undef,
37 11     11   418 builder => sub { $_[0]->store->command( 'cat-file', '--batch-check' ); },
38             predicate => 1,
39             clearer => 1,
40             );
41              
42             # Git::Database::Role::Backend
43             sub hash_object {
44 95     95 0 29350 my ($self, $object ) = @_;
45 95         1005 return scalar $self->store->run( 'hash-object', '-t', $object->kind,
46             '--stdin', { input => $object->content } );
47             }
48              
49             # Git::Database::Role::ObjectReader
50             sub get_object_meta {
51 57     57 0 352 my ( $self, $digest ) = @_;
52 57         1930 my $checker = $self->object_checker;
53              
54             # request the object
55 57         274233 print { $checker->stdin } $digest, "\n";
  57         525  
56              
57             # process the reply
58 57         3902 local $/ = "\012";
59 57         390 chomp( my $reply = $checker->stdout->getline );
60              
61             # git error messages
62 57         19656 my $bang;
63 57         432 my $select = IO::Select->new( my $err = $checker->stderr );
64 57         6061 $bang .= $err->getline while $select->can_read(0);
65 57 50       1585 warn $bang if $bang;
66              
67             # protect against weird cases like if $digest contains a space
68 57         447 my @parts = split / /, $reply;
69 57 100       1298 return ( $digest, 'missing', undef ) if $parts[-1] eq 'missing';
70              
71 24         187 my ( $kind, $size ) = splice @parts, -2;
72 24         684 return join( ' ', @parts ), $kind, $size;
73             }
74              
75             sub get_object_attributes {
76 156     156 0 1369 my ( $self, $digest ) = @_;
77 156         6198 my $factory = $self->object_factory;
78              
79             # request the object
80 156         834695 print { $factory->stdin } $digest, "\n";
  156         1062  
81              
82             # process the reply
83 156         8276 my $out = $factory->stdout;
84 156         2131 local $/ = "\012";
85 156         69600 chomp( my $reply = <$out> );
86              
87             # protect against weird cases like if $sha1 contains a space
88 156         2629 my ( $sha1, $kind, $size ) = my @parts = split / /, $reply;
89              
90             # git error messages
91 156         659 my $bang;
92 156         1182 my $select = IO::Select->new( my $err = $factory->stderr );
93 156         16507 $bang .= $err->getline while $select->can_read(0);
94 156 100       4495 warn $bang if $bang;
95              
96             # git versions >= 2.11.0.rc0 throw more verbose errors
97 156 50       1481 if ( $parts[0] =~ /^(?:symlink|dangling|loop|notdir)$/ ) {
98 0         0 <$out>; # eat the next line
99 0         0 return undef;
100             }
101              
102             # object does not exist in the git object database
103 156 100       1626 return undef if $parts[-1] eq 'missing';
104              
105             # read the whole content in memory at once
106 96         1078 my $res = read $out, (my $content), $size;
107 96 50       476 if( $res != $size ) {
108 0         0 $factory->close; # in case the exception is trapped
109 0         0 $self->clear_object_factory;
110 0         0 die "Read $res/$size of content from git";
111             }
112              
113             # read the last byte
114 96         525 $res = read $out, (my $junk), 1;
115 96 50       415 if( $res != 1 ) {
116 0         0 $factory->close; # in case the exception is trapped
117 0         0 $self->clear_object_factory;
118 0         0 die "Unable to finish reading content from git";
119             }
120              
121             # careful with utf-8!
122             # create a new hash with kind, digest, content and size
123             return {
124 96         2298 kind => $kind,
125             size => $size,
126             content => $content,
127             digest => $sha1
128             };
129             }
130              
131             sub all_digests {
132 11     11 0 553608 my ( $self, $kind ) = @_;
133 11         100 my $store = $self->store;
134 11 100       441 my $re = $kind ? qr/ \Q$kind\E / : qr/ /;
135              
136             # the --batch-all-objects option appeared in v2.6.0-rc0
137 11 50       875 if ( $store->version_ge('2.6.0.rc0') ) {
138 11         220573 return map +( split / / )[0],
139             grep /$re/,
140             $store->run(qw( cat-file --batch-check --batch-all-objects ));
141             }
142             else { # this won't return unreachable objects
143 0         0 my $batch = $store->command(qw( cat-file --batch-check ));
144 0         0 my ( $stdin, $stdout ) = ( $batch->stdin, $batch->stdout );
145             my @digests =
146             map +( split / / )[0], grep /$re/,
147 0         0 map { print {$stdin} ( split / / )[0], "\n"; $stdout->getline }
  0         0  
  0         0  
  0         0  
148             sort $store->run(qw( rev-list --all --objects ));
149 0         0 $batch->close;
150 0         0 return @digests;
151             }
152             }
153              
154             # Git::Database::Role::ObjectWriter
155             sub put_object {
156 13     13 0 96 my ( $self, $object ) = @_;
157 13         182 return scalar $self->store->run( 'hash-object', '-t', $object->kind,
158             '-w', '--stdin', { input => $object->content } );
159             }
160              
161             # Git::Database::Role::RefReader
162             sub refs {
163 20     20 0 208130 my ($self) = @_;
164             return {
165 20         202 reverse map +( split / / ),
166             $self->store->run(qw( show-ref --head ))
167             };
168             }
169              
170             # Git::Database::Role::RefWriter
171             sub put_ref {
172 2     2 0 46414 my ($self, $refname, $digest ) = @_;
173 2         48 $self->store->run( 'update-ref', $refname, $digest );
174             }
175              
176             sub delete_ref {
177 2     2 0 34 my ($self, $refname ) = @_;
178 2         44 $self->store->run( 'update-ref', '-d', $refname );
179             }
180              
181             sub DEMOLISH {
182 46     46 0 356933 my ( $self, $in_global_destruction ) = @_;
183 46 50       358 return if $in_global_destruction; # why bother?
184              
185 46 100       1505 $self->object_factory->close if $self->has_object_factory;
186 46 100       25081 $self->object_checker->close if $self->has_object_checker;
187             }
188              
189             1;
190              
191             __END__
192              
193             =pod
194              
195             =for Pod::Coverage
196             has_object_checker
197             has_object_factory
198             DEMOLISH
199             hash_object
200             get_object_attributes
201             get_object_meta
202             all_digests
203             put_object
204             refs
205             put_ref
206             delete_ref
207              
208             =head1 NAME
209              
210             Git::Database::Backend::Git::Repository - A Git::Database backend based on Git::Repository
211              
212             =head1 VERSION
213              
214             version 0.011
215              
216             =head1 SYNOPSIS
217              
218             # get a store
219             my $r = Git::Repository->new();
220              
221             # let Git::Database produce the backend
222             my $db = Git::Database->new( store => $r );
223              
224             =head1 DESCRIPTION
225              
226             This backend reads and writes data from a Git repository using the
227             L<Git::Repository> Git wrapper.
228              
229             =head2 Git Database Roles
230              
231             This backend does the following roles
232             (check their documentation for a list of supported methods):
233             L<Git::Database::Role::Backend>,
234             L<Git::Database::Role::ObjectReader>,
235             L<Git::Database::Role::ObjectWriter>,
236             L<Git::Database::Role::RefReader>,
237             L<Git::Database::Role::RefWriter>.
238              
239             =head1 AUTHOR
240              
241             Philippe Bruhat (BooK) <book@cpan.org>
242              
243             =head1 COPYRIGHT
244              
245             Copyright 2016-2017 Philippe Bruhat (BooK), all rights reserved.
246              
247             =head1 LICENSE
248              
249             This program is free software; you can redistribute it and/or modify it
250             under the same terms as Perl itself.
251              
252             =cut