~ubuntu-branches/debian/sid/git/sid

« back to all changes in this revision

Viewing changes to perl/Git/SVN/Editor.pm

  • Committer: Package Import Robot
  • Author(s): Jonathan Nieder
  • Date: 2013-06-12 07:50:53 UTC
  • mfrom: (1.2.19) (2.1.31 experimental)
  • Revision ID: package-import@ubuntu.com-20130612075053-uue9xe0dq0rvm44y
Tags: 1:1.8.3.1-1
* merge branch debian-experimental
* new upstream point release (see RelNotes/1.8.3.1.txt).
* debian/watch: use xz-compressed tarballs from kernel.org.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package Git::SVN::Editor;
 
2
use vars qw/@ISA $_rmdir $_cp_similarity $_find_copies_harder $_rename_limit/;
 
3
use strict;
 
4
use warnings;
 
5
use SVN::Core;
 
6
use SVN::Delta;
 
7
use Carp qw/croak/;
 
8
use IO::File;
 
9
use Git qw/command command_oneline command_noisy command_output_pipe
 
10
           command_input_pipe command_close_pipe
 
11
           command_bidi_pipe command_close_bidi_pipe/;
 
12
BEGIN {
 
13
        @ISA = qw(SVN::Delta::Editor);
 
14
}
 
15
 
 
16
sub new {
 
17
        my ($class, $opts) = @_;
 
18
        foreach (qw/svn_path r ra tree_a tree_b log editor_cb/) {
 
19
                die "$_ required!\n" unless (defined $opts->{$_});
 
20
        }
 
21
 
 
22
        my $pool = SVN::Pool->new;
 
23
        my $mods = generate_diff($opts->{tree_a}, $opts->{tree_b});
 
24
        my $types = check_diff_paths($opts->{ra}, $opts->{svn_path},
 
25
                                     $opts->{r}, $mods);
 
26
 
 
27
        # $opts->{ra} functions should not be used after this:
 
28
        my @ce  = $opts->{ra}->get_commit_editor($opts->{log},
 
29
                                                $opts->{editor_cb}, $pool);
 
30
        my $self = SVN::Delta::Editor->new(@ce, $pool);
 
31
        bless $self, $class;
 
32
        foreach (qw/svn_path r tree_a tree_b/) {
 
33
                $self->{$_} = $opts->{$_};
 
34
        }
 
35
        $self->{url} = $opts->{ra}->{url};
 
36
        $self->{mods} = $mods;
 
37
        $self->{types} = $types;
 
38
        $self->{pool} = $pool;
 
39
        $self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) };
 
40
        $self->{rm} = { };
 
41
        $self->{path_prefix} = length $self->{svn_path} ?
 
42
                               "$self->{svn_path}/" : '';
 
43
        $self->{config} = $opts->{config};
 
44
        $self->{mergeinfo} = $opts->{mergeinfo};
 
45
        return $self;
 
46
}
 
