git-svn: cleanup: put SVN workarounds into their own namespace
[gitweb.git] / git-svn.perl
index fcef05c2eec30a116cb7d7a44d3d5ebf087759a2..5a3a8777097e135ab072000df4d2b844fd913c5b 100755 (executable)
@@ -4,7 +4,7 @@
 use warnings;
 use strict;
 use vars qw/   $AUTHOR $VERSION
-               $SVN_URL $SVN_INFO $SVN_WC $SVN_UUID
+               $SVN_URL
                $GIT_SVN_INDEX $GIT_SVN
                $GIT_DIR $GIT_SVN_DIR $REVDB/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
@@ -38,6 +38,7 @@
 if ($SVN::Core::VERSION lt '1.1.0') {
        fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)\n";
 }
+push @Git::SVN::Ra::ISA, 'SVN::Ra';
 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
 push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
 use Carp qw/croak/;
 my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
        $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote,
        $_repack, $_repack_nr, $_repack_flags, $_q,
-       $_message, $_file, $_follow_parent, $_no_metadata,
+       $_message, $_file, $_no_metadata,
        $_template, $_shared, $_no_default_regex, $_no_graft_copy,
        $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
        $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
        $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
-       $_config_dir,
        $_pager, $_color, $_prefix);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
 my ($_svn_can_do_switch);
 my @repo_path_split_cache;
+use vars qw/$_follow_parent/;
 
 my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
                'branch|b=s' => \@_branch_from,
@@ -83,7 +84,7 @@
                'no-metadata' => \$_no_metadata,
                'quiet|q' => \$_q,
                'username=s' => \$Git::SVN::Prompt::_username,
-               'config-dir=s' => \$_config_dir,
+               'config-dir=s' => \$Git::SVN::Ra::config_dir,
                'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
                'ignore-nodate' => \$_ignore_nodate,
                'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
                        { %multi_opts, %init_opts,
                         'revision|r=i' => \$_revision,
                         'username=s' => \$Git::SVN::Prompt::_username,
-                        'config-dir=s' => \$_config_dir,
+                        'config-dir=s' => \$Git::SVN::Ra::config_dir,
                         'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
                         'prefix=s' => \$_prefix,
                        } ],
