| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
## Config::Versioned |
|
2
|
|
|
|
|
|
|
## |
|
3
|
|
|
|
|
|
|
## Written 2011-2012 by Scott T. Hardin for the OpenXPKI project |
|
4
|
|
|
|
|
|
|
## Copyright (C) 2010-2012 by The OpenXPKI Project |
|
5
|
|
|
|
|
|
|
## |
|
6
|
|
|
|
|
|
|
## Was based on the CPAN module App::Options, but the import() stuff |
|
7
|
|
|
|
|
|
|
## bit me so we're turning into a Moose. |
|
8
|
|
|
|
|
|
|
## |
|
9
|
|
|
|
|
|
|
## vim: syntax=perl |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
package Config::Versioned; |
|
12
|
|
|
|
|
|
|
|
|
13
|
5
|
|
|
5
|
|
801917
|
use Moose; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use namespace::autoclean; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Config::Versioned - Simple, versioned access to configuration data |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '1.01'; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use Carp; |
|
25
|
|
|
|
|
|
|
use Config::Std; |
|
26
|
|
|
|
|
|
|
use Data::Dumper; |
|
27
|
|
|
|
|
|
|
use DateTime; |
|
28
|
|
|
|
|
|
|
use Git::PurePerl; |
|
29
|
|
|
|
|
|
|
use Path::Class; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
has 'path' => ( is => 'ro', isa => 'ArrayRef', default => sub { [qw( . )] } ); |
|
32
|
|
|
|
|
|
|
has 'filename' => ( is => 'ro', isa => 'Str' ); |
|
33
|
|
|
|
|
|
|
has 'dbpath' => |
|
34
|
|
|
|
|
|
|
( is => 'ro', default => 'cfgver.git', required => 1 ); |
|
35
|
|
|
|
|
|
|
has 'author_name' => ( is => 'ro', isa => 'Str', default => "process: $@" ); |
|
36
|
|
|
|
|
|
|
has 'author_mail' => ( |
|
37
|
|
|
|
|
|
|
is => 'ro', |
|
38
|
|
|
|
|
|
|
isa => 'Str', |
|
39
|
|
|
|
|
|
|
default => $ENV{GIT_AUTHOR_EMAIL} || $ENV{USER} . '@localhost' |
|
40
|
|
|
|
|
|
|
); |
|
41
|
|
|
|
|
|
|
has 'autocreate' => ( is => 'ro', isa => 'Bool', default => 0 ); |
|
42
|
|
|
|
|
|
|
has 'commit_time' => ( is => 'ro', isa => 'DateTime' ); |
|
43
|
|
|
|
|
|
|
has 'comment' => ( is => 'rw', isa => 'Str' ); |
|
44
|
|
|
|
|
|
|
has 'delimiter' => ( is => 'ro', isa => 'Str', default => '.' ); |
|
45
|
|
|
|
|
|
|
has 'delimiter_regex' => |
|
46
|
|
|
|
|
|
|
( is => 'ro', isa => 'RegexpRef', default => sub { qr{ \. }xms } ); |
|
47
|
|
|
|
|
|
|
has 'log_get_callback' => ( is => 'ro' ); |
|
48
|
|
|
|
|
|
|
has '_git' => ( is => 'rw' ); |
|
49
|
|
|
|
|
|
|
has 'debug' => ( is => 'rw', isa => 'Int', default => 0 ); |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# a reference to the singleton Config::Versioned object that parsed the command line |
|
52
|
|
|
|
|
|
|
#my ($default_option_processor); |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
#my (%path_is_secure); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
use Config::Versioned; |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $cfg = Config::Versioned->new(); |
|
61
|
|
|
|
|
|
|
my $param1 = $cfg->get('subsystem1.group.param1'); |
|
62
|
|
|
|
|
|
|
my $old1 = $cfg->get('subsystem1.group.param1', $version); |
|
63
|
|
|
|
|
|
|
my @keys = $cfg->list('subsys1.db'); |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Config::Versioned allows an application to access configuration parameters |
|
69
|
|
|
|
|
|
|
not only by parameter name, but also by version number. This allows for |
|
70
|
|
|
|
|
|
|
the configuration subsystem to store previous versions of the configuration |
|
71
|
|
|
|
|
|
|
parameters. When requesting the value for a specific attribute, the programmer |
|
72
|
|
|
|
|
|
|
specifies whether to fetch the most recent value or a previous value. |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
This is useful for long-running tasks such as in a workflow-based application |
|
75
|
|
|
|
|
|
|
where task-specific values (e.g.: profiles) are static over the life of a |
|
76
|
|
|
|
|
|
|
workflow, while global values (e.g.: name of an LDAP server to be queried) |
|
77
|
|
|
|
|
|
|
should always be the most recent. |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Config::Versioned handles the versions by storing the configuration data |
|
80
|
|
|
|
|
|
|
in an internal Git repository. Each import of configuration files into |
|
81
|
|
|
|
|
|
|
the repository is documented with a commit. When a value is fetched, it is |
|
82
|
|
|
|
|
|
|
this commit that is referenced directly when specifying the version. |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
The access to the individual attributes is via a named-parameter scheme, where |
|
85
|
|
|
|
|
|
|
the key is a dot-separated string. |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Currently, C<Config::Std> is used for the import of the data files into the |
|
88
|
|
|
|
|
|
|
internal Git repository. Support for other configuration modules (e.g.: |
|
89
|
|
|
|
|
|
|
C<Config::Any>) is planned. |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head1 METHODS |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head2 init() |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
This is invoked automatically via import(). It is called when running the |
|
96
|
|
|
|
|
|
|
following code: |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
use Config::Versioned; |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
The init() method reads the configuration data from the configuration files |
|
101
|
|
|
|
|
|
|
and populates an internal data structure. |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Optionally, parameters may be passed to init(). The following |
|
104
|
|
|
|
|
|
|
named-parameters are supported: |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=over 8 |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=item path |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Specifies an anonymous array contianing the names of the directories to |
|
111
|
|
|
|
|
|
|
check for the configuration files. |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
path => qw( /etc/yourapp/etc /etc/yourapp/local/etc . ), |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
The default path is just the current directory. |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item filename |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Specifies the name of the configuration file to be found in the given path. |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
filename => qw( yourapp.conf ), |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
If no filename is given, no new configuration data will be imported and |
|
124
|
|
|
|
|
|
|
the internal git repository will be used. |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item dbpath |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
The directory for the internal git repository that stores the config. |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
dbpath => qw( config.git ), |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
The default is "cfgver.git". |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=item author_name, author_mail |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
The name and e-mail address to use in the internal git repository for |
|
137
|
|
|
|
|
|
|
commits. |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item autocreate |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
If no internal git repository exists, it will be created during code |
|
142
|
|
|
|
|
|
|
initialization. Note that if an import filename is specified, this |
|
143
|
|
|
|
|
|
|
automatically sets autocreate to true. |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
autocreate => 1, |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
The default is "0". |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Note: this option might become deprecated. I just wanted some extra |
|
150
|
|
|
|
|
|
|
"insurance" during the early stages of development. |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item commit_time |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
This sets the time to use for the commits in the internal git repository. |
|
155
|
|
|
|
|
|
|
It is used for debugging purposes only! |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Note: this must be a DateTime object instance. |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item delimiter |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
Specifies the delimiter used to separate the different levels in the |
|
162
|
|
|
|
|
|
|
string used to designate the location of a configuration parameter. [Default: '.'] |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item delimiter_regex |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Specifies the delimiter used to separate the different levels in the |
|
167
|
|
|
|
|
|
|
string used to designate the location of a configuration parameter. |
|
168
|
|
|
|
|
|
|
[Default: qr/ \. /xms] |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item log_get_callback |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Specifies a callback function to be called by get() after fetching |
|
173
|
|
|
|
|
|
|
the value for the given key. The subroutine should accept the |
|
174
|
|
|
|
|
|
|
parameters LOCATION, VERSION, VALUE. The VALUE may either be a single |
|
175
|
|
|
|
|
|
|
scalar value or an array reference containing a list of values. |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub cb_log_get { |
|
178
|
|
|
|
|
|
|
my $self = shift; |
|
179
|
|
|
|
|
|
|
my $loc = shift; |
|
180
|
|
|
|
|
|
|
my $ver = shift; |
|
181
|
|
|
|
|
|
|
my $val = shift; |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
warn "Access config parameter: $loc ($ver) => ", |
|
184
|
|
|
|
|
|
|
ref($val) eq 'ARRAY' |
|
185
|
|
|
|
|
|
|
? join(', ', @{ $val }) |
|
186
|
|
|
|
|
|
|
: $val, |
|
187
|
|
|
|
|
|
|
"\n"; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
my $cfg = Config::Versioned->new( { log_get_callback => 'cb_log_get' } ); |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Note: if log_get_callback is a code ref, it will be called as a function. |
|
192
|
|
|
|
|
|
|
Otherwise, the log_get_callback will specify a method name that is to be |
|
193
|
|
|
|
|
|
|
called on the current object instance. |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=back |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=head2 BUILD( { PARAMS } ) |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
NOTE: This is used internally, so the typical user shouldn't bother with this. |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
This is called after an object is created. When cloning, it is important that |
|
202
|
|
|
|
|
|
|
the new instance gets a reference to the same Git::PurePerl instance. This |
|
203
|
|
|
|
|
|
|
will prevent two instances from getting out of sync if modifications are made |
|
204
|
|
|
|
|
|
|
to the configuration data at runtime. To handle this, the parameter 'GITREF' |
|
205
|
|
|
|
|
|
|
must be passed when cloning. |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Note 2: this should be handled automatically in the _near_ future. |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
my $cv2 = $cv1->new( GITREF => $cv1->_git() ); |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub BUILD { |
|
214
|
|
|
|
|
|
|
my $self = shift; |
|
215
|
|
|
|
|
|
|
my $args = shift; |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
if ( defined $ENV{CONFIG_VERSIONED_DEBUG} ) { |
|
218
|
|
|
|
|
|
|
$self->debug( $ENV{CONFIG_VERSIONED_DEBUG} ); |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
if ( not $self->_init_repo() ) { |
|
222
|
|
|
|
|
|
|
return; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
# if ( not $self->_git() ) { |
|
225
|
|
|
|
|
|
|
# if ( $args->{GITREF} ) { |
|
226
|
|
|
|
|
|
|
# $self->_git( $args->{GITREF} ); |
|
227
|
|
|
|
|
|
|
# } |
|
228
|
|
|
|
|
|
|
# else { |
|
229
|
|
|
|
|
|
|
# if ( not $self->_init_repo() ) { |
|
230
|
|
|
|
|
|
|
# return; |
|
231
|
|
|
|
|
|
|
# } |
|
232
|
|
|
|
|
|
|
# } |
|
233
|
|
|
|
|
|
|
# } |
|
234
|
|
|
|
|
|
|
# |
|
235
|
|
|
|
|
|
|
# $self->parser($args); |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
return ($self); |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head2 get( LOCATION [, VERSION ] ) |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
This is the accessor for fetching the value(s) of the given parameter. The |
|
243
|
|
|
|
|
|
|
value may either be zero or more elements. |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
In list context, the values are returned. In scalar context, C<undef> is |
|
246
|
|
|
|
|
|
|
returned if the variable is empty. Otherwise, the first element is returned. |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Optionally, a VERSION may be specified to return the value for that |
|
249
|
|
|
|
|
|
|
specific version. |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=cut |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
sub get { |
|
254
|
|
|
|
|
|
|
my $self = shift; |
|
255
|
|
|
|
|
|
|
my $location = shift; |
|
256
|
|
|
|
|
|
|
my $version = shift; |
|
257
|
|
|
|
|
|
|
my $cb = $self->log_get_callback(); |
|
258
|
|
|
|
|
|
|
my ( $obj, $deobj ) = $self->_findobjx( $location, $version ); |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
if ( not defined $obj ) { |
|
261
|
|
|
|
|
|
|
$self->$cb( $location, $version, '<undefined>' ) if $cb; |
|
262
|
|
|
|
|
|
|
return; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
if ( $obj->kind eq 'blob' ) { |
|
266
|
|
|
|
|
|
|
$self->$cb( $location, $version, $obj->content ) if $cb; |
|
267
|
|
|
|
|
|
|
if ( $deobj->mode() == 120000 ) { |
|
268
|
|
|
|
|
|
|
my $tmp = $obj->content; |
|
269
|
|
|
|
|
|
|
return \$tmp; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
else { |
|
272
|
|
|
|
|
|
|
return $obj->content; |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
elsif ( $obj->kind eq 'tree' ) { |
|
276
|
|
|
|
|
|
|
my @entries = $obj->directory_entries; |
|
277
|
|
|
|
|
|
|
my @ret = (); |
|
278
|
|
|
|
|
|
|
foreach my $de (@entries) { |
|
279
|
|
|
|
|
|
|
push @ret, $de->filename; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
my @sorted = |
|
282
|
|
|
|
|
|
|
sort { ( $a =~ /^\d+$/ and $b =~ /^\d+$/ ) ? $a <=> $b : $a cmp $b } |
|
283
|
|
|
|
|
|
|
@ret; |
|
284
|
|
|
|
|
|
|
$self->$cb( $location, $version, \@sorted ) if $cb; |
|
285
|
|
|
|
|
|
|
return @sorted; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
else { |
|
288
|
|
|
|
|
|
|
$self->$cb( $location, $version, |
|
289
|
|
|
|
|
|
|
"<error: non-blob object '" . $obj->kind . "' not supported>" ) |
|
290
|
|
|
|
|
|
|
if $cb; |
|
291
|
|
|
|
|
|
|
warn "# DEBUG: get() was asked to return a non-blob object [kind=", |
|
292
|
|
|
|
|
|
|
$obj->kind, "]\n" if $self->debug(); |
|
293
|
|
|
|
|
|
|
return; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head2 kind ( LOCATION [, VERSION ] ) |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
The get() method tries to return a scalar when the location corresponds |
|
300
|
|
|
|
|
|
|
to a single value and a list when the location has child nodes. Sometimes, |
|
301
|
|
|
|
|
|
|
however, it is helpful to have a definitive answer on what a location |
|
302
|
|
|
|
|
|
|
contains. |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
The kind() method returns the object type that the given location accesses. |
|
305
|
|
|
|
|
|
|
This can be one of the following values: |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=over |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=item tree |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
The given location contains a tree object containing zero or more child |
|
312
|
|
|
|
|
|
|
objects. The get() method will return a list of the entry names. |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=item blob |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
The data node that usually contains a scalar value, but in future implementations |
|
317
|
|
|
|
|
|
|
may contain other encoded data. |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=back |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
B<Note:> As a side-effect, this can be used to test whether the given location |
|
322
|
|
|
|
|
|
|
exists at all in the configuration. If not found, C<undef> is returned. |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=cut |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub kind { |
|
327
|
|
|
|
|
|
|
my $self = shift; |
|
328
|
|
|
|
|
|
|
my $location = shift; |
|
329
|
|
|
|
|
|
|
my $version = shift; |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
my $obj = $self->_findobj( $location, $version ); |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
if ( not defined $obj ) { |
|
334
|
|
|
|
|
|
|
return; # if nothing found, just return undef |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
if ( $obj->kind eq 'blob' ) { |
|
338
|
|
|
|
|
|
|
return 'blob'; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
elsif ( $obj->kind eq 'tree' ) { |
|
341
|
|
|
|
|
|
|
return 'tree'; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
else { |
|
344
|
|
|
|
|
|
|
$@ = "Internal object error (expected tree or blob): [gpp kind=" |
|
345
|
|
|
|
|
|
|
. $obj->kind . "]\n"; |
|
346
|
|
|
|
|
|
|
warn "# DEBUG: " . $@ if $self->debug(); |
|
347
|
|
|
|
|
|
|
return; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=head2 listattr( LOCATION [, VERSION ] ) |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
This fetches a list of the parameters available for a given location in the |
|
355
|
|
|
|
|
|
|
configuration tree. |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=cut |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub listattr { |
|
360
|
|
|
|
|
|
|
my $self = shift; |
|
361
|
|
|
|
|
|
|
my $location = shift; |
|
362
|
|
|
|
|
|
|
my $version = shift; |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
my $obj = $self->_findobj( $location, $version ); |
|
365
|
|
|
|
|
|
|
if ( $obj and $obj->kind eq 'tree' ) { |
|
366
|
|
|
|
|
|
|
my @entries = $obj->directory_entries; |
|
367
|
|
|
|
|
|
|
my @ret = (); |
|
368
|
|
|
|
|
|
|
foreach my $de (@entries) { |
|
369
|
|
|
|
|
|
|
push @ret, $de->filename; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
return @ret; |
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
else { |
|
374
|
|
|
|
|
|
|
$@ = "obj at $location not found"; |
|
375
|
|
|
|
|
|
|
return; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 dumptree( [ VERSION ] ) |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
This fetches the entire tree for the given version (default: newest version) |
|
382
|
|
|
|
|
|
|
and returns a hashref to a named-parameter list. |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=cut |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub dumptree { |
|
387
|
|
|
|
|
|
|
my $self = shift; |
|
388
|
|
|
|
|
|
|
my $version = shift; |
|
389
|
|
|
|
|
|
|
my $cfg = $self->_git(); |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# If no version hash was given, default to the HEAD of master |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
if ( not $version ) { |
|
394
|
|
|
|
|
|
|
my $master = $self->_git()->ref('refs/heads/master'); |
|
395
|
|
|
|
|
|
|
if ( $master ) { |
|
396
|
|
|
|
|
|
|
$version = $master->sha1; |
|
397
|
|
|
|
|
|
|
} else { |
|
398
|
|
|
|
|
|
|
# if no sha1s are in repo, there's nothing to return |
|
399
|
|
|
|
|
|
|
return; |
|
400
|
|
|
|
|
|
|
} |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
my $obj = $cfg->get_object($version); |
|
404
|
|
|
|
|
|
|
if ( not $obj ) { |
|
405
|
|
|
|
|
|
|
$@ = "No object found for SHA1 " . $version ? $version : ''; |
|
406
|
|
|
|
|
|
|
return; |
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
if ( $obj->kind eq 'commit' ) { |
|
410
|
|
|
|
|
|
|
$obj = $obj->tree; |
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
my $ret = {}; |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
my @directory_entries = $obj->directory_entries; |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
foreach my $de (@directory_entries) { |
|
418
|
|
|
|
|
|
|
my $child = $cfg->get_object( $de->sha1 ); |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# warn "DEBUG: dump - child name = ", $de->filename, "\n"; |
|
421
|
|
|
|
|
|
|
# warn "DEBUG: dump - child kind = ", $child->kind, "\n"; |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
if ( $child->kind eq 'tree' ) { |
|
424
|
|
|
|
|
|
|
my $subret = $self->dumptree( $de->sha1 ); |
|
425
|
|
|
|
|
|
|
foreach my $key ( keys %{$subret} ) { |
|
426
|
|
|
|
|
|
|
$ret->{ $de->filename . $self->delimiter() . $key } = |
|
427
|
|
|
|
|
|
|
$subret->{$key}; |
|
428
|
|
|
|
|
|
|
} |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
elsif ( $child->kind eq 'blob' ) { |
|
431
|
|
|
|
|
|
|
$ret->{ $de->filename } = $child->content; |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
else { |
|
434
|
|
|
|
|
|
|
die "ERROR: unexpected kind: ", $child->kind, "\n"; |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
return $ret; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 version |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
This returns the current version of the configuration database, which |
|
444
|
|
|
|
|
|
|
happens to be the SHA1 hash of the HEAD of the internal git repository. |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Optionally, a version hash may be passed and version() will return a true |
|
447
|
|
|
|
|
|
|
value if it is found. |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=cut |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub version { |
|
452
|
|
|
|
|
|
|
my $self = shift; |
|
453
|
|
|
|
|
|
|
my $version = shift; |
|
454
|
|
|
|
|
|
|
my $cfg = $self->_git(); |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
if ($version) { |
|
457
|
|
|
|
|
|
|
my $obj = $cfg->get_object($version); |
|
458
|
|
|
|
|
|
|
if ( $obj and $obj->sha1 eq $version ) { |
|
459
|
|
|
|
|
|
|
return $version; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
else { |
|
462
|
|
|
|
|
|
|
return; |
|
463
|
|
|
|
|
|
|
} |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
else { |
|
466
|
|
|
|
|
|
|
my $head = $cfg->head; |
|
467
|
|
|
|
|
|
|
return $head->sha1; |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
} |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=head1 INTERNALS |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=head2 _init_repo |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Initializes the internal git repository used for storing the config |
|
476
|
|
|
|
|
|
|
values. |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
If the I<objects> directory in the C<dbpath> does not exist, an |
|
479
|
|
|
|
|
|
|
C<init()> on the C<Git::PurePerl> class is run. Otherwise, the |
|
480
|
|
|
|
|
|
|
instance is initialized using the existing bare repository. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
On error, it returns C<undef> and the reason is in C<$@>. |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=cut |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub _init_repo { |
|
487
|
|
|
|
|
|
|
my $self = shift; |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
my $git; |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# if ( not $init_args->{dbpath} ) { |
|
492
|
|
|
|
|
|
|
# die "ERROR: dbpath not set"; |
|
493
|
|
|
|
|
|
|
# } |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
if ( not -d $self->dbpath() . '/objects' ) { |
|
496
|
|
|
|
|
|
|
if ( $self->filename() || $self->autocreate() ) { |
|
497
|
|
|
|
|
|
|
if ( not -d $self->dbpath() ) { |
|
498
|
|
|
|
|
|
|
if ( not dir( $self->dbpath() )->mkpath ) { |
|
499
|
|
|
|
|
|
|
die 'Error creating directory ' . $self->dbpath() . ': ' . $!; |
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
$git = Git::PurePerl->init( gitdir => $self->dbpath() ); |
|
503
|
|
|
|
|
|
|
} else { |
|
504
|
|
|
|
|
|
|
die 'Error: dbpath (' . $self->dbpath() . ') does not exist'; |
|
505
|
|
|
|
|
|
|
} |
|
506
|
|
|
|
|
|
|
} |
|
507
|
|
|
|
|
|
|
else { |
|
508
|
|
|
|
|
|
|
$git = Git::PurePerl->new( gitdir => $self->dbpath() ); |
|
509
|
|
|
|
|
|
|
} |
|
510
|
|
|
|
|
|
|
$self->_git($git); |
|
511
|
|
|
|
|
|
|
$self->parser(); |
|
512
|
|
|
|
|
|
|
return $self; |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=head2 _get_anon_scalar |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Creates an anonymous scalar for representing symlinks in the tree structure. |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=cut |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
sub _get_anon_scalar { |
|
522
|
|
|
|
|
|
|
my $temp = shift; |
|
523
|
|
|
|
|
|
|
return \$temp; |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head2 parser ARGS |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Imports the configuration read and writes it to the internal database. If no |
|
529
|
|
|
|
|
|
|
filename is passed as an argument, then it will quietly skip the commit. |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
Note: if you override this method in a child class, it must create an |
|
532
|
|
|
|
|
|
|
anonymous hash tree and pass the reference to the commit() method. Here |
|
533
|
|
|
|
|
|
|
is a simple example: |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
sub parser { |
|
536
|
|
|
|
|
|
|
my $self = shift; |
|
537
|
|
|
|
|
|
|
my $args = shift; |
|
538
|
|
|
|
|
|
|
$args->{comment} = 'import from my perl hash'; |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
my $cfg = { |
|
541
|
|
|
|
|
|
|
group1 => { |
|
542
|
|
|
|
|
|
|
subgroup1 => { |
|
543
|
|
|
|
|
|
|
param1 => 'val1', |
|
544
|
|
|
|
|
|
|
param2 => 'val2', |
|
545
|
|
|
|
|
|
|
}, |
|
546
|
|
|
|
|
|
|
}, |
|
547
|
|
|
|
|
|
|
group2 => { |
|
548
|
|
|
|
|
|
|
subgroup1 => { |
|
549
|
|
|
|
|
|
|
param3 => 'val3', |
|
550
|
|
|
|
|
|
|
param4 => 'val4', |
|
551
|
|
|
|
|
|
|
}, |
|
552
|
|
|
|
|
|
|
}, |
|
553
|
|
|
|
|
|
|
# This creates a symlink from 'group3.subgroup3' to 'connector1/group4'. |
|
554
|
|
|
|
|
|
|
# Note the use of the scalar reference using the backslash. |
|
555
|
|
|
|
|
|
|
group3 => { |
|
556
|
|
|
|
|
|
|
subgroup3 => \'connector1/group4', |
|
557
|
|
|
|
|
|
|
}, |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
}; |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# pass original args, appended with a comment string for the commit |
|
562
|
|
|
|
|
|
|
$self->commit( $cfg, $args ); |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
In the comment, you should include details on where the config came from |
|
566
|
|
|
|
|
|
|
(i.e.: the filename or directory). |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=cut |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
sub parser { |
|
571
|
|
|
|
|
|
|
my $self = shift; |
|
572
|
|
|
|
|
|
|
my $args = shift; |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
foreach |
|
575
|
|
|
|
|
|
|
my $key (qw( comment filename path author_name author_mail commit_time )) |
|
576
|
|
|
|
|
|
|
{ |
|
577
|
|
|
|
|
|
|
if ( not exists $args->{$key} ) { |
|
578
|
|
|
|
|
|
|
$args->{$key} = $self->$key(); |
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# If no filename was specified, then there is no import of |
|
583
|
|
|
|
|
|
|
# configuration files needed. Quietly exit method. |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
if ( not $args->{filename} ) { |
|
586
|
|
|
|
|
|
|
return $self; |
|
587
|
|
|
|
|
|
|
} |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# Read the configuration from the import files |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
my %cfg = (); |
|
592
|
|
|
|
|
|
|
$self->_read_config_path( $args->{filename}, \%cfg, @{ $args->{path} } ); |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
$args->{comment} ||= "Import config from " |
|
595
|
|
|
|
|
|
|
. $self->_which( $args->{filename}, @{ $args->{path} } ); |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# convert the foreign data structure to a simple hash tree, |
|
598
|
|
|
|
|
|
|
# where the value is either a scalar or a hash reference. |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
my $tmphash = {}; |
|
601
|
|
|
|
|
|
|
foreach my $sect ( keys %cfg ) { |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# build up the underlying branch for these leaves |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
my @sectpath = split( $self->delimiter_regex(), $sect ); |
|
606
|
|
|
|
|
|
|
my $sectref = $tmphash; |
|
607
|
|
|
|
|
|
|
foreach my $nodename (@sectpath) { |
|
608
|
|
|
|
|
|
|
$sectref->{$nodename} ||= {}; |
|
609
|
|
|
|
|
|
|
$sectref = $sectref->{$nodename}; |
|
610
|
|
|
|
|
|
|
} |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
# now add the leaves |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
foreach my $leaf ( keys %{ $cfg{$sect} } ) { |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# If the leaf start or ends with an '@', treat it as |
|
617
|
|
|
|
|
|
|
# a symbolic link. |
|
618
|
|
|
|
|
|
|
if ( $leaf =~ |
|
619
|
|
|
|
|
|
|
m{ (?: \A @ (.*?) @ \z | \A @ (.*) | (.*?) @ \z ) }xms ) |
|
620
|
|
|
|
|
|
|
{ |
|
621
|
|
|
|
|
|
|
my $match = $1 || $2 || $3; |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
# make it a ref to an anonymous scalar so we know it's a symlink |
|
624
|
|
|
|
|
|
|
#my $t = _get_anon_scalar($1); |
|
625
|
|
|
|
|
|
|
$sectref->{$match} = \( $cfg{$sect}{$leaf} ); |
|
626
|
|
|
|
|
|
|
} |
|
627
|
|
|
|
|
|
|
else { |
|
628
|
|
|
|
|
|
|
$sectref->{$leaf} = $cfg{$sect}{$leaf}; |
|
629
|
|
|
|
|
|
|
} |
|
630
|
|
|
|
|
|
|
} |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
$self->commit( $tmphash, $args ); |
|
635
|
|
|
|
|
|
|
} |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=head2 commit CFGHASH[, ARGS] |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
Import the configuration tree in the CFGHASH anonymous hash and commit |
|
640
|
|
|
|
|
|
|
the modifications to the internal git bare repository. |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
ARGS is a ref to a named-parameter list (e.g. HASH) that may contain the |
|
643
|
|
|
|
|
|
|
following keys to override the instance defaults: |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
author_name, author_mail, comment, commit_time |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=cut |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
sub commit { |
|
650
|
|
|
|
|
|
|
my $self = shift; |
|
651
|
|
|
|
|
|
|
my $hash = shift; |
|
652
|
|
|
|
|
|
|
my $args = shift; |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
if ( ref($hash) ne 'HASH' ) { |
|
655
|
|
|
|
|
|
|
confess "ERR: commit() - arg not hash ref [$hash]"; |
|
656
|
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
my $parent = undef; |
|
659
|
|
|
|
|
|
|
my $master = undef; |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
$master = $self->_git()->ref('refs/heads/master'); |
|
662
|
|
|
|
|
|
|
if ( $master ) { |
|
663
|
|
|
|
|
|
|
$parent = $master->sha1; |
|
664
|
|
|
|
|
|
|
} |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
# warn "# author_name: ", $self->author_name(), "\n"; |
|
667
|
|
|
|
|
|
|
my $tree = $self->_hash2tree($hash); |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
670
|
|
|
|
|
|
|
print join( "\n# ", '', $self->_debugtree($tree) ), "\n"; |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
# |
|
674
|
|
|
|
|
|
|
# Now that we have a "staging" tree, compare its hash with |
|
675
|
|
|
|
|
|
|
# that of the current top-level tree. If they are the same, |
|
676
|
|
|
|
|
|
|
# there were no changes made to the config and we should |
|
677
|
|
|
|
|
|
|
# not create a commit object |
|
678
|
|
|
|
|
|
|
# |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
if ( $parent and $master->tree->sha1 eq $tree->sha1 ) { |
|
681
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
682
|
|
|
|
|
|
|
carp("Nothing to commit (index matches HEAD)"); |
|
683
|
|
|
|
|
|
|
} |
|
684
|
|
|
|
|
|
|
return $self; |
|
685
|
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
# |
|
688
|
|
|
|
|
|
|
# Prepare and execute the commit |
|
689
|
|
|
|
|
|
|
# |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
my $actor = Git::PurePerl::Actor->new( |
|
692
|
|
|
|
|
|
|
name => $args->{author_name} || $self->author_name, |
|
693
|
|
|
|
|
|
|
email => $args->{author_mail} || $self->author_mail, |
|
694
|
|
|
|
|
|
|
); |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
my $time = $args->{commit_time} || $self->commit_time || DateTime->now; |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
my @commit_attrs = ( |
|
699
|
|
|
|
|
|
|
tree => $tree->sha1, |
|
700
|
|
|
|
|
|
|
author => $actor, |
|
701
|
|
|
|
|
|
|
authored_time => $time, |
|
702
|
|
|
|
|
|
|
committer => $actor, |
|
703
|
|
|
|
|
|
|
committed_time => $time, |
|
704
|
|
|
|
|
|
|
comment => $args->{comment} || $self->comment(), |
|
705
|
|
|
|
|
|
|
); |
|
706
|
|
|
|
|
|
|
if ($parent) { |
|
707
|
|
|
|
|
|
|
push @commit_attrs, parent => $parent; |
|
708
|
|
|
|
|
|
|
} |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
my $commit = Git::PurePerl::NewObject::Commit->new(@commit_attrs); |
|
711
|
|
|
|
|
|
|
$self->_git()->put_object($commit); |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
sub _hash2tree { |
|
716
|
|
|
|
|
|
|
my $self = shift; |
|
717
|
|
|
|
|
|
|
my $hash = shift; |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
if ( ref($hash) ne 'HASH' ) { |
|
720
|
|
|
|
|
|
|
confess "ERR: _hash2tree() - arg not hash ref [$hash]"; |
|
721
|
|
|
|
|
|
|
} |
|
722
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
723
|
|
|
|
|
|
|
warn "Entered _hash2tree( $hash ): ", join( ', ', %{$hash} ), "\n"; |
|
724
|
|
|
|
|
|
|
} |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
my @dir_entries = (); |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
foreach my $key ( keys %{$hash} ) { |
|
729
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
730
|
|
|
|
|
|
|
warn "# _hash2tree() processing $key -> ", $hash->{$key}, "\n"; |
|
731
|
|
|
|
|
|
|
} |
|
732
|
|
|
|
|
|
|
if ( ref( $hash->{$key} ) eq 'HASH' ) { |
|
733
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
734
|
|
|
|
|
|
|
warn "# _hash2tree() adding subtree for $key\n"; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
my $subtree = $self->_hash2tree( $hash->{$key} ); |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
next unless($subtree); |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
my $local_key = $key; |
|
741
|
|
|
|
|
|
|
if ( $] > 5.007 && utf8::is_utf8($local_key) ) { |
|
742
|
|
|
|
|
|
|
utf8::downgrade($local_key); |
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
my $de = Git::PurePerl::NewDirectoryEntry->new( |
|
746
|
|
|
|
|
|
|
mode => '40000', |
|
747
|
|
|
|
|
|
|
filename => $local_key, |
|
748
|
|
|
|
|
|
|
sha1 => $subtree->sha1(), |
|
749
|
|
|
|
|
|
|
); |
|
750
|
|
|
|
|
|
|
push @dir_entries, $de; |
|
751
|
|
|
|
|
|
|
} |
|
752
|
|
|
|
|
|
|
elsif ( ref( $hash->{$key} ) eq 'SCALAR' ) { |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
# Support for symbolic links |
|
755
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
756
|
|
|
|
|
|
|
warn "# _hash2tree() adding symlink for $key\n"; |
|
757
|
|
|
|
|
|
|
} |
|
758
|
|
|
|
|
|
|
my $obj = |
|
759
|
|
|
|
|
|
|
Git::PurePerl::NewObject::Blob->new( |
|
760
|
|
|
|
|
|
|
content => ${ $hash->{$key} } ); |
|
761
|
|
|
|
|
|
|
$self->_git()->put_object($obj); |
|
762
|
|
|
|
|
|
|
my $local_key = $key; |
|
763
|
|
|
|
|
|
|
if ( $] > 5.007 && utf8::is_utf8($local_key) ) { |
|
764
|
|
|
|
|
|
|
utf8::downgrade($local_key); |
|
765
|
|
|
|
|
|
|
} |
|
766
|
|
|
|
|
|
|
my $de = Git::PurePerl::NewDirectoryEntry->new( |
|
767
|
|
|
|
|
|
|
mode => '120000', # symlink |
|
768
|
|
|
|
|
|
|
filename => $local_key, |
|
769
|
|
|
|
|
|
|
sha1 => $obj->sha1(), |
|
770
|
|
|
|
|
|
|
); |
|
771
|
|
|
|
|
|
|
push @dir_entries, $de; |
|
772
|
|
|
|
|
|
|
} |
|
773
|
|
|
|
|
|
|
elsif ( defined $hash->{$key} ) { |
|
774
|
|
|
|
|
|
|
my $obj = |
|
775
|
|
|
|
|
|
|
Git::PurePerl::NewObject::Blob->new( content => $hash->{$key} ); |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
my $local_key = $key; |
|
778
|
|
|
|
|
|
|
if ( $] > 5.007 && utf8::is_utf8($local_key) ) { |
|
779
|
|
|
|
|
|
|
utf8::downgrade($local_key); |
|
780
|
|
|
|
|
|
|
} |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
warn "# created blob for '$key' with sha " . $obj->sha1() if $self->debug(); |
|
783
|
|
|
|
|
|
|
warn "# '$key' utf8 flag: ", utf8::is_utf8($key) if $self->debug(); |
|
784
|
|
|
|
|
|
|
$self->_git()->put_object($obj); |
|
785
|
|
|
|
|
|
|
my $de = Git::PurePerl::NewDirectoryEntry->new( |
|
786
|
|
|
|
|
|
|
mode => '100644', # plain file |
|
787
|
|
|
|
|
|
|
filename => $local_key, |
|
788
|
|
|
|
|
|
|
sha1 => $obj->sha1(), |
|
789
|
|
|
|
|
|
|
); |
|
790
|
|
|
|
|
|
|
push @dir_entries, $de; |
|
791
|
|
|
|
|
|
|
} else { |
|
792
|
|
|
|
|
|
|
warn "# _hash2tree() value is undef for key $key\n" if $self->debug(); |
|
793
|
|
|
|
|
|
|
} |
|
794
|
|
|
|
|
|
|
} |
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
if (!scalar @dir_entries) { |
|
797
|
|
|
|
|
|
|
warn "# _hash2tree() nothing to push\n" if $self->debug();; |
|
798
|
|
|
|
|
|
|
return undef; |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
my $tree = |
|
802
|
|
|
|
|
|
|
Git::PurePerl::NewObject::Tree->new( directory_entries => |
|
803
|
|
|
|
|
|
|
[ sort { $a->filename cmp $b->filename } @dir_entries ] ); |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
if ( $self->debug() ) { |
|
806
|
|
|
|
|
|
|
my $content = $tree->content; |
|
807
|
|
|
|
|
|
|
$content =~ s/(.)/sprintf("%x",ord($1))/eg; |
|
808
|
|
|
|
|
|
|
warn "# Added tree with dir entries: ", |
|
809
|
|
|
|
|
|
|
join( ', ', map { $_->filename } @dir_entries ), "\n"; |
|
810
|
|
|
|
|
|
|
warn "# content: ", $content, "\n"; |
|
811
|
|
|
|
|
|
|
warn "# size: ", $tree->size, "\n"; |
|
812
|
|
|
|
|
|
|
warn "# kind: ", $tree->kind, "\n"; |
|
813
|
|
|
|
|
|
|
warn "# sha1: ", $tree->sha1, "\n"; |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
} |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
$self->_git()->put_object($tree); |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
return $tree; |
|
820
|
|
|
|
|
|
|
} |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=head2 _mknode LOCATION |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
Creates a node at the given LOCATION, creating parent nodes if necessary. |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
A reference to the node at the LOCATION is returned. |
|
827
|
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=cut |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
sub _mknode { |
|
831
|
|
|
|
|
|
|
my $self = shift; |
|
832
|
|
|
|
|
|
|
my $location = shift; |
|
833
|
|
|
|
|
|
|
my $ref = $self->_git(); |
|
834
|
|
|
|
|
|
|
foreach my $key ( split( $self->delimiter_regex(), $location ) ) { |
|
835
|
|
|
|
|
|
|
if ( not exists $ref->{$key} ) { |
|
836
|
|
|
|
|
|
|
$ref->{$key} = {}; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
elsif ( ref( $ref->{$key} ) ne 'HASH' ) { |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
# TODO: fix this ugly error to something more appropriate |
|
841
|
|
|
|
|
|
|
die "Location at $key in $location already assigned to non-HASH"; |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
$ref = $ref->{$key}; |
|
844
|
|
|
|
|
|
|
} |
|
845
|
|
|
|
|
|
|
return $ref; |
|
846
|
|
|
|
|
|
|
} |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=head2 _findobjx LOCATION [, VERSION ] |
|
849
|
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
Returns the Git::PurePerl and Git::PurePerl::DirectoryEntry objects found in |
|
851
|
|
|
|
|
|
|
the file path at LOCATION. |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
my ($ref1, $de1) = $cfg->_findnode("smartcard.ldap.uri"); |
|
854
|
|
|
|
|
|
|
my $ref2, $de2) = $cfg->_findnode("certs.signature.duration", $wfcfgver); |
|
855
|
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
In most cases, the C<_findobj> version is sufficient. This extended version |
|
857
|
|
|
|
|
|
|
is used to look at the attribtes of the directory entry for things like whether |
|
858
|
|
|
|
|
|
|
the blob is a symlink. |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
=cut |
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
sub _findobjx { |
|
863
|
|
|
|
|
|
|
my $self = shift; |
|
864
|
|
|
|
|
|
|
my $location = shift; |
|
865
|
|
|
|
|
|
|
my $ver = shift; |
|
866
|
|
|
|
|
|
|
my $cfg = $self->_git(); |
|
867
|
|
|
|
|
|
|
my ( $obj, $deobj ); |
|
868
|
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
# If no version hash was given, default to the HEAD of master |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
if ( not $ver ) { |
|
872
|
|
|
|
|
|
|
my $master = $self->_git()->ref('refs/heads/master'); |
|
873
|
|
|
|
|
|
|
if ( $master ) { |
|
874
|
|
|
|
|
|
|
$ver = $master->sha1; |
|
875
|
|
|
|
|
|
|
} else { |
|
876
|
|
|
|
|
|
|
# if no sha1s are in repo, there's nothing to return |
|
877
|
|
|
|
|
|
|
return; |
|
878
|
|
|
|
|
|
|
} |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
} |
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
# TODO: is this the way we want to handle the error of not finding |
|
883
|
|
|
|
|
|
|
# the given object? |
|
884
|
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
$obj = $cfg->get_object($ver); |
|
886
|
|
|
|
|
|
|
if ( not $obj ) { |
|
887
|
|
|
|
|
|
|
$@ = "No object found for SHA1 $ver"; |
|
888
|
|
|
|
|
|
|
return; |
|
889
|
|
|
|
|
|
|
} |
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
if ( $obj->kind eq 'commit' ) { |
|
892
|
|
|
|
|
|
|
$obj = $obj->tree; |
|
893
|
|
|
|
|
|
|
} |
|
894
|
|
|
|
|
|
|
my @keys = split $self->delimiter_regex(), $location; |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
# iterate thru the levels in the location |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
while (@keys) { |
|
899
|
|
|
|
|
|
|
my $key = shift @keys; |
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
# if the object is a blob, we already reached the leaf |
|
902
|
|
|
|
|
|
|
if ($obj->kind eq 'blob') { |
|
903
|
|
|
|
|
|
|
return undef; |
|
904
|
|
|
|
|
|
|
} |
|
905
|
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
# $obj should contain the parent tree object. |
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
my @directory_entries = $obj->directory_entries; |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
# find the corresponding child object |
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
my $found = 0; |
|
913
|
|
|
|
|
|
|
foreach my $de (@directory_entries) { |
|
914
|
|
|
|
|
|
|
if ( $de->filename eq $key ) { |
|
915
|
|
|
|
|
|
|
$found++; |
|
916
|
|
|
|
|
|
|
$obj = $cfg->get_object( $de->sha1 ); |
|
917
|
|
|
|
|
|
|
$deobj = $de; |
|
918
|
|
|
|
|
|
|
last; |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
if ( not $found ) { |
|
923
|
|
|
|
|
|
|
return; |
|
924
|
|
|
|
|
|
|
} |
|
925
|
|
|
|
|
|
|
} |
|
926
|
|
|
|
|
|
|
return $obj, $deobj; |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
} |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
=head2 _findobj LOCATION [, VERSION ] |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
Returns the Git::PurePerl object found in the file path at LOCATION. |
|
933
|
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
my $ref1 = $cfg->_findnode("smartcard.ldap.uri"); |
|
935
|
|
|
|
|
|
|
my $ref2 = $cfg->_findnode("certs.signature.duration", $wfcfgver); |
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
=cut |
|
938
|
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
sub _findobj { |
|
940
|
|
|
|
|
|
|
my $self = shift; |
|
941
|
|
|
|
|
|
|
my ( $obj, $deobj ) = $self->_findobjx(@_); |
|
942
|
|
|
|
|
|
|
if ( defined $obj ) { |
|
943
|
|
|
|
|
|
|
return $obj; |
|
944
|
|
|
|
|
|
|
} |
|
945
|
|
|
|
|
|
|
else { |
|
946
|
|
|
|
|
|
|
return; |
|
947
|
|
|
|
|
|
|
} |
|
948
|
|
|
|
|
|
|
} |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
=head2 _get_sect_key LOCATION |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
Returns the section and key needed by Config::Std to access the |
|
953
|
|
|
|
|
|
|
configuration values. The given LOCATION is split on the last delimiter. |
|
954
|
|
|
|
|
|
|
The resulting section and key are returned as a list. |
|
955
|
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=cut |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
sub _get_sect_key { |
|
959
|
|
|
|
|
|
|
my $self = shift; |
|
960
|
|
|
|
|
|
|
my $key = shift; |
|
961
|
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
# Config::Std uses section/key, so we need to split up the |
|
963
|
|
|
|
|
|
|
# given key |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
my @tokens = split( $self->delimiter_regex(), $key ); |
|
966
|
|
|
|
|
|
|
$key = pop @tokens; |
|
967
|
|
|
|
|
|
|
my $sect = join( $self->delimiter(), @tokens ); |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
return $sect, $key; |
|
970
|
|
|
|
|
|
|
} |
|
971
|
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
=head2 _which( NAME, DIR ... ) |
|
973
|
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
Searches the directory list DIR, returning the full path in which the file NAME was |
|
975
|
|
|
|
|
|
|
found. |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=cut |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
sub _which { |
|
980
|
|
|
|
|
|
|
my $self = shift; |
|
981
|
|
|
|
|
|
|
my $name = shift; |
|
982
|
|
|
|
|
|
|
my @dirs = @_; |
|
983
|
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
foreach (@dirs) { |
|
985
|
|
|
|
|
|
|
my $path = $_ . '/' . $name; |
|
986
|
|
|
|
|
|
|
if ( -f $path ) { |
|
987
|
|
|
|
|
|
|
return $path; |
|
988
|
|
|
|
|
|
|
} |
|
989
|
|
|
|
|
|
|
} |
|
990
|
|
|
|
|
|
|
return; |
|
991
|
|
|
|
|
|
|
} |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
=head2 _read_config_path SELF, FILENAME, CFGREF, PATH |
|
994
|
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
Searches for FILENAME in the given directories in PATH. When found, |
|
996
|
|
|
|
|
|
|
the file is parsed and a data structure is written to the location |
|
997
|
|
|
|
|
|
|
in CFGREF. |
|
998
|
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
Note: this is the wrapper around the underlying libs that read the |
|
1000
|
|
|
|
|
|
|
configuration data from the files. |
|
1001
|
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
=cut |
|
1003
|
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
sub _read_config_path { |
|
1005
|
|
|
|
|
|
|
my $self = shift; |
|
1006
|
|
|
|
|
|
|
my $cfgname = shift; |
|
1007
|
|
|
|
|
|
|
my $cfgref = shift; |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
my $cfgfile = $self->_which( $cfgname, @_ ); |
|
1010
|
|
|
|
|
|
|
if ( not $cfgfile ) { |
|
1011
|
|
|
|
|
|
|
die "ERROR: couldn't find $cfgname in ", join( ', ', @_ ); |
|
1012
|
|
|
|
|
|
|
} |
|
1013
|
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
read_config( $cfgfile => %{$cfgref} ); |
|
1015
|
|
|
|
|
|
|
} |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
=head2 _debugtree( OBJREF | SHA1 ) |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
This fetches the entire tree for the given SHA1 and dumps it in a |
|
1020
|
|
|
|
|
|
|
human-readable format. |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
=cut |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
sub _debugtree { |
|
1025
|
|
|
|
|
|
|
my $self = shift; |
|
1026
|
|
|
|
|
|
|
my $start = shift; |
|
1027
|
|
|
|
|
|
|
my $indent = shift || 0; |
|
1028
|
|
|
|
|
|
|
my $cfg = $self->_git(); |
|
1029
|
|
|
|
|
|
|
my @out = (); |
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
my $tabsize = 2; |
|
1032
|
|
|
|
|
|
|
my $obj; |
|
1033
|
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
# Soooo, let's see what we've been fed... |
|
1035
|
|
|
|
|
|
|
if ( not $start ) { # default to the HEAD of master |
|
1036
|
|
|
|
|
|
|
my $master = $cfg->ref('refs/heads/master'); |
|
1037
|
|
|
|
|
|
|
if ( $master ) { |
|
1038
|
|
|
|
|
|
|
$obj = $cfg->get_object( $master->sha1 ); |
|
1039
|
|
|
|
|
|
|
} |
|
1040
|
|
|
|
|
|
|
else { |
|
1041
|
|
|
|
|
|
|
push @out, "NO SHA1s IN TREE"; |
|
1042
|
|
|
|
|
|
|
return @out; # if no sha1s are in repo, there's nothing to return |
|
1043
|
|
|
|
|
|
|
} |
|
1044
|
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
} |
|
1046
|
|
|
|
|
|
|
elsif ( not ref($start) ) { # possibly a sha1 |
|
1047
|
|
|
|
|
|
|
$obj = $cfg->get_object($start); |
|
1048
|
|
|
|
|
|
|
if ( not $obj ) { |
|
1049
|
|
|
|
|
|
|
$@ = "No object found for SHA1 " . $start ? $start : ''; |
|
1050
|
|
|
|
|
|
|
return $@; |
|
1051
|
|
|
|
|
|
|
} |
|
1052
|
|
|
|
|
|
|
} |
|
1053
|
|
|
|
|
|
|
elsif ( ref($start) =~ /^(REF|SCALAR|ARRAY|HASH|CODE|GLOB)$/ ) { |
|
1054
|
|
|
|
|
|
|
croak( "_debugtree doesn't support ref type " . ref($start) ); |
|
1055
|
|
|
|
|
|
|
} |
|
1056
|
|
|
|
|
|
|
else { |
|
1057
|
|
|
|
|
|
|
$obj = $start; |
|
1058
|
|
|
|
|
|
|
} |
|
1059
|
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
# At this point, we should have a Git::PurePerl (new) Object. |
|
1061
|
|
|
|
|
|
|
# Let's double-check. |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
if ( $obj->can('kind') ) { |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
# push @out, ( ' ' x ( $tabsize * $indent ) ) . ('=' x 40); |
|
1066
|
|
|
|
|
|
|
#foreach my $attr (qw( kind size content sha1 git )) { |
|
1067
|
|
|
|
|
|
|
foreach my $attr (qw( kind size sha1 )) { |
|
1068
|
|
|
|
|
|
|
if ( $obj->can($attr) ) { |
|
1069
|
|
|
|
|
|
|
push @out, |
|
1070
|
|
|
|
|
|
|
( ' ' x ( $tabsize * $indent ) ) . $attr . ': ' . $obj->$attr; |
|
1071
|
|
|
|
|
|
|
} |
|
1072
|
|
|
|
|
|
|
} |
|
1073
|
|
|
|
|
|
|
} |
|
1074
|
|
|
|
|
|
|
elsif ($obj->isa('Git::PurePerl::NewDirectoryEntry') |
|
1075
|
|
|
|
|
|
|
or $obj->isa('Git::PurePerl::DirectoryEntry') ) |
|
1076
|
|
|
|
|
|
|
{ |
|
1077
|
|
|
|
|
|
|
foreach my $attr (qw( mode filename sha1 )) { |
|
1078
|
|
|
|
|
|
|
if ( $obj->can($attr) ) { |
|
1079
|
|
|
|
|
|
|
push @out, |
|
1080
|
|
|
|
|
|
|
( ' ' x ( $tabsize * $indent ) ) . $attr . ': ' . $obj->$attr; |
|
1081
|
|
|
|
|
|
|
} |
|
1082
|
|
|
|
|
|
|
} |
|
1083
|
|
|
|
|
|
|
push @out, $self->_debugtree( $obj->sha1, $indent + 1 ); |
|
1084
|
|
|
|
|
|
|
return @out; |
|
1085
|
|
|
|
|
|
|
} |
|
1086
|
|
|
|
|
|
|
else { |
|
1087
|
|
|
|
|
|
|
die "Obj $obj doesn't seem to be supported"; |
|
1088
|
|
|
|
|
|
|
} |
|
1089
|
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
if ( $obj->kind eq 'commit' ) { |
|
1091
|
|
|
|
|
|
|
foreach my $attr ( |
|
1092
|
|
|
|
|
|
|
qw( tree_sha1 parent_sha1s author authored_time committer |
|
1093
|
|
|
|
|
|
|
commited_time comment encoding ) |
|
1094
|
|
|
|
|
|
|
) |
|
1095
|
|
|
|
|
|
|
{ |
|
1096
|
|
|
|
|
|
|
if ( $obj->can($attr) ) { |
|
1097
|
|
|
|
|
|
|
push @out, |
|
1098
|
|
|
|
|
|
|
( ' ' x ( $tabsize * $indent ) ) . $attr . ': ' . $obj->$attr; |
|
1099
|
|
|
|
|
|
|
} |
|
1100
|
|
|
|
|
|
|
} |
|
1101
|
|
|
|
|
|
|
push @out, $self->_debugtree( $obj->tree, $indent + 1 ); |
|
1102
|
|
|
|
|
|
|
} |
|
1103
|
|
|
|
|
|
|
elsif ( $obj->kind eq 'tree' ) { |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
push @out, ( ' ' x ( $tabsize * $indent ) ) . 'raw: '; |
|
1106
|
|
|
|
|
|
|
push @out, map { |
|
1107
|
|
|
|
|
|
|
chomp $_; |
|
1108
|
|
|
|
|
|
|
( ' ' x ( $tabsize * $indent ) ) . $_ |
|
1109
|
|
|
|
|
|
|
} hdump( $obj->kind . ' ' . $obj->size . "\0" . $obj->content ); |
|
1110
|
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
my $sha1a = Digest::SHA->new; |
|
1112
|
|
|
|
|
|
|
$sha1a->add( $obj->kind . ' ' . $obj->size . "\0" . $obj->content ); |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
push @out, |
|
1115
|
|
|
|
|
|
|
( ' ' x ( $tabsize * $indent ) ) |
|
1116
|
|
|
|
|
|
|
. 'my sha1 from Digest::SHA: ' |
|
1117
|
|
|
|
|
|
|
. $sha1a->hexdigest; |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
my @directory_entries = $obj->directory_entries; |
|
1120
|
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
foreach my $de (@directory_entries) { |
|
1122
|
|
|
|
|
|
|
push @out, |
|
1123
|
|
|
|
|
|
|
( ' ' x ( $tabsize * $indent ) ) |
|
1124
|
|
|
|
|
|
|
. 'Directory Entry: '; # . $de->filename; |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
push @out, $self->_debugtree( $de, $indent + 1 ); |
|
1127
|
|
|
|
|
|
|
} |
|
1128
|
|
|
|
|
|
|
} |
|
1129
|
|
|
|
|
|
|
elsif ( $obj->kind eq 'blob' ) { |
|
1130
|
|
|
|
|
|
|
push @out, ' ' x ( $tabsize * ($indent) ) . 'content: '; |
|
1131
|
|
|
|
|
|
|
push @out, ( ' ' x ( $tabsize * ( $indent + 1 ) ) ) |
|
1132
|
|
|
|
|
|
|
. join( |
|
1133
|
|
|
|
|
|
|
"\n" . ( ' ' x ( $tabsize * ( $indent + 1 ) ) ), |
|
1134
|
|
|
|
|
|
|
split( /\n/, $obj->content ) |
|
1135
|
|
|
|
|
|
|
); |
|
1136
|
|
|
|
|
|
|
} |
|
1137
|
|
|
|
|
|
|
else { |
|
1138
|
|
|
|
|
|
|
push @out, |
|
1139
|
|
|
|
|
|
|
' ' x ( $tabsize * $indent ) |
|
1140
|
|
|
|
|
|
|
. 'Dump object kind ' |
|
1141
|
|
|
|
|
|
|
. $obj->kind |
|
1142
|
|
|
|
|
|
|
. ' not implemented'; |
|
1143
|
|
|
|
|
|
|
} |
|
1144
|
|
|
|
|
|
|
return @out; |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
} |
|
1147
|
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
=head2 hdump |
|
1149
|
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
Return hexdump of given data. |
|
1151
|
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
=cut |
|
1153
|
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
sub hdump { |
|
1155
|
|
|
|
|
|
|
my $offset = 0; |
|
1156
|
|
|
|
|
|
|
my @out = (); |
|
1157
|
|
|
|
|
|
|
my ( @array, $format ); |
|
1158
|
|
|
|
|
|
|
foreach |
|
1159
|
|
|
|
|
|
|
my $data ( unpack( "a16" x ( length( $_[0] ) / 16 ) . "a*", $_[0] ) ) |
|
1160
|
|
|
|
|
|
|
{ |
|
1161
|
|
|
|
|
|
|
my ($len) = length($data); |
|
1162
|
|
|
|
|
|
|
if ( $len == 16 ) { |
|
1163
|
|
|
|
|
|
|
@array = unpack( 'N4', $data ); |
|
1164
|
|
|
|
|
|
|
$format = "0x%08x (%05d) %08x %08x %08x %08x %s\n"; |
|
1165
|
|
|
|
|
|
|
} |
|
1166
|
|
|
|
|
|
|
else { |
|
1167
|
|
|
|
|
|
|
@array = unpack( 'C*', $data ); |
|
1168
|
|
|
|
|
|
|
$_ = sprintf "%2.2x", $_ for @array; |
|
1169
|
|
|
|
|
|
|
push( @array, ' ' ) while $len++ < 16; |
|
1170
|
|
|
|
|
|
|
$format = |
|
1171
|
|
|
|
|
|
|
"0x%08x (%05d)" . " %s%s%s%s %s%s%s%s %s%s%s%s %s%s%s%s %s\n"; |
|
1172
|
|
|
|
|
|
|
} |
|
1173
|
|
|
|
|
|
|
$data =~ tr/\0-\37\177-\377/./; |
|
1174
|
|
|
|
|
|
|
push @out, sprintf $format, $offset, $offset, @array, $data; |
|
1175
|
|
|
|
|
|
|
$offset += 16; |
|
1176
|
|
|
|
|
|
|
} |
|
1177
|
|
|
|
|
|
|
return @out; |
|
1178
|
|
|
|
|
|
|
} |
|
1179
|
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
Was based on the CPAN module App::Options, but since been converted to Moose. |
|
1183
|
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1185
|
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
Scott T. Hardin, C<< <mrscotty at cpan.org> >> |
|
1187
|
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
Martin Bartosch |
|
1189
|
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
Oliver Welter |
|
1191
|
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=head1 BUGS |
|
1193
|
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
Please report any bugs or feature requests to C<bug-config-versioned at |
|
1195
|
|
|
|
|
|
|
rt.cpan.org>, or through the web interface at |
|
1196
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Config-Versioned>. |
|
1197
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress |
|
1198
|
|
|
|
|
|
|
on your bug as I make changes. |
|
1199
|
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
=head1 SUPPORT |
|
1201
|
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
1203
|
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
perldoc Config::Versioned |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
You can also look for information at: |
|
1208
|
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
=over 4 |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
|
1212
|
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Config-Versioned> |
|
1214
|
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
L<http://annocpan.org/dist/Config-Versioned> |
|
1218
|
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
1220
|
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/Config-Versioned> |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
=item * Search CPAN |
|
1224
|
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/Config-Versioned/> |
|
1226
|
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
=back |
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
1231
|
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
Copyright 2011 Scott T. Hardin, all rights reserved. |
|
1233
|
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
This program is free software; you can redistribute it |
|
1235
|
|
|
|
|
|
|
and/or modify it under the same terms as Perl itself. |
|
1236
|
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
=cut |
|
1238
|
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
|
1240
|
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
1; # End of Config::Versioned |
|
1242
|
|
|
|
|
|
|
|