47
 
 
48
sub generate_diff {
 
49
        my ($tree_a, $tree_b) = @_;
 
50
        my @diff_tree = qw(diff-tree -z -r);
 
51
        if ($_cp_similarity) {
 
52
                push @diff_tree, "-C$_cp_similarity";
 
53
        } else {
 
54
                push @diff_tree, '-C';
 
55
        }
 
56
        push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
 
57
        push @diff_tree, "-l$_rename_limit" if defined $_rename_limit;
 
58
        push @diff_tree, $tree_a, $tree_b;
 
59
        my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
 
60
        local $/ = "\0";
 
61
        my $state = 'meta';
 
62
        my @mods;
 
63
        while (<$diff_fh>) {
 
64
                chomp $_; # this gets rid of the trailing "\0"
 
65
                if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
 
66
                                        ($::sha1)\s($::sha1)\s
 
67
                                        ([MTCRAD])\d*$/xo) {
 
68
                        push @mods, {   mode_a => $1, mode_b => $2,
 
69
                                        sha1_a => $3, sha1_b => $4,
 
70
                                        chg => $5 };
 
71
                        if ($5 =~ /^(?:C|R)$/) {
 
72
                                $state = 'file_a';
 
73
                        } else {
 
74
                                $state = 'file_b';
 
75
                        }
 
76
                } elsif ($state eq 'file_a') {
 
77
                        my $x = $mods[$#mods] or croak "Empty array\n";
 
78
                        if ($x->{chg} !~ /^(?:C|R)$/) {
 
79
                                croak "Error parsing $_, $x->{chg}\n";
 
80
                        }
 
81
                        $x->{file_a} = $_;
 
82
                        $state = 'file_b';
 
83
                } elsif ($state eq 'file_b') {
 
84
                        my $x = $mods[$#mods] or croak "Empty array\n";
 
85
                        if (exists $x->{file_a} && $x->{chg} !~ /^(?:C|R)$/) {
 
86
                                croak "Error parsing $_, $x->{chg}\n";
 
87
                        }
 
88
                        if (!exists $x->{file_a} && $x->{chg} =~ /^(?:C|R)$/) {
 
89
                                croak "Error parsing $_, $x->{chg}\n";
 
90
                        }
 
91
                        $x->{file_b} = $_;
 
92
                        $state = 'meta';
 
93
                } else {
 
94
                        croak "Error parsing $_\n";
 
95
                }
 
96
        }
 
97
        command_close_pipe($diff_fh, $ctx);
 
98
        \@mods;
 
99
}
 
100
 
 
101
sub check_diff_paths {
 
102
        my ($ra, $pfx, $rev, $mods) = @_;
 
103
        my %types;
 
104
        $pfx .= '/' if length $pfx;
 
105
 
 
106
        sub type_diff_paths {
 
107
                my ($ra, $types, $path, $rev) = @_;
 
108
                my @p = split m#/+#, $path;
 
109
                my $c = shift @p;
 
110
                unless (defined $types->{$c}) {
 
111
                        $types->{$c} = $ra->check_path($c, $rev);
 
112
                }
 
113
                while (@p) {
 
114
                        $c .= '/' . shift @p;
 
115
                        next if defined $types->{$c};
 
116
                        $types->{$c} = $ra->check_path($c, $rev);
 
117
                }
 
118
        }
 
119
 
 
120
        foreach my $m (@$mods) {
 
121
                foreach my $f (qw/file_a file_b/) {
 
122
                        next unless defined $m->{$f};
 
123
                        my ($dir) = ($m->{$f} =~ m#^(.*?)/?(?:[^/]+)$#);
 
124
                        if (length $pfx.$dir && ! defined $types{$dir}) {
 
125
                                type_diff_paths($ra, \%types, $pfx.$dir, $rev);
 
126
                        }
 
127
                }
 
128
        }
 
129
        \%types;
 
130
}
 
131
 
 
132
sub split_path {
 
133
        return ($_[0] =~ m#^(.*?)/?([^/]+)$#);
 
134
}
 
135
 
 
136
sub repo_path {
 
137
        my ($self, $path) = @_;
 
138
        if (my $enc = $self->{pathnameencoding}) {
 
139
                require Encode;
 
140
                Encode::from_to($path, $enc, 'UTF-8');
 
141
        }
 
142
        $self->{path_prefix}.(defined $path ? $path : '');
 
143
}
 
144
 
 
145
sub url_path {
 
146
        my ($self, $path) = @_;
 
147
        if ($self->{url} =~ m#^https?://#) {
 
148
                # characters are taken from subversion/libsvn_subr/path.c
 
149
                $path =~ s#([^~a-zA-Z0-9_./!$&'()*+,-])#sprintf("%%%02X",ord($1))#eg;
 
150
        }
 
151
        $self->{url} . '/' . $self->repo_path($path);
 
152
}
 
153
 
 
154
sub rmdirs {
 
155
        my ($self) = @_;
 
156
        my $rm = $self->{rm};
 
157
        delete $rm->{''}; # we never delete the url we're tracking
 
158
        return unless %$rm;
 
159
 
 
160
        foreach (keys %$rm) {
 
161
                my @d = split m#/#, $_;
 
162
                my $c = shift @d;
 
163
                $rm->{$c} = 1;
 
164
                while (@d) {
 
165
                        $c .= '/' . shift @d;
 
166
                        $rm->{$c} = 1;
 
167
                }
 
168
        }
 
169
        delete $rm->{$self->{svn_path}};
 
170
        delete $rm->{''}; # we never delete the url we're tracking
 
171
        return unless %$rm;
 
172
 
 
173
        my ($fh, $ctx) = command_output_pipe(qw/ls-tree --name-only -r -z/,
 
174
                                             $self->{tree_b});
 
175
        local $/ = "\0";
 
176
        while (<$fh>) {
 
177
                chomp;
 
178
                my @dn = split m#/#, $_;
 
179
                while (pop @dn) {
 
180
                        delete $rm->{join '/', @dn};
 
181
                }
 
182
                unless (%$rm) {
 
183
                        close $fh;
 
184
                        return;
 
185
                }
 
186
        }
 
187
        command_close_pipe($fh, $ctx);
 
188
 
 
189
        my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat});
 
190
        foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {
 
191
                $self->close_directory($bat->{$d}, $p);
 
192
                my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#);
 
193
                print "\tD+\t$d/\n" unless $::_q;
 
194
                $self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p);
 
195
                delete $bat->{$d};
 
196
        }
 
197
}
 
