File Coverage

blib/lib/Git/Database/Backend/Git/Repository.pm
Criterion Covered Total %
statement 67 83 80.7
branch 19 26 73.0
condition n/a
subroutine 15 15 100.0
pod 0 9 0.0
total 101 133 75.9


line stmt bran cond sub pod time code
1             package Git::Database::Backend::Git::Repository;
2             $Git::Database::Backend::Git::Repository::VERSION = '0.012';
3 7     7   194356 use IO::Select;
  7         14766  
  7         868  
4 7     7   127 use Sub::Quote;
  7         96  
  7         1184  
5              
6 7     7   96 use Moo;
  7         56  
  7         230  
7 7     7   25889 use namespace::clean;
  7         44  
  7         155  
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   931 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   291 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 27763 my ($self, $object ) = @_;
45 95         1072 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 260 my ( $self, $digest ) = @_;
52 57         1886 my $checker = $self->object_checker;
53              
54             # request the object
55 57         184797 print { $checker->stdin } $digest, "\n";
  57         502  
56              
57             # process the reply
58 57         3047 local $/ = "\012";
59 57         354 chomp( my $reply = $checker->stdout->getline );
60              
61             # git error messages
62 57         14241 my $bang;
63 57         424 my $select = IO::Select->new( my $err = $checker->stderr );
64 57         4874 $bang .= $err->getline while $select->can_read(0);
65 57 50       1425 warn $bang if $bang;
66              
67             # protect against weird cases like if $digest contains a space
68 57         503 my @parts = split / /, $reply;
69 57 100       1053 return ( $digest, 'missing', undef ) if $parts[-1] eq 'missing';
70              
71 24         163 my ( $kind, $size ) = splice @parts, -2;
72 24         558 return join( ' ', @parts ), $kind, $size;
73             }
74              
75             sub get_object_attributes {
76 156     156 0 1228 my ( $self, $digest ) = @_;
77 156         5001 my $factory = $self->object_factory;
78              
79             # request the object
80 156         643405 print { $factory->stdin } $digest, "\n";
  156         1101  
81              
82             # process the reply
83 156         7354 my $out = $factory->stdout;
84 156         1867 local $/ = "\012";
85 156         57986 chomp( my $reply = <$out> );
86              
87             # protect against weird cases like if $sha1 contains a space
88 156         2706 my ( $sha1, $kind, $size ) = my @parts = split / /, $reply;
89              
90             # git error messages
91 156         628 my $bang;
92 156         1014 my $select = IO::Select->new( my $err = $factory->stderr );
93 156         14346 $bang .= $err->getline while $select->can_read(0);
94 156 100       3987 warn $bang if $bang;
95              
96             # git versions >= 2.11.0.rc0 throw more verbose errors
97 156 50       1457 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       1425 return undef if $parts[-1] eq 'missing';
104              
105             # git versions >= 2.21.0.rc0 explicitely say if a sha1 is ambiguous
106 96 50       433 return undef if $kind eq 'ambiguous';
107              
108             # read the whole content in memory at once
109 96         864 my $res = read $out, (my $content), $size;
110 96 50       432 if( $res != $size ) {
111 0         0 $factory->close; # in case the exception is trapped
112 0         0 $self->clear_object_factory;
113 0         0 die "Read $res/$size of content from git";
114             }
115              
116             # read the last byte
117 96         516 $res = read $out, (my $junk), 1;
118 96 50       406 if( $res != 1 ) {
119 0         0 $factory->close; # in case the exception is trapped
120 0         0 $self->clear_object_factory;
121 0         0 die "Unable to finish reading content from git";
122             }
123              
124             # careful with utf-8!
125             # create a new hash with kind, digest, content and size
126             return {
127 96         2138 kind => $kind,
128             size => $size,
129             content => $content,
130             digest => $sha1
131             };
132             }
133              
134             sub all_digests {
135 11     11 0 487886 my ( $self, $kind ) = @_;
136 11         99 my $store = $self->store;
137 11 100       374 my $re = $kind ? qr/ \Q$kind\E / : qr/ /;
138              
139             # the --batch-all-objects option appeared in v2.6.0-rc0
140 11 50       861 if ( $store->version_ge('2.6.0.rc0') ) {
141 11         205053 return map +( split / / )[0],
142             grep /$re/,
143             $store->run(qw( cat-file --batch-check --batch-all-objects ));
144             }
145             else { # this won't return unreachable objects
146 0         0 my $batch = $store->command(qw( cat-file --batch-check ));
147 0         0 my ( $stdin, $stdout ) = ( $batch->stdin, $batch->stdout );
148             my @digests =
149             map +( split / / )[0], grep /$re/,
150 0         0 map { print {$stdin} ( split / / )[0], "\n"; $stdout->getline }
  0         0  
  0         0  
  0         0  
151             sort $store->run(qw( rev-list --all --objects ));
152 0         0 $batch->close;
153 0         0 return @digests;
154             }
155             }
156              
157             # Git::Database::Role::ObjectWriter
158             sub put_object {
159 13     13 0 140 my ( $self, $object ) = @_;
160 13         163 return scalar $self->store->run( 'hash-object', '-t', $object->kind,
161             '-w', '--stdin', { input => $object->content } );
162             }
163              
164             # Git::Database::Role::RefReader
165             sub refs {
166 20     20 0 172247 my ($self) = @_;
167             return {
168 20         135 reverse map +( split / / ),
169             $self->store->run(qw( show-ref --head ))
170             };
171             }
172              
173             # Git::Database::Role::RefWriter
174             sub put_ref {
175 2     2 0 32753 my ($self, $refname, $digest ) = @_;
176 2         36 $self->store->run( 'update-ref', $refname, $digest );
177             }
178              
179             sub delete_ref {
180 2     2 0 30 my ($self, $refname ) = @_;
181 2         42 $self->store->run( 'update-ref', '-d', $refname );
182             }
183              
184             sub DEMOLISH {
185 46     46 0 308941 my ( $self, $in_global_destruction ) = @_;
186 46 50       340 return if $in_global_destruction; # why bother?
187              
188 46 100       1078 $self->object_factory->close if $self->has_object_factory;
189 46 100       19211 $self->object_checker->close if $self->has_object_checker;
190             }
191              
192             1;
193              
194             __END__
195              
196             =pod
197              
198             =for Pod::Coverage
199             has_object_checker
200             has_object_factory
201             DEMOLISH
202             hash_object
203             get_object_attributes
204             get_object_meta
205             all_digests
206             put_object
207             refs
208             put_ref
209             delete_ref
210              
211             =head1 NAME
212              
213             Git::Database::Backend::Git::Repository - A Git::Database backend based on Git::Repository
214              
215             =head1 VERSION
216              
217             version 0.012
218              
219             =head1 SYNOPSIS
220              
221             # get a store
222             my $r = Git::Repository->new();
223              
224             # let Git::Database produce the backend
225             my $db = Git::Database->new( store => $r );
226              
227             =head1 DESCRIPTION
228              
229             This backend reads and writes data from a Git repository using the
230             L<Git::Repository> Git wrapper.
231              
232             =head2 Git Database Roles
233              
234             This backend does the following roles
235             (check their documentation for a list of supported methods):
236             L<Git::Database::Role::Backend>,
237             L<Git::Database::Role::ObjectReader>,
238             L<Git::Database::Role::ObjectWriter>,
239             L<Git::Database::Role::RefReader>,
240             L<Git::Database::Role::RefWriter>.
241              
242             =head1 AUTHOR
243              
244             Philippe Bruhat (BooK) <book@cpan.org>
245              
246             =head1 COPYRIGHT
247              
248             Copyright 2016-2019 Philippe Bruhat (BooK), all rights reserved.
249              
250             =head1 LICENSE
251              
252             This program is free software; you can redistribute it and/or modify it
253             under the same terms as Perl itself.
254              
255             =cut