git-svn: enable --minimize to simplify the config and connections
--minimize will update the git-svn configuration to attempt to connect to the repository root (instead of directly to the path(s) we are tracking) in order to allow more efficient reuse of connections (for multi-fetch and follow-parent). Signed-off-by: Eric Wong <normalperson@yhbt.net>
This commit is contained in:
110
git-svn.perl
110
git-svn.perl
@ -119,8 +119,6 @@ my %cmd = (
|
|||||||
\%fc_opts ],
|
\%fc_opts ],
|
||||||
'migrate' => [ sub { },
|
'migrate' => [ sub { },
|
||||||
# no-op, we automatically run this anyways,
|
# no-op, we automatically run this anyways,
|
||||||
# we may add a flag to automatically optimize the
|
|
||||||
# configuration to avoid reconnects in the future
|
|
||||||
'Migrate configuration/metadata/layout from
|
'Migrate configuration/metadata/layout from
|
||||||
previous versions of git-svn',
|
previous versions of git-svn',
|
||||||
\%remote_opts ],
|
\%remote_opts ],
|
||||||
@ -158,6 +156,8 @@ my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
|
|||||||
read_repo_config(\%opts);
|
read_repo_config(\%opts);
|
||||||
my $rv = GetOptions(%opts, 'help|H|h' => \$_help,
|
my $rv = GetOptions(%opts, 'help|H|h' => \$_help,
|
||||||
'version|V' => \$_version,
|
'version|V' => \$_version,
|
||||||
|
'minimize-connections' =>
|
||||||
|
\$Git::SVN::Migration::_minimize,
|
||||||
'id|i=s' => \$Git::SVN::default_repo_id);
|
'id|i=s' => \$Git::SVN::default_repo_id);
|
||||||
exit 1 if (!$rv && $cmd ne 'log');
|
exit 1 if (!$rv && $cmd ne 'log');
|
||||||
|
|
||||||
@ -702,10 +702,22 @@ BEGIN {
|
|||||||
svn:entry:committed-date/;
|
svn:entry:committed-date/;
|
||||||
}
|
}
|
||||||
|
|
||||||
# we allow dashes, unlike remotes2config.sh
|
sub read_all_remotes {
|
||||||
|
my $r = {};
|
||||||
|
foreach (grep { s/^svn-remote\.// } command(qw/repo-config -l/)) {
|
||||||
|
if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) {
|
||||||
|
$r->{$1}->{fetch}->{$2} = $3;
|
||||||
|
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
|
||||||
|
$r->{$1}->{url} = $2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$r;
|
||||||
|
}
|
||||||
|
|
||||||
|
# we allow more chars than remotes2config.sh...
|
||||||
sub sanitize_remote_name {
|
sub sanitize_remote_name {
|
||||||
my ($name) = @_;
|
my ($name) = @_;
|
||||||
$name =~ tr/A-Za-z0-9-/./c;
|
$name =~ tr{A-Za-z0-9:,/+-}{.}c;
|
||||||
$name;
|
$name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2467,7 +2479,8 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use Carp qw/croak/;
|
use Carp qw/croak/;
|
||||||
use File::Path qw/mkpath/;
|
use File::Path qw/mkpath/;
|
||||||
use File::Basename qw/dirname/;
|
use File::Basename qw/dirname basename/;
|
||||||
|
use vars qw/$_minimize/;
|
||||||
|
|
||||||
sub migrate_from_v0 {
|
sub migrate_from_v0 {
|
||||||
my $git_dir = $ENV{GIT_DIR};
|
my $git_dir = $ENV{GIT_DIR};
|
||||||
@ -2577,16 +2590,101 @@ sub migrate_from_v2 {
|
|||||||
my $migrated = 0;
|
my $migrated = 0;
|
||||||
|
|
||||||
foreach my $ref_id (sort keys %l_map) {
|
foreach my $ref_id (sort keys %l_map) {
|
||||||
Git::SVN->init($l_map{$ref_id}, $ref_id);
|
Git::SVN->init($l_map{$ref_id}, '', $ref_id, $ref_id);
|
||||||
$migrated++;
|
$migrated++;
|
||||||
}
|
}
|
||||||
$migrated;
|
$migrated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub minimize_connections {
|
||||||
|
my $r = Git::SVN::read_all_remotes();
|
||||||
|
my $new_urls = {};
|
||||||
|
my $root_repos = {};
|
||||||
|
foreach my $repo_id (keys %$r) {
|
||||||
|
my $url = $r->{$repo_id}->{url} or next;
|
||||||
|
my $fetch = $r->{$repo_id}->{fetch} or next;
|
||||||
|
my $ra = Git::SVN::Ra->new($url);
|
||||||
|
|
||||||
|
# skip existing cases where we already connect to the root
|
||||||
|
if (($ra->{url} eq $ra->{repos_root}) ||
|
||||||
|
(Git::SVN::sanitize_remote_name($ra->{repos_root}) eq
|
||||||
|
$repo_id)) {
|
||||||
|
$root_repos->{$ra->{url}} = $repo_id;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $root_ra = Git::SVN::Ra->new($ra->{repos_root});
|
||||||
|
my $root_path = $ra->{url};
|
||||||
|
$root_path =~ s#^\Q$ra->{repos_root}\E/*##;
|
||||||
|
foreach my $path (keys %$fetch) {
|
||||||
|
my $ref_id = $fetch->{$path};
|
||||||
|
my $gs = Git::SVN->new($ref_id, $repo_id, $path);
|
||||||
|
|
||||||
|
# make sure we can read when connecting to
|
||||||
|
# a higher level of a repository
|
||||||
|
my ($last_rev, undef) = $gs->last_rev_commit;
|
||||||
|
if (!defined $last_rev) {
|
||||||
|
$last_rev = eval {
|
||||||
|
$root_ra->get_latest_revnum;
|
||||||
|
};
|
||||||
|
next if $@;
|
||||||
|
}
|
||||||
|
my $new = $root_path;
|
||||||
|
$new .= length $path ? "/$path" : '';
|
||||||
|
eval {
|
||||||
|
$root_ra->get_log([$new], $last_rev, $last_rev,
|
||||||
|
0, 0, 1, sub { });
|
||||||
|
};
|
||||||
|
next if $@;
|
||||||
|
$new_urls->{$ra->{repos_root}}->{$new} =
|
||||||
|
{ ref_id => $ref_id,
|
||||||
|
old_repo_id => $repo_id,
|
||||||
|
old_path => $path };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my @emptied;
|
||||||
|
foreach my $url (keys %$new_urls) {
|
||||||
|
# see if we can re-use an existing [svn-remote "repo_id"]
|
||||||
|
# instead of creating a(n ugly) new section:
|
||||||
|
my $repo_id = $root_repos->{$url} ||
|
||||||
|
Git::SVN::sanitize_remote_name($url);
|
||||||
|
|
||||||
|
my $fetch = $new_urls->{$url};
|
||||||
|
foreach my $path (keys %$fetch) {
|
||||||
|
my $x = $fetch->{$path};
|
||||||
|
Git::SVN->init($url, $path, $repo_id, $x->{ref_id});
|
||||||
|
my $pfx = "svn-remote.$x->{old_repo_id}";
|
||||||
|
|
||||||
|
my $old_fetch = quotemeta("$x->{old_path}:".
|
||||||
|
"refs/remotes/$x->{ref_id}");
|
||||||
|
command_noisy(qw/repo-config --unset/,
|
||||||
|
"$pfx.fetch", '^'. $old_fetch . '$');
|
||||||
|
delete $r->{$x->{old_repo_id}}->
|
||||||
|
{fetch}->{$x->{old_path}};
|
||||||
|
if (!keys %{$r->{$x->{old_repo_id}}->{fetch}}) {
|
||||||
|
command_noisy(qw/repo-config --unset/,
|
||||||
|
"$pfx.url");
|
||||||
|
push @emptied, $x->{old_repo_id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (@emptied) {
|
||||||
|
my $file = $ENV{GIT_CONFIG} || $ENV{GIT_CONFIG_LOCAL} ||
|
||||||
|
"$ENV{GIT_DIR}/config";
|
||||||
|
print STDERR <<EOF;
|
||||||
|
The following [svn-remote] sections in your config file ($file) are empty
|
||||||
|
and can be safely removed:
|
||||||
|
EOF
|
||||||
|
print STDERR "[svn-remote \"$_\"]\n" foreach @emptied;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub migration_check {
|
sub migration_check {
|
||||||
migrate_from_v0();
|
migrate_from_v0();
|
||||||
migrate_from_v1();
|
migrate_from_v1();
|
||||||
migrate_from_v2();
|
migrate_from_v2();
|
||||||
|
minimize_connections() if $_minimize;
|
||||||
}
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
@ -46,6 +46,7 @@ test_expect_success 'initialize a multi-repository repo' "
|
|||||||
grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out
|
grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out
|
||||||
"
|
"
|
||||||
|
|
||||||
|
# refs should all be different, but the trees should all be the same:
|
||||||
test_expect_success 'multi-fetch works on partial urls + paths' "
|
test_expect_success 'multi-fetch works on partial urls + paths' "
|
||||||
git-svn multi-fetch &&
|
git-svn multi-fetch &&
|
||||||
for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
|
for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
|
||||||
@ -59,5 +60,29 @@ test_expect_success 'multi-fetch works on partial urls + paths' "
|
|||||||
refs/remotes/\$j\`\" ||exit 1; done; done
|
refs/remotes/\$j\`\" ||exit 1; done; done
|
||||||
"
|
"
|
||||||
|
|
||||||
|
test_expect_success 'migrate --minimize on old multi-inited layout' "
|
||||||
|
git repo-config --unset-all svn-remote.git-svn.fetch &&
|
||||||
|
git repo-config --unset-all svn-remote.git-svn.url &&
|
||||||
|
rm -rf $GIT_DIR/svn &&
|
||||||
|
for i in \`cat fetch.out\`; do
|
||||||
|
path=\`expr \$i : '\\([^:]*\\):.*$'\`
|
||||||
|
ref=\`expr \$i : '[^:]*:refs/remotes/\\(.*\\)$'\`
|
||||||
|
if test -z \"\$ref\"; then continue; fi
|
||||||
|
if test -n \"\$path\"; then path=\"/\$path\"; fi
|
||||||
|
( mkdir -p $GIT_DIR/svn/\$ref/info/ &&
|
||||||
|
echo $svnrepo\$path > $GIT_DIR/svn/\$ref/info/url ) || exit 1;
|
||||||
|
done &&
|
||||||
|
git-svn migrate --minimize &&
|
||||||
|
test -z \"\`git-repo-config -l |grep -v '^svn-remote\.git-svn\.'\`\" &&
|
||||||
|
git-repo-config --get-all svn-remote.git-svn.fetch > fetch.out &&
|
||||||
|
grep '^trunk:refs/remotes/trunk$' fetch.out &&
|
||||||
|
grep '^branches/a:refs/remotes/a$' fetch.out &&
|
||||||
|
grep '^branches/b:refs/remotes/b$' fetch.out &&
|
||||||
|
grep '^tags/0\.1:refs/remotes/tags/0\.1$' fetch.out &&
|
||||||
|
grep '^tags/0\.2:refs/remotes/tags/0\.2$' fetch.out &&
|
||||||
|
grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out
|
||||||
|
grep '^:refs/remotes/git-svn' fetch.out
|
||||||
|
"
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user