File Coverage

blib/lib/Git/Database/Backend/Git/Sub.pm
Criterion Covered Total %
statement 63 64 98.4
branch 9 12 75.0
condition n/a
subroutine 14 14 100.0
pod 0 8 0.0
total 86 98 87.7


line stmt bran cond sub pod time code
1             package Git::Database::Backend::Git::Sub;
2             $Git::Database::Backend::Git::Sub::VERSION = '0.012';
3 7         335 use Git::Sub qw(
4             cat_file
5             hash_object
6             rev_list
7             show_ref
8             update_ref
9             version
10 7     7   148699 );
  7         70  
11 7     7   1022 use Git::Version::Compare qw( ge_git );
  7         59  
  7         1363  
12 7     7   3636 use File::pushd qw( pushd );
  7         11418  
  7         599  
13 7     7   90 use File::Spec;
  7         40  
  7         265  
14              
15 7     7   70 use Moo;
  7         57  
  7         211  
16 7     7   26889 use namespace::clean;
  7         50  
  7         214  
17              
18             with
19             'Git::Database::Role::Backend',
20             'Git::Database::Role::ObjectReader',
21             'Git::Database::Role::ObjectWriter',
22             'Git::Database::Role::RefReader',
23             'Git::Database::Role::RefWriter',
24             ;
25              
26             # the store attribute is a directory name
27             # or an object representing a directory
28             # (e.g. Path::Class, Path::Tiny, File::Fu)
29             has '+store' => (
30             is => 'ro',
31             required => 1,
32             predicate => 1,
33             coerce => sub {
34             my $dir = shift;
35             return # coerce to an absolute path
36             File::Spec->file_name_is_absolute($dir) ? $dir
37             : ref $dir ? eval { ref($dir)->new( File::Spec->rel2abs($dir) ) }
38             || File::Spec->rel2abs($dir)
39             : File::Spec->rel2abs($dir);
40             },
41             );
42              
43             # Git::Database::Role::Backend
44             sub hash_object {
45 95     95 0 28065 my ( $self, $object ) = @_;
46 95         1326 my $keeper = pushd $self->store;
47 95         17164 my $hash = git::hash_object
48             '-t' => $object->kind,
49             '--stdin' => \$object->content;
50 95         1002580 return $hash;
51             }
52              
53             # Git::Database::Role::ObjectReader
54             sub get_object_meta {
55 57     57 0 409 my ( $self, $digest ) = @_;
56 57         822 my $keeper = pushd $self->store;
57 57         11808 my $meta = git::cat_file
58             '--batch-check' => \"$digest\n";
59              
60             # protect against weird cases like if $digest contains a space
61 57         630202 my @parts = split / /, $meta;
62 57 100       1461 return ( $digest, 'missing', undef ) if $parts[-1] eq 'missing';
63              
64 24         415 my ( $kind, $size ) = splice @parts, -2;
65 24         642 return join( ' ', @parts ), $kind, $size;
66             }
67              
68             sub get_object_attributes {
69 156     156 0 1389 my ( $self, $digest ) = @_;
70              
71 156         642 my $out = do {
72 156         1960 my $keeper = pushd $self->store;
73 156         26999 local $/;
74 156         3750 git::cat_file '--batch' => \"$digest\n";
75             };
76              
77 156         1752170 my ( $meta, $content ) = split "\n", $out, 2;
78              
79             # protect against weird cases like if $digest contains a space
80 156         2901 my ( $sha1, $kind, $size ) = my @parts = split / /, $meta;
81              
82             # git versions >= 2.11.0.rc0 throw more verbose errors
83 156 50       2719 return undef if $parts[0] =~ /^(?:symlink|dangling|loop|notdir)$/;
84              
85             # object does not exist in the git object database
86 156 100       2955 return undef if $parts[-1] eq 'missing';
87              
88             # git versions >= 2.21.0.rc0 explicitely say if a sha1 is ambiguous
89 96 50       644 return undef if $kind eq 'ambiguous';
90              
91             return {
92 96         3934 kind => $kind,
93             size => $size,
94             content => substr( $content, 0, $size ),
95             digest => $sha1
96             };
97             }
98              
99             sub all_digests {
100 11     11 0 38960 my ( $self, $kind ) = @_;
101              
102 11 100       508 my $re = $kind ? qr/ \Q$kind\E / : qr/ /;
103 11         49 my @digests;
104              
105 11         201 my $keeper = pushd $self->store;
106              
107             # the --batch-all-objects option appeared in v2.6.0-rc0
108 11 50       2253 if ( ge_git git::version, '2.6.0.rc0' ) {
109 11         131109 @digests = map +( split / / )[0],
110             grep /$re/,
111             git::cat_file '--batch-check', '--batch-all-objects';
112             }
113             else { # this won't return unreachable objects
114 0         0 @digests =
115             map +( split / / )[0], grep /$re/,
116             git::cat_file '--batch-check', \join '', map +( split / / )[0] . "\n",
117             sort +git::rev_list '--all', '--objects';
118             }
119              
120 11         150507 return @digests;
121             }
122              
123             # Git::Database::Role::ObjectWriter
124             sub put_object {
125 13     13 0 144 my ( $self, $object ) = @_;
126 13         203 my $keeper = pushd $self->store;
127              
128 13         2425 my $hash = git::hash_object
129             '-w',
130             '-t' => $object->kind,
131             '--stdin' => \$object->content;
132 13         141459 return $hash;
133             }
134              
135             # Git::Database::Role::RefReader
136             sub refs {
137 20     20 0 16941 my ($self) = @_;
138 20         200 my $keeper = pushd $self->store;
139 20         3749 my %digest = reverse map +( split / / ),
140             git::show_ref '--head';
141 20         258569 return \%digest;
142             }
143              
144             # Git::Database::Role::RefWriter
145             sub put_ref {
146 2     2 0 3475 my ( $self, $refname, $digest ) = @_;
147 2         54 my $keeper = pushd $self->store;
148 2         458 git::update_ref( $refname, $digest );
149             return
150 2         62689 }
151              
152             sub delete_ref {
153 2     2 0 37 my ( $self, $refname ) = @_;
154 2         34 my $keeper = pushd $self->store;
155 2         346 git::update_ref( '-d', $refname );
156             return
157 2         43526 }
158              
159             1;
160              
161             __END__
162              
163             =pod
164              
165             =for Pod::Coverage
166             has_object_checker
167             has_object_factory
168             DEMOLISH
169             hash_object
170             get_object_attributes
171             get_object_meta
172             all_digests
173             put_object
174             refs
175             put_ref
176             delete_ref
177              
178             =head1 NAME
179              
180             Git::Database::Backend::Git::Sub - A Git::Database backend based on Git::Sub
181              
182             =head1 VERSION
183              
184             version 0.012
185              
186             =head1 SYNOPSIS
187              
188             # Git::Sub does not offer an OO interface
189             $dir = 'path/to/some/git/repository/';
190              
191             # let Git::Database figure it out by itself
192             my $db = Git::Database->new( store => $dir );
193              
194             =head1 DESCRIPTION
195              
196             This backend reads and writes data from a Git repository using the
197             L<Git::Sub> Git wrapper.
198              
199             Since L<Git::Sub> has a functional interface, the
200             L<store|Git::Database::Role::Backend/store> attribute is simply the path
201             in which the git commands will run. If the path is a relative path,
202             it will be coerced to an absolute path.
203              
204             Note that overloaded objects (such as L<Path::Tiny>, L<Path::Class> and
205             others) that stringify to the actual path are supported. When coercion
206             to an absolute path occurs, it attempts to create an object of the same
207             class representing the absolute path. If the coercion fails to create an
208             object of the same class, the store attribute will be a string containing
209             the absolute path.
210              
211             =head2 Git Database Roles
212              
213             This backend does the following roles
214             (check their documentation for a list of supported methods):
215             L<Git::Database::Role::Backend>,
216             L<Git::Database::Role::ObjectReader>,
217             L<Git::Database::Role::ObjectWriter>,
218             L<Git::Database::Role::RefReader>,
219             L<Git::Database::Role::RefWriter>.
220              
221             =head1 CAVEAT
222              
223             This backend may have issues with Perl 5.8.9, they are fixed in L<Git::Sub> 0.163320.
224              
225             There is also a minimum requirement on L<System::Sub> 0.162800.
226              
227             =head1 AUTHOR
228              
229             Philippe Bruhat (BooK) <book@cpan.org>
230              
231             =head1 COPYRIGHT
232              
233             Copyright 2016-2019 Philippe Bruhat (BooK), all rights reserved.
234              
235             =head1 LICENSE
236              
237             This program is free software; you can redistribute it and/or modify it
238             under the same terms as Perl itself.
239              
240             =cut