198
 
 
199
sub open_or_add_dir {
 
200
        my ($self, $full_path, $baton, $deletions) = @_;
 
201
        my $t = $self->{types}->{$full_path};
 
202
        if (!defined $t) {
 
203
                die "$full_path not known in r$self->{r} or we have a bug!\n";
 
204
        }
 
205
        {
 
206
                no warnings 'once';
 
207
                # SVN::Node::none and SVN::Node::file are used only once,
 
208
                # so we're shutting up Perl's warnings about them.
 
209
                if ($t == $SVN::Node::none || defined($deletions->{$full_path})) {
 
210
                        return $self->add_directory($full_path, $baton,
 
211
                            undef, -1, $self->{pool});
 
212
                } elsif ($t == $SVN::Node::dir) {
 
213
                        return $self->open_directory($full_path, $baton,
 
214
                            $self->{r}, $self->{pool});
 
215
                } # no warnings 'once'
 
216
                print STDERR "$full_path already exists in repository at ",
 
217
                    "r$self->{r} and it is not a directory (",
 
218
                    ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n";
 
219
        } # no warnings 'once'
 
220
        exit 1;
 
221
}
 
222
 
 
223
sub ensure_path {
 
224
        my ($self, $path, $deletions) = @_;
 
225
        my $bat = $self->{bat};
 
226
        my $repo_path = $self->repo_path($path);
 
227
        return $bat->{''} unless (length $repo_path);
 
228
 
 
229
        my @p = split m#/+#, $repo_path;
 
230
        my $c = shift @p;
 
231
        $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''}, $deletions);
 
232
        while (@p) {
 
233
                my $c0 = $c;
 
234
                $c .= '/' . shift @p;
 
235
                $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0}, $deletions);
 
236
        }
 
237
        return $bat->{$c};
 
238
}
 
239
 
 
240
# Subroutine to convert a globbing pattern to a regular expression.
 
241
# From perl cookbook.
 
242
sub glob2pat {
 
243
        my $globstr = shift;
 
244
        my %patmap = ('*' => '.*', '?' => '.', '[' => '[', ']' => ']');
 
245
        $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
 
246
        return '^' . $globstr . '$';
 
247
}
 
248
 
 
249
sub check_autoprop {
 
250
        my ($self, $pattern, $properties, $file, $fbat) = @_;
 
251
        # Convert the globbing pattern to a regular expression.
 
252
        my $regex = glob2pat($pattern);
 
253
        # Check if the pattern matches the file name.
 
254
        if($file =~ m/($regex)/) {
 
255
                # Parse the list of properties to set.
 
256
                my @props = split(/;/, $properties);
 
257
                foreach my $prop (@props) {
 
258
                        # Parse 'name=value' syntax and set the property.
 
259
                        if ($prop =~ /([^=]+)=(.*)/) {
 
260
                                my ($n,$v) = ($1,$2);
 
261
                                for ($n, $v) {
 
262
                                        s/^\s+//; s/\s+$//;
 
263
                                }
 
264
                                $self->change_file_prop($fbat, $n, $v);
 
265
                        }
 
266
                }
 
267
        }
 
268
}
 