@@ -236,6 +237,7 @@ sub rebuild {
        my ($rev_list, $ctx) = command_output_pipe("rev-list",
                                                   "refs/remotes/$GIT_SVN");
        my $latest;
+       my $svn_uuid;
        while (<$rev_list>) {
                chomp;
                my $c = $_;
@@ -251,7 +253,7 @@ sub rebuild {
 
                # if we merged or otherwise started elsewhere, this is
                # how we break out of it
-               next if (defined $SVN_UUID && ($uuid ne $SVN_UUID));
+               next if (defined $svn_uuid && ($uuid ne $svn_uuid));
                next if (defined $SVN_URL && defined $url && ($url ne $SVN_URL));
 
                unless (defined $latest) {
@@ -259,7 +261,7 @@ sub rebuild {
                                croak "SVN repository location required: $url\n";
                        }
                        $SVN_URL ||= $url;
-                       $SVN_UUID ||= $uuid;
+                       $svn_uuid ||= $uuid;
                        setup_git_svn();
                        $latest = $rev;
                }
@@ -310,7 +312,7 @@ sub fetch {
 sub fetch_lib {
        my (@parents) = @_;
        $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
-       $SVN ||= libsvn_connect($SVN_URL);
+       $SVN ||= Git::SVN::Ra->new($SVN_URL);
        my ($last_rev, $last_commit) = svn_grab_base_rev();
        my ($base, $head) = libsvn_parse_revision($last_rev);
        if ($base > $head) {
@@ -322,7 +324,6 @@ sub fetch_lib {
        # after processing a revision and SVN stuff seems to leak
        my $inc = 1000;
        my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc);
-       read_uuid();
        if (defined $last_commit) {
                unless (-e $GIT_SVN_INDEX) {
                        command_noisy('read-tree', $last_commit);
@@ -352,8 +353,7 @@ sub fetch_lib {
                        # performance sucks with it enabled, so it's much
                        # faster to fetch revision ranges instead of relying
                        # on the limiter.
-                       libsvn_get_log(libsvn_dup_ra($SVN), [''],
-                                       $min, $max, 0, 1, 1,
+                       $SVN->dup->get_log([''], $min, $max, 0, 1, 1,
                                sub {
                                        my $log_msg;
                                        if ($last_commit) {
@@ -378,7 +378,7 @@ sub fetch_lib {
                $min = $max + 1;
                $max += $inc;
                $max = $head if ($max > $head);
-               $SVN = libsvn_connect($SVN_URL);
+               $SVN = Git::SVN::Ra->new($SVN_URL);
        }
        restore_index($index);
        return { revision => $last_rev, commit => $last_commit };
@@ -424,8 +424,6 @@ sub commit_lib {
                                " current: $fetched->{revision}\n";
                exit 1;
        }
-       read_uuid();
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
        my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
 
        my $repo;
@@ -437,9 +435,10 @@ sub commit_lib {
                # can't track down... (it's probably in the SVN code)
                defined(my $pid = open my $fh, '-|') or croak $!;
                if (!$pid) {
+                       my $pool = SVN::Pool->new;
                        my $ed = SVN::Git::Editor->new(
                                        {       r => $r_last,
-                                               ra => libsvn_dup_ra($SVN),
+                                               ra => $SVN->dup,
                                                c => $c,
                                                svn_path => $SVN->{svn_path},
                                        },
@@ -451,8 +450,7 @@ sub commit_lib {
                                                                $log_msg->{msg},
                                                                $r_last,
                                                                $cmt_last)
-                                               },
-                                               @lock)
+                                               }, $pool)
                                        );
                        my $mods = libsvn_checkout_tree($cmt_last, $c, $ed);
                        if (@$mods == 0) {
@@ -461,6 +459,7 @@ sub commit_lib {
                        } else {
                                $ed->close_edit;
                        }
+                       $pool->clear;
                        exit 0;
                }
                my ($r_new, $cmt_new, $no);
@@ -534,7 +533,7 @@ sub dcommit {
 sub show_ignore {
        $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
        my $repo;
-       $SVN ||= libsvn_connect($SVN_URL);
+       $SVN ||= Git::SVN::Ra->new($SVN_URL);
        my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
        libsvn_traverse_ignore(\*STDOUT, '', $r);
 }
@@ -716,16 +715,16 @@ sub commit_diff {
                $_message ||= get_commit_message($tb,
                                        "$GIT_DIR/.svn-commit.tmp.$$")->{msg};
        }
-       $SVN ||= libsvn_connect($SVN_URL);
+       $SVN ||= Git::SVN::Ra->new($SVN_URL);
        if ($r eq 'HEAD') {
                $r = $SVN->get_latest_revnum;
        } elsif ($r !~ /^\d+$/) {
                die "revision argument: $r not understood by git-svn\n";
        }
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
        my $rev_committed;
+       my $pool = SVN::Pool->new;
        my $ed = SVN::Git::Editor->new({        r => $r,
-                                               ra => libsvn_dup_ra($SVN),
+                                               ra => $SVN->dup,
                                                c => $tb,
                                                svn_path => $SVN->{svn_path}
                                        },
@@ -733,7 +732,8 @@ sub commit_diff {
                                        sub {
                                                $rev_committed = $_[0];
                                                print "Committed $_[0]\n";
-                                       }, @lock)
+                                       },
+                                       $pool)
                                );
        eval {
                my $mods = libsvn_checkout_tree($ta, $tb, $ed);
@@ -744,6 +744,7 @@ sub commit_diff {
                        $ed->close_edit;
                }
        };
+       $pool->clear;
        fatal "$@\n" if $@;
        $_message = $_file = undef;
        return $rev_committed;
@@ -1012,7 +1013,7 @@ sub graft_file_copy_lib {
        my $tree_paths = $l_map->{$u};
        my $pfx = common_prefix([keys %$tree_paths]);
        my ($repo, $path) = repo_path_split($u.$pfx);
-       $SVN = libsvn_connect($repo);
+       $SVN = Git::SVN::Ra->new($repo);
 
        my ($base, $head) = libsvn_parse_revision();
        my $inc = 1000;
@@ -1020,14 +1021,11 @@ sub graft_file_copy_lib {
        my $eh = $SVN::Error::handler;
        $SVN::Error::handler = \&libsvn_skip_unknown_revs;
        while (1) {
-               my $pool = SVN::Pool->new;
-               libsvn_get_log(libsvn_dup_ra($SVN), [$path],
-                              $min, $max, 0, 2, 1,
+               $SVN->dup->get_log([$path], $min, $max, 0, 2, 1,
                        sub {
                                libsvn_graft_file_copies($grafts, $tree_paths,
                                                        $path, @_);
-                       }, $pool);
-               $pool->clear;
+                       });
                last if ($max >= $head);
                $min = $max + 1;
                $max += $inc;
@@ -1095,13 +1093,6 @@ sub graft_merge_msg {
        }
 }
 
-sub read_uuid {
-       return if $SVN_UUID;
-       my $pool = SVN::Pool->new;
-       $SVN_UUID = $SVN->get_uuid($pool);
-       $pool->clear;
-}
-
 sub verify_ref {
        my ($ref) = @_;
        eval { command_oneline([ 'rev-parse', '--verify', $ref ],
@@ -1119,7 +1110,7 @@ sub repo_path_split {
                        return ($u, $full_url);
                }
        }
-       my $tmp = libsvn_connect($full_url);
+       my $tmp = Git::SVN::Ra->new($full_url);
        return ($tmp->{repos_root}, $tmp->{svn_path});
 }
 
@@ -1371,10 +1362,10 @@ sub git_commit {
                        }
                        next if $skip;
                        my ($url_p, $r_p, $uuid_p) = cmt_metadata($p);
-                       next if (($SVN_UUID eq $uuid_p) &&
+                       next if (($SVN->uuid eq $uuid_p) &&
                                                ($log_msg->{revision} > $r_p));
                        next if (defined $url_p && defined $SVN_URL &&
-                                               ($SVN_UUID eq $uuid_p) &&
+                                               ($SVN->uuid eq $uuid_p) &&
                                                ($url_p eq $SVN_URL));
                        push @tmp_parents, $p;
                }
@@ -1394,8 +1385,8 @@ sub git_commit {
                                                                or croak $!;
        print $msg_fh $log_msg->{msg} or croak $!;
        unless ($_no_metadata) {
-               print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_msg->{revision}",
-                                       " $SVN_UUID\n" or croak $!;
+               print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_msg->{revision} ",
+                                       $SVN->uuid,"\n" or croak $!;
        }
        $msg_fh->flush == 0 or croak $!;
        close $msg_fh or croak $!;
@@ -1429,7 +1420,7 @@ sub set_commit_env {
                $author = '(no author)';
        }
        my ($name,$email) = defined $users{$author} ?  @{$users{$author}}
-                               : ($author,"$author\@$SVN_UUID");
+                               : ($author,$author . '@' . $SVN->uuid);
        $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
        $ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email;
        $ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_msg->{date};
@@ -1589,7 +1580,6 @@ sub init_vars {
        $REVDB = "$GIT_SVN_DIR/.rev_db";
        $GIT_SVN_INDEX = "$GIT_SVN_DIR/index";
        $SVN_URL = undef;
-       $SVN_WC = "$GIT_SVN_DIR/tree";
        %tree_map = ();
 }
 
@@ -2042,60 +2032,6 @@ sub _read_password {
 
 package main;
 
-sub libsvn_connect {
-       my ($url) = @_;
-       SVN::_Core::svn_config_ensure($_config_dir, undef);
-       my ($baton, $callbacks) = SVN::Core::auth_open_helper([
-           SVN::Client::get_simple_provider(),
-           SVN::Client::get_ssl_server_trust_file_provider(),
-           SVN::Client::get_simple_prompt_provider(
-             \&Git::SVN::Prompt::simple, 2),
-           SVN::Client::get_ssl_client_cert_prompt_provider(
-             \&Git::SVN::Prompt::ssl_client_cert, 2),
-           SVN::Client::get_ssl_client_cert_pw_prompt_provider(
-             \&Git::SVN::Prompt::ssl_client_cert_pw, 2),
-           SVN::Client::get_username_provider(),
-           SVN::Client::get_ssl_server_trust_prompt_provider(
-             \&Git::SVN::Prompt::ssl_server_trust),
-           SVN::Client::get_username_prompt_provider(
-             \&Git::SVN::username, 2),
-         ]);
-       my $config = SVN::Core::config_get_config($_config_dir);
-       my $ra = SVN::Ra->new(url => $url, auth => $baton,
-                             config => $config,
-                             pool => SVN::Pool->new,
-                             auth_provider_callbacks => $callbacks);
-       $ra->{svn_path} = $url;
-       $ra->{repos_root} = $ra->get_repos_root;
-       $ra->{svn_path} =~ s#^\Q$ra->{repos_root}\E/*##;
-       push @repo_path_split_cache, qr/^(\Q$ra->{repos_root}\E)/;
-       return $ra;
-}
-
-sub libsvn_can_do_switch {
-       unless (defined $_svn_can_do_switch) {
-               my $pool = SVN::Pool->new;
-               my $rep = eval {
-                       $SVN->do_switch(1, '', 0, $SVN->{url},
-                                       SVN::Delta::Editor->new, $pool);
-               };
-               if ($@) {
-                       $_svn_can_do_switch = 0;
-               } else {
-                       $rep->abort_report($pool);
-                       $_svn_can_do_switch = 1;
-               }
-               $pool->clear;
-       }
-       $_svn_can_do_switch;
-}
-
-sub libsvn_dup_ra {
-       my ($ra) = @_;
-       SVN::Ra->new(map { $_ => $ra->{$_} } qw/config url
-                    auth auth_provider_callbacks repos_root svn_path/);
-}
-
 sub uri_encode {
        my ($f) = @_;
        $f =~ s#([^a-zA-Z0-9\*!\:_\./\-])#uc sprintf("%%%02x",ord($1))#eg;
@@ -2165,14 +2101,12 @@ sub libsvn_log_entry {
        }
 
        # revprops (make this optional? it's an extra network trip...)
-       my $pool = SVN::Pool->new;
-       my $rp = $SVN->rev_proplist($rev, $pool);
+       my $rp = $SVN->rev_proplist($rev);
        foreach (sort keys %$rp) {
                next if /^svn:(?:author|date|log)$/;
                print $un "  rev_prop: ", uri_encode($_), ' ',
                          uri_encode($rp->{$_}), "\n";
        }
-       $pool->clear;
        close $un or croak $!;
 
        { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
@@ -2182,15 +2116,9 @@ sub libsvn_log_entry {
 
 sub libsvn_fetch {
        my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
-       my $pool = SVN::Pool->new;
        my $ed = SVN::Git::Fetcher->new({ c => $last_commit, q => $_q });
-       my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
        my (undef, $last_rev, undef) = cmt_metadata($last_commit);
-       $reporter->set_path('', $last_rev, 0, @lock, $pool);
-       $reporter->finish_report($pool);
-       $pool->clear;
-       unless ($ed->{git_commit_ok}) {
+       unless ($SVN->gs_do_update($last_rev, $rev, '', 1, $ed)) {
                die "SVN connection failed somewhere...\n";
        }
        libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ed);
@@ -2250,8 +2178,7 @@ sub libsvn_parse_revision {
 sub libsvn_traverse_ignore {
        my ($fh, $path, $r) = @_;
        $path =~ s#^/+##g;
-       my $pool = SVN::Pool->new;
-       my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
+       my ($dirent, undef, $props) = $SVN->get_dir($path, $r);
        my $p = $path;
        $p =~ s#^\Q$SVN->{svn_path}\E/##;
        print $fh length $p ? "\n# $p\n" : "\n# /\n";
@@ -2270,7 +2197,6 @@ sub libsvn_traverse_ignore {
                next if $dirent->{$_}->kind != $SVN::Node::dir;
                libsvn_traverse_ignore($fh, "$path/$_", $r);
        }
-       $pool->clear;
 }
 
 sub revisions_eq {
@@ -2278,10 +2204,7 @@ sub revisions_eq {
        return 1 if $r0 == $r1;
        my $nr = 0;
        # should be OK to use Pool here (r1 - r0) should be small
-       my $pool = SVN::Pool->new;
-       libsvn_get_log($SVN, [$path], $r0, $r1,
-                       0, 0, 1, sub {$nr++}, $pool);
-       $pool->clear;
+       $SVN->get_log([$path], $r0, $r1, 0, 0, 1, sub {$nr++});
        return 0 if ($nr > 1);
        return 1;
 }
@@ -2337,40 +2260,23 @@ sub libsvn_find_parent_branch {
                unlink $GIT_SVN_INDEX;
                print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
                command_noisy('read-tree', $parent);
-               unless (libsvn_can_do_switch()) {
+               unless ($SVN->can_do_switch) {
                        return _libsvn_new_tree($paths, $rev, $author, $date,
                                                $msg, [$parent]);
                }
                # do_switch works with svn/trunk >= r22312, but that is not
                # included with SVN 1.4.2 (the latest version at the moment),
                # so we can't rely on it.
-               my $ra = libsvn_connect("$url/$branch_from");
+               my $ra = Git::SVN::Ra->new("$url/$branch_from");
                my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q });
-               my $pool = SVN::Pool->new;
-               my $reporter = $ra->do_switch($rev, '', 1, $SVN->{url},
-                                             $ed, $pool);
-               my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
-               $reporter->set_path('', $r0, 0, @lock, $pool);
-               $reporter->finish_report($pool);
-               $pool->clear;
-               unless ($ed->{git_commit_ok}) {
-                       die "SVN connection failed somewhere...\n";
-               }
+               $ra->gs_do_switch($r0, $rev, '', 1, $SVN->{url}, $ed) or
+                                  die "SVN connection failed somewhere...\n";
                return libsvn_log_entry($rev, $author, $date, $msg, [$parent]);
        }
        print STDERR "Nope, branch point not imported or unknown\n";
        return undef;
 }
 
-sub libsvn_get_log {
-       my ($ra, @args) = @_;
-       $args[4]-- if $args[4] && ! $_follow_parent;
-       if ($SVN::Core::VERSION le '1.2.0') {
-               splice(@args, 3, 1);
-       }
-       $ra->get_log(@args);
-}
-
 sub libsvn_new_tree {
        if (my $log_entry = libsvn_find_parent_branch(@_)) {
                return $log_entry;
@@ -2381,14 +2287,8 @@ sub libsvn_new_tree {
 
 sub _libsvn_new_tree {
        my ($paths, $rev, $author, $date, $msg, $parents) = @_;
-       my $pool = SVN::Pool->new;
        my $ed = SVN::Git::Fetcher->new({q => $_q});
-       my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
-       $reporter->set_path('', $rev, 1, @lock, $pool);
-       $reporter->finish_report($pool);
-       $pool->clear;
-       unless ($ed->{git_commit_ok}) {
+       unless ($SVN->gs_do_update($rev, $rev, '', 1, $ed)) {
                die "SVN connection failed somewhere...\n";
        }
        libsvn_log_entry($rev, $author, $date, $msg, $parents, $ed);
@@ -2474,21 +2374,18 @@ sub libsvn_commit_cb {
 
 sub libsvn_ls_fullurl {
        my $fullurl = shift;
-       my $ra = libsvn_connect($fullurl);
+       my $ra = Git::SVN::Ra->new($fullurl);
        my @ret;
-       my $pool = SVN::Pool->new;
        my $r = defined $_revision ? $_revision : $ra->get_latest_revnum;
-       my ($dirent, undef, undef) = $ra->get_dir('', $r, $pool);
+       my ($dirent, undef, undef) = $ra->get_dir('', $r);
        foreach my $d (sort keys %$dirent) {
                if ($dirent->{$d}->kind == $SVN::Node::dir) {
                        push @ret, "$d/"; # add '/' for compat with cli svn
                }
        }
-       $pool->clear;
        return @ret;
 }
 
-
 sub libsvn_skip_unknown_revs {
        my $err = shift;
        my $errno = $err->apr_err();
@@ -2866,9 +2763,7 @@ sub rmdirs {
 
 sub open_or_add_dir {
        my ($self, $full_path, $baton) = @_;
-       my $p = SVN::Pool->new;
-       my $t = $self->{ra}->check_path($full_path, $self->{r}, $p);
-       $p->clear;
+       my $t = $self->{ra}->check_path($full_path, $self->{r});
        if ($t == $SVN::Node::none) {
                return $self->add_directory($full_path, $baton,
                                                undef, -1, $self->{pool});
@@ -3023,6 +2918,135 @@ sub abort_edit {
        $self->{pool}->clear;
 }
 
+package Git::SVN::Ra;
+use vars qw/@ISA $config_dir/;
+use strict;
+use warnings;
+my ($can_do_switch);
+
+BEGIN {
+       # enforce temporary pool usage for some simple functions
+       my $e;
+       foreach (qw/get_latest_revnum rev_proplist get_file
+                   check_path get_dir get_uuid get_repos_root/) {
+               $e .= "sub $_ {
+                       my \$self = shift;
+                       my \$pool = SVN::Pool->new;
+                       my \@ret = \$self->SUPER::$_(\@_,\$pool);
+                       \$pool->clear;
+                       wantarray ? \@ret : \$ret[0]; }\n";
+       }
+       eval $e;
+}
+
+sub new {
+       my ($class, $url) = @_;
+       SVN::_Core::svn_config_ensure($config_dir, undef);
+       my ($baton, $callbacks) = SVN::Core::auth_open_helper([
+           SVN::Client::get_simple_provider(),
+           SVN::Client::get_ssl_server_trust_file_provider(),
+           SVN::Client::get_simple_prompt_provider(
+             \&Git::SVN::Prompt::simple, 2),
+           SVN::Client::get_ssl_client_cert_prompt_provider(
+             \&Git::SVN::Prompt::ssl_client_cert, 2),
+           SVN::Client::get_ssl_client_cert_pw_prompt_provider(
+             \&Git::SVN::Prompt::ssl_client_cert_pw, 2),
+           SVN::Client::get_username_provider(),
+           SVN::Client::get_ssl_server_trust_prompt_provider(
+             \&Git::SVN::Prompt::ssl_server_trust),
+           SVN::Client::get_username_prompt_provider(
+             \&Git::SVN::Prompt::username, 2),
+         ]);
+       my $config = SVN::Core::config_get_config($config_dir);
+       my $self = SVN::Ra->new(url => $url, auth => $baton,
+                             config => $config,
+                             pool => SVN::Pool->new,
+                             auth_provider_callbacks => $callbacks);
+       $self->{svn_path} = $url;
+       $self->{repos_root} = $self->get_repos_root;
+       $self->{svn_path} =~ s#^\Q$self->{repos_root}\E/*##;
+       bless $self, $class;
+}
+
+sub DESTROY {
+       my $self = shift;
+       $self->{pool}->clear if $self->{pool};
+       $self->SUPER::DESTROY(@_);
+}
+
+sub dup {
+       my ($self) = @_;
+       my $dup = SVN::Ra->new(pool => SVN::Pool->new,
+                               map { $_ => $self->{$_} } qw/config url
+                    auth auth_provider_callbacks repos_root svn_path/);
+       bless $dup, ref $self;
+}
+
+sub get_log {
+       my ($self, @args) = @_;
+       my $pool = SVN::Pool->new;
+       $args[4]-- if $args[4] && ! $::_follow_parent;
+       splice(@args, 3, 1) if ($SVN::Core::VERSION le '1.2.0');
+       my $ret = $self->SUPER::get_log(@args, $pool);
+       $pool->clear;
+       $ret;
+}
+
+sub get_commit_editor {
+       my ($self, $msg, $cb, $pool) = @_;
+       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
+       $self->SUPER::get_commit_editor($msg, $cb, @lock, $pool);
+}
+
+sub uuid {
+       my ($self) = @_;
+       $self->{uuid} ||= $self->get_uuid;
+}
+
+sub gs_do_update {
+       my ($self, $rev_a, $rev_b, $path, $recurse, $editor) = @_;
+       my $pool = SVN::Pool->new;
+       my $reporter = $self->do_update($rev_b, $path, $recurse,
+                                       $editor, $pool);
+       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+       my $new = ($rev_a == $rev_b);
+       $reporter->set_path($path, $rev_a, $new, @lock, $pool);
+       $reporter->finish_report($pool);
+       $pool->clear;
+       $editor->{git_commit_ok};
+}
+
+sub gs_do_switch {
+       my ($self, $rev_a, $rev_b, $path, $recurse, $url_b, $editor) = @_;
+       my $pool = SVN::Pool->new;
+       my $reporter = $self->do_switch($rev_b, $path, $recurse,
+                                       $url_b, $editor, $pool);
+       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+       $reporter->set_path($path, $rev_a, 0, @lock, $pool);
+       $reporter->finish_report($pool);
+       $pool->clear;
+       $editor->{git_commit_ok};
+}
+
+sub can_do_switch {
+       my $self = shift;
+       unless (defined $can_do_switch) {
+               my $pool = SVN::Pool->new;
+               my $rep = eval {
+                       $self->do_switch(1, '', 0, $self->{url},
+                                        SVN::Delta::Editor->new, $pool);
+               };
+               if ($@) {
+                       $can_do_switch = 0;
+               } else {
+                       $rep->abort_report($pool);
+                       $can_do_switch = 1;
+               }
+               $pool->clear;
+       }
+       $can_do_switch;
+}
+
 __END__
 
 Data structures: