File Coverage

blib/lib/SVN/Simple/Edit.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package SVN::Simple::Edit;
2             @ISA = qw(SVN::Delta::Editor);
3             $VERSION = '0.28';
4 1     1   37399 use strict;
  1         3  
  1         41  
5 1     1   473 use SVN::Core;
  0            
  0            
6             use SVN::Delta;
7              
8             =head1 NAME
9              
10             SVN::Simple::Edit - A simple interface for driving svn delta editors
11              
12             =head1 SYNOPSIS
13              
14             my $edit = SVN::Simple::Edit->new
15             (_editor => [SVN::Repos::get_commit_editor($repos, "file://$repospath",
16             '/', 'root', 'FOO', \&committed)],
17             );
18              
19             $edit->open_root($fs->youngest_rev);
20             $edit->add_directory ('trunk');
21             $edit->add_file ('trunk/filea');
22             $edit->modify_file ("trunk/fileb", "content", $checksum);
23             $edit->delete_entry ("trunk/filec");
24             $edit->close_edit ();
25             ...
26             $edit->copy_directory ('branches/a, trunk, 0);
27              
28             =head1 DESCRIPTION
29              
30             SVN::Simple::Edit wraps the subversion delta editor with a perl
31             friendly interface and then you could easily drive it for describing
32             changes to a tree. A common usage is to wrap the commit editor, so
33             you could make commits to a subversion repository easily.
34              
35             This also means you can not supply the C<$edit> object as an
36             delta_editor to other API, and that's why this module is named
37             B<::Edit> instead of B<::Editor>.
38              
39             See L for simple interface implementing a delta editor.
40              
41             =head1 PARAMETERS
42              
43             =head2 for constructor
44              
45             =over
46              
47             =item _editor
48              
49             The editor that will receive delta editor calls.
50              
51             =item missing_handler
52              
53             Called when parent directory are not opened yet, could be:
54              
55             =over
56              
57             =item \&SVN::Simple::Edit::build_missing
58              
59             Always build parents if you don't open them explicitly.
60              
61             =item \&SVN::Simple::Edit::open_missing
62              
63             Always open the parents if you don't create them explicitly.
64              
65             =item SVN::Simple::Edit::check_missing ([$root])
66              
67             Check if the path exists on $root. Open it if so, otherwise create it.
68              
69             =back
70              
71             =item root
72              
73             The default root to use by SVN::Simple::Edit::check_missing.
74              
75             =item base_path
76              
77             The base path the edit object is created to send delta editor calls.
78              
79             =item noclose
80              
81             Do not close files or directories. This might make non-sorted
82             operations on directories/files work.
83              
84             =back
85              
86             =head1 METHODS
87              
88             Note: Don't expect all editors will work with operations not sorted in
89             DFS order.
90              
91             =over
92              
93             =item open_root ($base_rev)
94              
95             =item add_directory ($path)
96              
97             =item open_directory ($path)
98              
99             =item copy_directory ($path, $from, $fromrev)
100              
101             =item add_file ($path)
102              
103             =item open_file ($path)
104              
105             =item copy_file ($path, $from, $fromrev)
106              
107             =item delete_entry ($path)
108              
109             =item change_dir_prop ($path, $propname, $propvalue)
110              
111             =item change_file_prop ($path, $propname, $propvalue)
112              
113             =item close_edit ()
114              
115             =back
116              
117             =cut
118              
119             require File::Spec::Unix;
120              
121             sub splitpath { File::Spec::Unix->splitpath(@_) };
122             sub canonpath { File::Spec::Unix->canonpath(@_) };
123              
124             sub build_missing {
125             my ($self, $path) = @_;
126             $self->add_directory ($path);
127             }
128              
129             sub open_missing {
130             my ($self, $path) = @_;
131             $self->open_directory ($path);
132             }
133              
134             sub check_missing {
135             my ($root) = @_;
136             return sub {
137             my ($self, $path) = @_;
138             $root ||= $self->{root};
139             $root->check_path (($self->{base_path} || '')."/$path") == $SVN::Node::none ?
140             $self->add_directory ($path) : $self->open_directory($path);
141             }
142             }
143              
144             sub new {
145             my $class = shift;
146             my $self = $class->SUPER::new(@_);
147             $self->{BATON} = {};
148             $self->{missing_handler} ||= \&build_missing;
149             return $self;
150             }
151              
152             sub set_target_revision {
153             my ($self, $target_revision) = @_;
154             $self->SUPER::set_target_revision ($target_revision);
155             }
156              
157             sub _rev_from_root {
158             my ($self, $path) = @_;
159             $path = "/$path" if $path;
160             $path ||= '';
161             return $self->{root}->node_created_rev($self->{base_path}.$path);
162             }
163              
164             sub open_root {
165             my ($self, $base_revision) = @_;
166             $base_revision ||= $self->_rev_from_root () if $self->{root};
167             $self->{BASE} = $base_revision;
168             $self->{BATON}{''} = $self->SUPER::open_root
169             ($base_revision, ${$self->{pool}});
170             }
171              
172             sub find_pbaton {
173             my ($self, $path, $missing_handler) = @_;
174             use Carp;
175             return $self->{BATON}{''} unless $path;
176             my (undef, $dir, undef) = splitpath($path);
177             $dir = canonpath ($dir);
178              
179              
180             return $self->{BATON}{$dir} if exists $self->{BATON}{$dir};
181              
182             $missing_handler ||= $self->{missing_handler};
183             die "unable to get baton for directory $dir"
184             unless $missing_handler;
185              
186             my $pbaton = &$missing_handler ($self, $dir);
187              
188             return $pbaton;
189             }
190              
191             sub close_other_baton {
192             my ($self, $path) = @_;
193             return if $self->{noclose};
194             my (undef, $dir, undef) = splitpath($path);
195             $dir = canonpath ($dir);
196              
197             for (reverse sort grep { !$dir || substr ($_, 0, length ($dir)+1) eq "$dir/"}
198             keys %{$self->{BATON}}) {
199             next unless $path;
200             my $baton = $self->{BATON}{$path};
201             if ($self->{FILES}{$path}) {
202             $self->SUPER::close_file ($baton, undef, $self->{pool});
203             }
204             else {
205             $self->SUPER::close_directory ($baton, $self->{pool});
206             }
207             delete $self->{FILES}{$path};
208             delete $self->{BATON}{$path};
209             }
210             }
211              
212             sub open_directory {
213             my ($self, $path, $pbaton) = @_;
214             $path =~ s|^/||;
215             $self->close_other_baton ($path);
216             $pbaton ||= $self->find_pbaton ($path);
217             my $base_revision = $self->_rev_from_root ($path) if $self->{root};
218             $base_revision ||= $self->{BASE};
219             $self->{BATON}{$path} = $self->SUPER::open_directory ($path, $pbaton,
220             $base_revision,
221             $self->{pool});
222             }
223              
224             sub add_directory {
225             my ($self, $path, $pbaton) = @_;
226             $path =~ s|^/||;
227             $self->close_other_baton ($path);
228             $pbaton ||= $self->find_pbaton ($path);
229             $self->{BATON}{$path} = $self->SUPER::add_directory ($path, $pbaton, undef,
230             -1, $self->{pool});
231             }
232              
233             sub copy_directory {
234             my ($self, $path, $from, $fromrev, $pbaton) = @_;
235             $path =~ s|^/||;
236             $pbaton ||= $self->find_pbaton ($path);
237             $self->{BATON}{$path} = $self->SUPER::add_directory ($path, $pbaton, $from,
238             $fromrev,
239             $self->{pool});
240             }
241              
242             sub open_file {
243             my ($self, $path, $pbaton) = @_;
244             $path =~ s|^/||;
245             $self->close_other_baton ($path);
246             $pbaton ||= $self->find_pbaton ($path);
247             my $base_revision = $self->_rev_from_root ($path) if $self->{root};
248             $base_revision ||= $self->{BASE};
249             $self->{FILES}{$path} = 1;
250             $self->{BATON}{$path} = $self->SUPER::open_file ($path, $pbaton,
251             $base_revision,
252             $self->{pool});
253             }
254              
255             sub add_file {
256             my ($self, $path, $pbaton) = @_;
257             $path =~ s|^/||;
258             $self->close_other_baton ($path);
259             $pbaton ||= $self->find_pbaton ($path);
260             $self->{FILES}{$path} = 1;
261             $self->{BATON}{$path} = $self->SUPER::add_file ($path, $pbaton, undef, -1,
262             $self->{pool});
263             }
264              
265             sub copy_file {
266             my ($self, $path, $from, $fromrev, $pbaton) = @_;
267             $path =~ s|^/||;
268             $pbaton ||= $self->find_pbaton ($path);
269             $self->{BATON}{$path} = $self->SUPER::add_file ($path, $pbaton, $from,
270             $fromrev, $self->{pool});
271             }
272              
273             sub modify_file {
274             my ($self, $path, $content, $targetchecksum) = @_;
275             $path =~ s|^/|| unless ref($path);
276             my $baton = ref($path) ? $path :
277             ($self->{BATON}{$path} || $self->open_file ($path));
278             my $ret = $self->apply_textdelta ($baton, undef, $self->{pool});
279              
280             return unless $ret && $ret->[0];
281              
282             if (ref($content) && $content->isa ('GLOB')) {
283             my $md5 = SVN::TxDelta::send_stream ($content,
284             @$ret,
285             $self->{pool});
286             die "checksum mistach ($md5) vs ($targetchecksum)" if $targetchecksum
287             && $targetchecksum ne $md5;
288             }
289             else {
290             SVN::_Delta::svn_txdelta_send_string ($content, @$ret, $self->{pool});
291             }
292             }
293              
294             sub delete_entry {
295             my ($self, $path, $pbaton) = @_;
296             my $base_revision;
297             $path =~ s|^/||;
298             $pbaton ||= $self->find_pbaton ($path, \&open_missing);
299              
300             $base_revision = $self->_rev_from_root ($path) if $self->{root};
301             $base_revision ||= $self->{BASE};
302             $self->SUPER::delete_entry ($path, $base_revision, $pbaton, $self->{pool});
303             }
304              
305             sub change_file_prop {
306             my ($self, $path, $key, $value) = @_;
307             $path =~ s|^/|| unless ref($path);
308             my $baton = ref($path) ? $path :
309             ($self->{BATON}{$path} || $self->open_file ($path));
310             $self->SUPER::change_file_prop ($baton, $key, $value, $self->{pool});
311             }
312              
313             sub change_dir_prop {
314             my ($self, $path, $key, $value) = @_;
315             $path =~ s|^/|| unless ref($path);
316             my $baton = ref($path) ? $path :
317             ($self->{BATON}{$path} || $self->open_directory ($path));
318             $self->SUPER::change_dir_prop ($baton, $key, $value, $self->{pool});
319             }
320              
321             sub close_file {
322             my ($self, $path, $checksum) = @_;
323             my $baton = $self->{BATON}{$path} or die "not opened";
324             delete $self->{BATON}{$path};
325             $self->SUPER::close_file ($baton, $checksum, $self->{pool});
326             }
327              
328             sub close_directory {
329             my ($self, $path) = @_;
330             my $baton = $self->{BATON}{$path} or die "not opened";
331             delete $self->{BATON}{$path};
332             $self->SUPER::close_directory ($baton, $self->{pool});
333             }
334              
335             sub close_edit {
336             my ($self) = @_;
337             $self->close_other_baton ('');
338             $self->SUPER::close_edit ($self->{pool});
339             }
340              
341             sub abort_edit {
342             my ($self) = @_;
343              
344             $self->SUPER::abort_edit ($self->{pool});
345             }
346              
347             =head1 AUTHORS
348              
349             Chia-liang Kao Eclkao@clkao.orgE
350              
351             =head1 COPYRIGHT
352              
353             Copyright 2003-2004 by Chia-liang Kao Eclkao@clkao.orgE.
354              
355             This program is free software; you can redistribute it and/or modify it
356             under the same terms as Perl itself.
357              
358             See L
359              
360             =cut
361             1;