269
 
 
270
sub apply_autoprops {
 
271
        my ($self, $file, $fbat) = @_;
 
272
        my $conf_t = ${$self->{config}}{'config'};
 
273
        no warnings 'once';
 
274
        # Check [miscellany]/enable-auto-props in svn configuration.
 
275
        if (SVN::_Core::svn_config_get_bool(
 
276
                $conf_t,
 
277
                $SVN::_Core::SVN_CONFIG_SECTION_MISCELLANY,
 
278
                $SVN::_Core::SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS,
 
279
                0)) {
 
280
                # Auto-props are enabled.  Enumerate them to look for matches.
 
281
                my $callback = sub {
 
282
                        $self->check_autoprop($_[0], $_[1], $file, $fbat);
 
283
                };
 
284
                SVN::_Core::svn_config_enumerate(
 
285
                        $conf_t,
 
286
                        $SVN::_Core::SVN_CONFIG_SECTION_AUTO_PROPS,
 
287
                        $callback);
 
288
        }
 
289
}
 
290
 
 
291
sub A {
 
292
        my ($self, $m, $deletions) = @_;
 
293
        my ($dir, $file) = split_path($m->{file_b});
 
294
        my $pbat = $self->ensure_path($dir, $deletions);
 
295
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
 
296
                                        undef, -1);
 
297
        print "\tA\t$m->{file_b}\n" unless $::_q;
 
298
        $self->apply_autoprops($file, $fbat);
 
299
        $self->chg_file($fbat, $m);
 
300
        $self->close_file($fbat,undef,$self->{pool});
 
301
}
 
302
 
 
303
sub C {
 
304
        my ($self, $m, $deletions) = @_;
 
305
        my ($dir, $file) = split_path($m->{file_b});
 
306
        my $pbat = $self->ensure_path($dir, $deletions);
 
307
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
 
308
                                $self->url_path($m->{file_a}), $self->{r});
 
309
        print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
 
310
        $self->chg_file($fbat, $m);
 
311
        $self->close_file($fbat,undef,$self->{pool});
 
312
}
 
313
 
 
314
sub delete_entry {
 
315
        my ($self, $path, $pbat) = @_;
 
316
        my $rpath = $self->repo_path($path);
 
317
        my ($dir, $file) = split_path($rpath);
 
318
        $self->{rm}->{$dir} = 1;
 
319
        $self->SUPER::delete_entry($rpath, $self->{r}, $pbat, $self->{pool});
 
320
}
 
321
 
 
322
sub R {
 
323
        my ($self, $m, $deletions) = @_;
 
324
        my ($dir, $file) = split_path($m->{file_b});
 
325
        my $pbat = $self->ensure_path($dir, $deletions);
 
326
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
 
327
                                $self->url_path($m->{file_a}), $self->{r});
 
328
        print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
 
329
        $self->apply_autoprops($file, $fbat);
 
330
        $self->chg_file($fbat, $m);
 
331
        $self->close_file($fbat,undef,$self->{pool});
 
332
 
 
333
        ($dir, $file) = split_path($m->{file_a});
 
334
        $pbat = $self->ensure_path($dir, $deletions);
 
335
        $self->delete_entry($m->{file_a}, $pbat);
 
336
}
 
337
 
 
338
sub M {
 
339
        my ($self, $m, $deletions) = @_;
 
340
        my ($dir, $file) = split_path($m->{file_b});
 
341
        my $pbat = $self->ensure_path($dir, $deletions);
 
342
        my $fbat = $self->open_file($self->repo_path($m->{file_b}),
 
343
                                $pbat,$self->{r},$self->{pool});
 
344
        print "\t$m->{chg}\t$m->{file_b}\n" unless $::_q;
 
345
        $self->chg_file($fbat, $m);
 
346
        $self->close_file($fbat,undef,$self->{pool});
 
347
}
 
