File Coverage

blib/lib/Git/Database/Backend/Git/Sub.pm
Criterion Covered Total %
statement 62 63 98.4
branch 8 10 80.0
condition n/a
subroutine 14 14 100.0
pod 0 8 0.0
total 84 95 88.4


line stmt bran cond sub pod time code
1             package Git::Database::Backend::Git::Sub;
2             $Git::Database::Backend::Git::Sub::VERSION = '0.011';
3 7         347 use Git::Sub qw(
4             cat_file
5             hash_object
6             rev_list
7             show_ref
8             update_ref
9             version
10 7     7   191623 );
  7         81  
11 7     7   1107 use Git::Version::Compare qw( ge_git );
  7         94  
  7         1528  
12 7     7   4010 use File::pushd qw( pushd );
  7         11811  
  7         628  
13 7     7   106 use File::Spec;
  7         71  
  7         314  
14              
15 7     7   74 use Moo;
  7         51  
  7         209  
16 7     7   26686 use namespace::clean;
  7         38  
  7         399  
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 30294 my ( $self, $object ) = @_;
46 95         1278 my $keeper = pushd $self->store;
47 95         18370 my $hash = git::hash_object
48             '-t' => $object->kind,
49             '--stdin' => \$object->content;
50 95         1251659 return $hash;
51             }
52              
53             # Git::Database::Role::ObjectReader
54             sub get_object_meta {
55 57     57 0 380 my ( $self, $digest ) = @_;
56 57         973 my $keeper = pushd $self->store;
57 57         12990 my $meta = git::cat_file
58             '--batch-check' => \"$digest\n";
59              
60             # protect against weird cases like if $digest contains a space
61 57         829816 my @parts = split / /, $meta;
62 57 100       1678 return ( $digest, 'missing', undef ) if $parts[-1] eq 'missing';
63              
64 24         377 my ( $kind, $size ) = splice @parts, -2;
65 24         841 return join( ' ', @parts ), $kind, $size;
66             }
67              
68             sub get_object_attributes {
69 156     156 0 1695 my ( $self, $digest ) = @_;
70              
71 156         458 my $out = do {
72 156         1914 my $keeper = pushd $self->store;
73 156         30593 local $/;
74 156         4127 git::cat_file '--batch' => \"$digest\n";
75             };
76              
77 156         2232959 my ( $meta, $content ) = split "\n", $out, 2;
78              
79             # protect against weird cases like if $digest contains a space
80 156         3229 my ( $sha1, $kind, $size ) = my @parts = split / /, $meta;
81              
82             # git versions >= 2.11.0.rc0 throw more verbose errors
83 156 50       2249 return undef if $parts[0] =~ /^(?:symlink|dangling|loop|notdir)$/;
84              
85             # object does not exist in the git object database
86 156 100       2802 return undef if $parts[-1] eq 'missing';
87              
88             return {
89 96         4453 kind => $kind,
90             size => $size,
91             content => substr( $content, 0, $size ),
92             digest => $sha1
93             };
94             }
95              
96             sub all_digests {
97 11     11 0 39299 my ( $self, $kind ) = @_;
98              
99 11 100       496 my $re = $kind ? qr/ \Q$kind\E / : qr/ /;
100 11         56 my @digests;
101              
102 11         212 my $keeper = pushd $self->store;
103              
104             # the --batch-all-objects option appeared in v2.6.0-rc0
105 11 50       2367 if ( ge_git git::version, '2.6.0.rc0' ) {
106 11         140326 @digests = map +( split / / )[0],
107             grep /$re/,
108             git::cat_file '--batch-check', '--batch-all-objects';
109             }
110             else { # this won't return unreachable objects
111 0         0 @digests =
112             map +( split / / )[0], grep /$re/,
113             git::cat_file '--batch-check', \join '', map +( split / / )[0] . "\n",
114             sort +git::rev_list '--all', '--objects';
115             }
116              
117 11         164659 return @digests;
118             }
119              
120             # Git::Database::Role::ObjectWriter
121             sub put_object {
122 13     13 0 182 my ( $self, $object ) = @_;
123 13         228 my $keeper = pushd $self->store;
124              
125 13         3012 my $hash = git::hash_object
126             '-w',
127             '-t' => $object->kind,
128             '--stdin' => \$object->content;
129 13         184855 return $hash;
130             }
131              
132             # Git::Database::Role::RefReader
133             sub refs {
134 20     20 0 17377 my ($self) = @_;
135 20         178 my $keeper = pushd $self->store;
136 20         3727 my %digest = reverse map +( split / / ),
137             git::show_ref '--head';
138 20         262854 return \%digest;
139             }
140              
141             # Git::Database::Role::RefWriter
142             sub put_ref {
143 2     2 0 3194 my ( $self, $refname, $digest ) = @_;
144 2         43 my $keeper = pushd $self->store;
145 2         392 git::update_ref( $refname, $digest );
146             return
147 2         64346 }
148              
149             sub delete_ref {
150 2     2 0 57 my ( $self, $refname ) = @_;
151 2         41 my $keeper = pushd $self->store;
152 2         383 git::update_ref( '-d', $refname );
153             return
154 2         66584 }
155              
156             1;
157              
158             __END__
159              
160             =pod
161              
162             =for Pod::Coverage
163             has_object_checker
164             has_object_factory
165             DEMOLISH
166             hash_object
167             get_object_attributes
168             get_object_meta
169             all_digests
170             put_object
171             refs
172             put_ref
173             delete_ref
174              
175             =head1 NAME
176              
177             Git::Database::Backend::Git::Sub - A Git::Database backend based on Git::Sub
178              
179             =head1 VERSION
180              
181             version 0.011
182              
183             =head1 SYNOPSIS
184              
185             # Git::Sub does not offer an OO interface
186             $dir = 'path/to/some/git/repository/';
187              
188             # let Git::Database figure it out by itself
189             my $db = Git::Database->new( store => $dir );
190              
191             =head1 DESCRIPTION
192              
193             This backend reads and writes data from a Git repository using the
194             L<Git::Sub> Git wrapper.
195              
196             Since L<Git::Sub> has a functional interface, the
197             L<store|Git::Database::Role::Backend/store> attribute is simply the path
198             in which the git commands will run. If the path is a relative path,
199             it will be coerced to an absolute path.
200              
201             Note that overloaded objects (such as L<Path::Tiny>, L<Path::Class> and
202             others) that stringify to the actual path are supported. When coercion
203             to an absolute path occurs, it attempts to create an object of the same
204             class representing the absolute path. If the coercion fails to create an
205             object of the same class, the store attribute will be a string containing
206             the absolute path.
207              
208             =head2 Git Database Roles
209              
210             This backend does the following roles
211             (check their documentation for a list of supported methods):
212             L<Git::Database::Role::Backend>,
213             L<Git::Database::Role::ObjectReader>,
214             L<Git::Database::Role::ObjectWriter>,
215             L<Git::Database::Role::RefReader>,
216             L<Git::Database::Role::RefWriter>.
217              
218             =head1 CAVEAT
219              
220             This backend may have issues with Perl 5.8.9, they are fixed in L<Git::Sub> 0.163320.
221              
222             There is also a minimum requirement on L<System::Sub> 0.162800.
223              
224             =head1 AUTHOR
225              
226             Philippe Bruhat (BooK) <book@cpan.org>
227              
228             =head1 COPYRIGHT
229              
230             Copyright 2016-2017 Philippe Bruhat (BooK), all rights reserved.
231              
232             =head1 LICENSE
233              
234             This program is free software; you can redistribute it and/or modify it
235             under the same terms as Perl itself.
236              
237             =cut