]> git.ipfire.org Git - thirdparty/git.git/blobdiff - git-svn.perl
git-svn: avoid crashing svnserve when creating new directories
[thirdparty/git.git] / git-svn.perl
index d74e6d3112eb6ad444df35a1dae1fc4fab2afb00..b87dedc99c511213c5bbda916ba583e60e76096a 100755 (executable)
@@ -80,6 +80,7 @@ my %icv;
 my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared,
                   'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags,
                   'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix,
+                  'minimize-url|m' => \$Git::SVN::_minimize_url,
                  'no-metadata' => sub { $icv{noMetadata} = 1 },
                  'use-svm-props' => sub { $icv{useSvmProps} = 1 },
                  'use-svnsync-props' => sub { $icv{useSvnsyncProps} = 1 },
@@ -820,7 +821,7 @@ use strict;
 use warnings;
 use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags $_use_svm_props $_head
-            $_use_svnsync_props $no_reuse_existing/;
+            $_use_svnsync_props $no_reuse_existing $_minimize_url/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
@@ -1037,7 +1038,7 @@ sub init_remote_config {
                                     "[svn-remote \"$existing\"]\n";
                }
                $self->{repo_id} = $existing;
-       } else {
+       } elsif ($_minimize_url) {
                my $min_url = Git::SVN::Ra->new($url)->minimize_url;
                $existing = find_existing_remote($min_url, $r);
                if ($existing) {
@@ -1390,7 +1391,7 @@ sub traverse_ignore {
                }
        }
        foreach (sort keys %$dirent) {
-               next if $dirent->{$_}->kind != $SVN::Node::dir;
+               next if $dirent->{$_}->{kind} != $SVN::Node::dir;
                $self->traverse_ignore($fh, "$path/$_", $r);
        }
 }
@@ -2840,8 +2841,10 @@ sub close_edit {
        my ($self) = @_;
        my ($p,$bat) = ($self->{pool}, $self->{bat});
        foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
+               next if $_ eq '';
                $self->close_directory($bat->{$_}, $p);
        }
+       $self->close_directory($bat->{''}, $p);
        $self->SUPER::close_edit($p);
        $p->clear;
 }
@@ -2888,7 +2891,7 @@ my ($can_do_switch, %ignored_err, $RA);
 BEGIN {
        # enforce temporary pool usage for some simple functions
        my $e;
-       foreach (qw/get_latest_revnum get_uuid get_repos_root/) {
+       foreach (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) {
                $e .= "sub $_ {
                        my \$self = shift;
                        my \$pool = SVN::Pool->new;
@@ -2897,36 +2900,13 @@ BEGIN {
                        wantarray ? \@ret : \$ret[0]; }\n";
        }
 
-       # get_dir needs $pool held in cache for dirents to work,
-       # check_path is cacheable and rev_proplist is close enough
-       # for our purposes.
-       foreach (qw/check_path get_dir rev_proplist/) {
-               $e .= "my \%${_}_cache; my \$${_}_rev = 0; sub $_ {
-                       my \$self = shift;
-                       my \$r = pop;
-                       my \$k = join(\"\\0\", \@_);
-                       if (my \$x = \$${_}_cache{\$r}->{\$k}) {
-                               return wantarray ? \@\$x : \$x->[0];
-                       }
-                       my \$pool = SVN::Pool->new;
-                       my \@ret = \$self->SUPER::$_(\@_, \$r, \$pool);
-                       if (\$r != \$${_}_rev) {
-                               \%${_}_cache = ( pool => [] );
-                               \$${_}_rev = \$r;
-                       }
-                       \$${_}_cache{\$r}->{\$k} = \\\@ret;
-                       push \@{\$${_}_cache{pool}}, \$pool;
-                       wantarray ? \@ret : \$ret[0]; }\n";
-       }
-       $e .= "\n1;";
-       eval $e or die $@;
+       eval "$e; 1;" or die $@;
 }
 
 sub new {
        my ($class, $url) = @_;
        $url =~ s!/+$!!;
        return $RA if ($RA && $RA->{url} eq $url);
-       $RA->{pool}->clear if $RA;
 
        SVN::_Core::svn_config_ensure($config_dir, undef);
        my ($baton, $callbacks) = SVN::Core::auth_open_helper([
@@ -2952,9 +2932,47 @@ sub new {
        $self->{svn_path} = $url;
        $self->{repos_root} = $self->get_repos_root;
        $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
+       $self->{cache} = { check_path => { r => 0, data => {} },
+                          get_dir => { r => 0, data => {} } };
        $RA = bless $self, $class;
 }
 
+sub check_path {
+       my ($self, $path, $r) = @_;
+       my $cache = $self->{cache}->{check_path};
+       if ($r == $cache->{r} && exists $cache->{data}->{$path}) {
+               return $cache->{data}->{$path};
+       }
+       my $pool = SVN::Pool->new;
+       my $t = $self->SUPER::check_path($path, $r, $pool);
+       $pool->clear;
+       if ($r != $cache->{r}) {
+               %{$cache->{data}} = ();
+               $cache->{r} = $r;
+       }
+       $cache->{data}->{$path} = $t;
+}
+
+sub get_dir {
+       my ($self, $dir, $r) = @_;
+       my $cache = $self->{cache}->{get_dir};
+       if ($r == $cache->{r}) {
+               if (my $x = $cache->{data}->{$dir}) {
+                       return wantarray ? @$x : $x->[0];
+               }
+       }
+       my $pool = SVN::Pool->new;
+       my ($d, undef, $props) = $self->SUPER::get_dir($dir, $r, $pool);
+       my %dirents = map { $_ => { kind => $d->{$_}->kind } } keys %$d;
+       $pool->clear;
+       if ($r != $cache->{r}) {
+               %{$cache->{data}} = ();
+               $cache->{r} = $r;
+       }
+       $cache->{data}->{$dir} = [ \%dirents, $r, $props ];
+       wantarray ? (\%dirents, $r, $props) : \%dirents;
+}
+
 sub DESTROY {
        # do not call the real DESTROY since we store ourselves in $RA
 }
@@ -3169,7 +3187,7 @@ sub match_globs {
                return unless scalar @x == 3;
                my $dirents = $x[0];
                foreach my $de (keys %$dirents) {
-                       next if $dirents->{$de}->kind != $SVN::Node::dir;
+                       next if $dirents->{$de}->{kind} != $SVN::Node::dir;
                        my $p = $g->{path}->full_path($de);
                        next if $exists->{$p};
                        next if (length $g->{path}->{right} &&