348
 
 
349
sub T {
 
350
        my ($self, $m, $deletions) = @_;
 
351
 
 
352
        # Work around subversion issue 4091: toggling the "is a
 
353
        # symlink" property requires removing and re-adding a
 
354
        # file or else "svn up" on affected clients trips an
 
355
        # assertion and aborts.
 
356
        if (($m->{mode_b} =~ /^120/ && $m->{mode_a} !~ /^120/) ||
 
357
            ($m->{mode_b} !~ /^120/ && $m->{mode_a} =~ /^120/)) {
 
358
                $self->D({
 
359
                        mode_a => $m->{mode_a}, mode_b => '000000',
 
360
                        sha1_a => $m->{sha1_a}, sha1_b => '0' x 40,
 
361
                        chg => 'D', file_b => $m->{file_b}
 
362
                }, $deletions);
 
363
                $self->A({
 
364
                        mode_a => '000000', mode_b => $m->{mode_b},
 
365
                        sha1_a => '0' x 40, sha1_b => $m->{sha1_b},
 
366
                        chg => 'A', file_b => $m->{file_b}
 
367
                }, $deletions);
 
368
                return;
 
369
        }
 
370
 
 
371
        $self->M($m, $deletions);
 
372
}
 
373
 
 
374
sub change_file_prop {
 
375
        my ($self, $fbat, $pname, $pval) = @_;
 
376
        $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
 
377
}
 
378
 
 
379
sub change_dir_prop {
 
380
        my ($self, $pbat, $pname, $pval) = @_;
 
381
        $self->SUPER::change_dir_prop($pbat, $pname, $pval, $self->{pool});
 
382
}
 
383
 
 
384
sub _chg_file_get_blob ($$$$) {
 
385
        my ($self, $fbat, $m, $which) = @_;
 
386
        my $fh = $::_repository->temp_acquire("git_blob_$which");
 
387
        if ($m->{"mode_$which"} =~ /^120/) {
 
388
                print $fh 'link ' or croak $!;
 
389
                $self->change_file_prop($fbat,'svn:special','*');
 
390
        } elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) {
 
391
                $self->change_file_prop($fbat,'svn:special',undef);
 
392
        }
 
393
        my $blob = $m->{"sha1_$which"};
 
394
        return ($fh,) if ($blob =~ /^0{40}$/);
 
395
        my $size = $::_repository->cat_blob($blob, $fh);
 
396
        croak "Failed to read object $blob" if ($size < 0);
 
397
        $fh->flush == 0 or croak $!;
 
398
        seek $fh, 0, 0 or croak $!;
 
399
 
 
400
        my $exp = ::md5sum($fh);
 
401
        seek $fh, 0, 0 or croak $!;
 
402
        return ($fh, $exp);
 
403
}
 
404
 
 
405
sub chg_file {
 
406
        my ($self, $fbat, $m) = @_;
 
407
        if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
 
408
                $self->change_file_prop($fbat,'svn:executable','*');
 
409
        } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
 
410
                $self->change_file_prop($fbat,'svn:executable',undef);
 
411
        }
 
412
        my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a';
 
413
        my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b';
 
414
        my $pool = SVN::Pool->new;
 
415
        my $atd = $self->apply_textdelta($fbat, $exp_a, $pool);
 
416
        if (-s $fh_a) {
 
417
                my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool);
 
418
                my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool);
 
419
                if (defined $res) {
 
420
                        die "Unexpected result from send_txstream: $res\n",
 
421
                            "(SVN::Core::VERSION: $SVN::Core::VERSION)\n";
 
422
                }
 
423
        } else {
 
424
                my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool);
 
425
                die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n"
 
426
                    if ($got ne $exp_b);
 
427
        }
 
428
        Git::temp_release($fh_b, 1);
 
429
        Git::temp_release($fh_a, 1);
 
430
        $pool->clear;
 
431
}
 
432
 
 
433
sub D {
 
434
        my ($self, $m, $deletions) = @_;
 
435
        my ($dir, $file) = split_path($m->{file_b});
 
436
        my $pbat = $self->ensure_path($dir, $deletions);
 
437
        print "\tD\t$m->{file_b}\n" unless $::_q;
 
438
        $self->delete_entry($m->{file_b}, $pbat);
 
439
}
 
440
 
 
441
sub close_edit {
 
442
        my ($self) = @_;
 
443
        my ($p,$bat) = ($self->{pool}, $self->{bat});
 
444
        foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
 
445
                next if $_ eq '';
 
446
                $self->close_directory($bat->{$_}, $p);
 
447
        }
 
448
        $self->close_directory($bat->{''}, $p);
 
449
        $self->SUPER::close_edit($p);
 
450
        $p->clear;
 
451
}
 
452
 
 
453
sub abort_edit {
 
454
        my ($self) = @_;
 
455
        $self->SUPER::abort_edit($self->{pool});
 
456
}
 
457
 
 
458
sub DESTROY {
 
459
        my $self = shift;
 
460
        $self->SUPER::DESTROY(@_);
 
461
        $self->{pool}->clear;
 
462
}
 
463
 
 
464
# this drives the editor
 
465
sub apply_diff {
 
466
        my ($self) = @_;
 
467
        my $mods = $self->{mods};
 
468
        my %o = ( D => 0, C => 1, R => 2, A => 3, M => 4, T => 5 );
 
469
        my %deletions;
 
470
 
 
471
        foreach my $m (@$mods) {
 
472
                if ($m->{chg} eq "D") {
 
473
                        $deletions{$m->{file_b}} = 1;
 
474
                }
 
475
        }
 
476
 
 
477
        foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
 
478
                my $f = $m->{chg};
 
479
                if (defined $o{$f}) {
 
480
                        $self->$f($m, \%deletions);
 
481
                } else {
 
482
                        fatal("Invalid change type: $f");
 
483
                }
 
484
        }
 
485
 
 
486
        if (defined($self->{mergeinfo})) {
 
487
                $self->change_dir_prop($self->{bat}{''}, "svn:mergeinfo",
 
488
                                       $self->{mergeinfo});
 
489
        }
 
490
        $self->rmdirs if $_rmdir;
 
491
        if (@$mods == 0 && !defined($self->{mergeinfo})) {
 
492
                $self->abort_edit;
 
493
        } else {
 
494
                $self->close_edit;
 
495
        }
 
496
        return scalar @$mods;
 
497
}
 
498
 
 
499
1;
 
500
__END__
 
501
 
 
502
=head1 NAME
 
503
 
 
504
Git::SVN::Editor - commit driver for "git svn set-tree" and dcommit
 
505
 
 
506
=head1 SYNOPSIS
 
507
 
 
508
        use Git::SVN::Editor;
 
509
        use Git::SVN::Ra;
 
510
 
 
511
        my $ra = Git::SVN::Ra->new($url);
 
512
        my %opts = (
 
513
                r => 19,
 
514
                log => "log message",
 
515
                ra => $ra,
 
516
                config => SVN::Core::config_get_config($svn_config_dir),
 
517
                tree_a => "$commit^",
 
518
                tree_b => "$commit",
 
519
                editor_cb => sub { print "Committed r$_[0]\n"; },
 
520
                mergeinfo => "/branches/foo:1-10",
 
521
                svn_path => "trunk"
 
522
        );
 
523
        Git::SVN::Editor->new(\%opts)->apply_diff or print "No changes\n";
 
524
 
 
525
        my $re = Git::SVN::Editor::glob2pat("trunk/*");
 
526
        if ($branchname =~ /$re/) {
 
527
                print "matched!\n";
 
528
        }
 
529
 
 
530
=head1 DESCRIPTION
 
531
 
 
532
This module is an implementation detail of the "git svn" command.
 
533
Do not use it unless you are developing git-svn.
 
534
 
 
535
This module adapts the C<SVN::Delta::Editor> object returned by
 
536
C<SVN::Delta::get_commit_editor> and drives it to convey the
 
537
difference between two git tree objects to a remote Subversion
 
538
repository.
 
539
 
 
540
The interface will change as git-svn evolves.
 
541
 
 
542
=head1 DEPENDENCIES
 
543
 
 
544
Subversion perl bindings,
 
545
the core L<Carp> and L<IO::File> modules,
 
546
and git's L<Git> helper module.
 
547
 
 
548
C<Git::SVN::Editor> has not been tested using callers other than
 
549
B<git-svn> itself.
 
550
 
 
551
=head1 SEE ALSO
 
552
 
 
553
L<SVN::Delta>,
 
554
L<Git::SVN::Fetcher>.
 
555
 
 
556
=head1 INCOMPATIBILITIES
 
557
 
 
558
None reported.
 
559
 
 
560
=head1 BUGS
 
561
 
 
562
None.