$VERSION = '@@GIT_VERSION@@';
$ENV{GIT_DIR} ||= '.git';
-$Git::SVN::default_repo_id = $ENV{GIT_SVN_ID} || 'git-svn';
+$Git::SVN::default_repo_id = 'git-svn';
+$Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
my $LC_ALL = $ENV{LC_ALL};
$Git::SVN::Log::TZ = $ENV{TZ};
'version|V' => \$_version,
'minimize-connections' =>
\$Git::SVN::Migration::_minimize,
- 'id|i=s' => \$Git::SVN::default_repo_id);
+ 'id|i=s' => \$Git::SVN::default_ref_id);
exit 1 if (!$rv && $cmd ne 'log');
usage(0) if $_help;
unless ($cmd =~ /^(?:init|rebuild|multi-init|commit-diff)$/) {
Git::SVN::Migration::migration_check();
}
-$cmd{$cmd}->[0]->(@ARGV);
+eval {
+ Git::SVN::verify_remotes_sanity();
+ $cmd{$cmd}->[0]->(@ARGV);
+};
+fatal $@ if $@;
exit 0;
####################### primary functions ######################
}
sub cmd_fetch {
+ if (@_) {
+ die "Additional fetch arguments are no longer supported.\n",
+ "Use --follow-parent if you have moved/copied directories
+ instead.\n";
+ }
my $gs = Git::SVN->new;
- $gs->fetch(@_);
+ $gs->fetch(parse_revision_argument());
if ($gs->{last_commit} && !verify_ref('refs/heads/master^0')) {
command_noisy(qw(update-ref refs/heads/master),
$gs->{last_commit});
my $gs = Git::SVN->new;
my ($r_last, $cmt_last) = $gs->last_rev_commit;
$gs->fetch;
- if ($r_last != $gs->{last_rev}) {
+ if (defined $gs->{last_rev} && $r_last != $gs->{last_rev}) {
fatal "There are new revisions that were fetched ",
"and need to be merged (or acknowledged) ",
"before committing.\nlast rev: $r_last\n",
my $pool = SVN::Pool->new;
my %ed_opts = ( r => $last_rev,
ra => $ra->dup,
- svn_path => $ra->{svn_path} );
+ svn_path => $gs->{path} );
my $ed = SVN::Git::Editor->new(\%ed_opts,
$ra->get_commit_editor($log,
sub { print "Committed r$_[0]\n";
my $usage = "Usage: $0 commit-diff -r<revision> ".
"<tree-ish> <tree-ish> [<URL>]\n";
fatal($usage) if (!defined $ta || !defined $tb);
+ my $svn_path;
if (!defined $url) {
my $gs = eval { Git::SVN->new };
if (!$gs) {
"the command-line\n", $usage);
}
$url = $gs->{url};
+ $svn_path = $gs->{path};
}
unless (defined $_revision) {
fatal("-r|--revision is a required argument\n", $usage);
$_message ||= get_commit_entry($tb)->{log};
}
my $ra ||= Git::SVN::Ra->new($url);
+ $svn_path ||= $ra->{svn_path};
my $r = $_revision;
if ($r eq 'HEAD') {
$r = $ra->get_latest_revnum;
my $pool = SVN::Pool->new;
my %ed_opts = ( r => $r,
ra => $ra->dup,
- svn_path => $ra->{svn_path} );
+ svn_path => $svn_path );
my $ed = SVN::Git::Editor->new(\%ed_opts,
$ra->get_commit_editor($_message,
sub { print "Committed r$_[0]\n" }),
########################### utility functions #########################
+sub parse_revision_argument {
+ if (!defined $_revision || $_revision eq 'BASE:HEAD') {
+ return (undef, undef);
+ }
+ return ($1, $2) if ($_revision =~ /^(\d+):(\d+)$/);
+ return ($_revision, $_revision) if ($_revision =~ /^\d+$/);
+ return (undef, $1) if ($_revision =~ /^BASE:(\d+)$/);
+ return ($1, undef) if ($_revision =~ /^(\d+):HEAD$/);
+ die "revision argument: $_revision not understood by git-svn\n",
+ "Try using the command-line svn client instead\n";
+}
+
sub complete_svn_url {
my ($url, $path) = @_;
$path =~ s#/+$##;
package Git::SVN;
use strict;
use warnings;
-use vars qw/$default_repo_id/;
+use vars qw/$default_repo_id $default_ref_id/;
use Carp qw/croak/;
use File::Path qw/mkpath/;
use IPC::Open3;
sub read_all_remotes {
my $r = {};
- foreach (grep { s/^svn-remote\.// } command(qw/repo-config -l/)) {
+ foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) {
$r->{$1}->{fetch}->{$2} = $3;
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r;
}
+sub verify_remotes_sanity {
+ return unless -d $ENV{GIT_DIR};
+ my %seen;
+ foreach (command(qw/config -l/)) {
+ if (m!^svn-remote\.(?:.+)\.fetch=.*:refs/remotes/(\S+)\s*$!) {
+ if ($seen{$1}) {
+ die "Remote ref refs/remote/$1 is tracked by",
+ "\n \"$_\"\nand\n \"$seen{$1}\"\n",
+ "Please resolve this ambiguity in ",
+ "your git configuration file before ",
+ "continuing\n";
+ }
+ $seen{$1} = $_;
+ }
+ }
+}
+
# we allow more chars than remotes2config.sh...
sub sanitize_remote_name {
my ($name) = @_;
$name;
}
-sub init {
- my ($class, $url, $path, $repo_id, $ref_id) = @_;
- my $self = _new($class, $repo_id, $ref_id, $path);
- mkpath([$self->{dir}]);
- if (defined $url) {
- $url =~ s!/+$!!; # strip trailing slash
- my $orig_url = eval {
+sub find_existing_remote {
+ my ($url, $remotes) = @_;
+ my $existing;
+ foreach my $repo_id (keys %$remotes) {
+ my $u = $remotes->{$repo_id}->{url} or next;
+ next if $u ne $url;
+ $existing = $repo_id;
+ last;
+ }
+ $existing;
+}
+
+sub init_remote_config {
+ my ($self, $url) = @_;
+ $url =~ s!/+$!!; # strip trailing slash
+ my $r = read_all_remotes();
+ my $existing = find_existing_remote($url, $r);
+ if ($existing) {
+ print STDERR "Using existing ",
+ "[svn-remote \"$existing\"]\n";
+ $self->{repo_id} = $existing;
+ } else {
+ my $min_url = Git::SVN::Ra->new($url)->minimize_url;
+ $existing = find_existing_remote($min_url, $r);
+ if ($existing) {
+ print STDERR "Using existing ",
+ "[svn-remote \"$existing\"]\n";
+ $self->{repo_id} = $existing;
+ }
+ if ($min_url ne $url) {
+ print STDERR "Using higher level of URL: ",
+ "$url => $min_url\n";
+ my $old_path = $self->{path};
+ $self->{path} = $url;
+ $self->{path} =~ s!^\Q$min_url\E/*!!;
+ if (length $old_path) {
+ $self->{path} .= "/$old_path";
+ }
+ $url = $min_url;
+ }
+ }
+ my $orig_url;
+ if (!$existing) {
+ # verify that we aren't overwriting anything:
+ $orig_url = eval {
command_oneline('config', '--get',
- "svn-remote.$repo_id.url")
+ "svn-remote.$self->{repo_id}.url")
};
- if ($orig_url) {
- if ($orig_url ne $url) {
- die "svn-remote.$repo_id.url already set: ",
- "$orig_url\nwanted to set to: $url\n";
- }
- } else {
- command_noisy('config',
- "svn-remote.$repo_id.url", $url);
+ if ($orig_url && ($orig_url ne $url)) {
+ die "svn-remote.$self->{repo_id}.url already set: ",
+ "$orig_url\nwanted to set to: $url\n";
}
- command_noisy('config', '--add',
- "svn-remote.$repo_id.fetch",
- "$path:".$self->refname);
}
+ my ($xrepo_id, $xpath) = find_ref($self->refname);
+ if (defined $xpath) {
+ die "svn-remote.$xrepo_id.fetch already set to track ",
+ "$xpath:refs/remotes/", $self->refname, "\n";
+ }
+ command_noisy('config',
+ "svn-remote.$self->{repo_id}.url", $url);
+ command_noisy('config', '--add',
+ "svn-remote.$self->{repo_id}.fetch",
+ "$self->{path}:".$self->refname);
$self->{url} = $url;
- unless (-f $self->{db_path}) {
- open my $fh, '>>', $self->{db_path} or croak $!;
- close $fh or croak $!;
+}
+
+sub init {
+ my ($class, $url, $path, $repo_id, $ref_id) = @_;
+ my $self = _new($class, $repo_id, $ref_id, $path);
+ if (defined $url) {
+ $self->init_remote_config($url);
}
$self;
}
}
}
my $self = _new($class, $repo_id, $ref_id, $path);
+ if (!defined $self->{path} || !length $self->{path}) {
+ my $fetch = command_oneline('config', '--get',
+ "svn-remote.$repo_id.fetch",
+ ":refs/remotes/$ref_id\$") or
+ die "Failed to read \"svn-remote.$repo_id.fetch\" ",
+ "\":refs/remotes/$ref_id\$\" in config\n";
+ ($self->{path}, undef) = split(/\s*:\s*/, $fetch);
+ }
$self->{url} = command_oneline('config', '--get',
"svn-remote.$repo_id.url") or
die "Failed to read \"svn-remote.$repo_id.url\" in config\n";
}
}
+sub last_rev { ($_[0]->last_rev_commit)[0] }
+sub last_commit { ($_[0]->last_rev_commit)[1] }
+
# returns the newest SVN revision number and newest commit SHA1
sub last_rev_commit {
my ($self) = @_;
return ($rev, $c);
}
-sub parse_revision {
- my ($self, $base) = @_;
- my $head = $self->ra->get_latest_revnum;
- if (!defined $::_revision || $::_revision eq 'BASE:HEAD') {
- return ($base + 1, $head) if (defined $base);
- return (0, $head);
- }
- return ($1, $2) if ($::_revision =~ /^(\d+):(\d+)$/);
- return ($::_revision, $::_revision) if ($::_revision =~ /^\d+$/);
- if ($::_revision =~ /^BASE:(\d+)$/) {
- return ($base + 1, $1) if (defined $base);
- return (0, $head);
- }
- return ($1, $head) if ($::_revision =~ /^(\d+):HEAD$/);
- die "revision argument: $::_revision not understood by git-svn\n",
- "Try using the command-line svn client instead\n";
+sub get_fetch_range {
+ my ($self, $min, $max) = @_;
+ $max ||= $self->ra->get_latest_revnum;
+ $min ||= $self->last_rev || 0;
+ (++$min, $max);
}
sub tmp_index_do {
sub find_parent_branch {
my ($self, $paths, $rev) = @_;
+ return undef unless $::_follow_parent;
+ unless (defined $paths) {
+ $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1,
+ sub { $paths = dup_changed_paths($_[0]) });
+ }
+ return undef unless defined $paths;
# look for a parent from another branch:
- my $i = $paths->{'/'.$self->rel_path} or return;
- my $branch_from = $i->copyfrom_path or return;
- my $r = $i->copyfrom_rev;
+ my @b_path_components = split m#/#, $self->rel_path;
+ my @a_path_components;
+ my $i;
+ while (@b_path_components) {
+ $i = $paths->{'/'.join('/', @b_path_components)};
+ last if $i;
+ unshift(@a_path_components, pop(@b_path_components));
+ }
+ goto not_found unless defined $i;
+ my $branch_from = $i->{copyfrom_path} or goto not_found;
+ if (@a_path_components) {
+ print STDERR "branch_from: $branch_from => ";
+ $branch_from .= '/'.join('/', @a_path_components);
+ print STDERR $branch_from, "\n";
+ }
+ my $r = $i->{copyfrom_rev};
my $repos_root = $self->ra->{repos_root};
my $url = $self->ra->{url};
my $new_url = $repos_root . $branch_from;
}
my ($r0, $parent) = $gs->find_rev_before($r, 1);
if ($::_follow_parent && (!defined $r0 || !defined $parent)) {
- foreach (0 .. $r) {
- my $log_entry = eval { $gs->do_fetch(undef, $_) };
- $gs->do_git_commit($log_entry) if $log_entry;
- }
+ $gs->fetch(0, $r);
($r0, $parent) = $gs->last_rev_commit;
}
if (defined $r0 && defined $parent && $gs->revisions_eq($r0, $r)) {
print STDERR "Found branch parent: ($self->{ref_id}) $parent\n";
- command_noisy('read-tree', $parent);
+ $self->assert_index_clean($parent);
my $ed;
if ($self->ra->can_do_switch) {
+ print STDERR "Following parent with do_switch\n";
# 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
$self->full_url, $ed)
or die "SVN connection failed somewhere...\n";
} else {
+ print STDERR "Following parent with do_update\n";
$ed = SVN::Git::Fetcher->new($self);
$self->ra->gs_do_update($rev, $rev, $self->{path},
1, $ed)
}
return $self->make_log_entry($rev, [$parent], $ed);
}
- print STDERR "Branch parent not found...\n";
+not_found:
+ print STDERR "Branch parent for path: '/",
+ $self->rel_path, "' @ r$rev not found:\n";
+ return undef unless $paths;
+ print STDERR "Changed paths:\n";
+ foreach my $x (sort keys %$paths) {
+ my $p = $paths->{$x};
+ print STDERR "\t$p->{action}\t$x";
+ if ($p->{copyfrom_path}) {
+ print STDERR "(from $p->{copyfrom_path}: ",
+ "$p->{copyfrom_rev})";
+ }
+ print STDERR "\n";
+ }
+ print STDERR '-'x72, "\n";
return undef;
}
$self->make_log_entry($rev, \@parents, $ed);
}
-sub write_untracked {
- my ($self, $rev, $fh, $untracked) = @_;
- my $h;
- print $fh "r$rev\n" or croak $!;
- $h = $untracked->{empty};
+sub get_untracked {
+ my ($self, $ed) = @_;
+ my @out;
+ my $h = $ed->{empty};
foreach (sort keys %$h) {
my $act = $h->{$_} ? '+empty_dir' : '-empty_dir';
- print $fh " $act: ", uri_encode($_), "\n" or croak $!;
+ push @out, " $act: " . uri_encode($_);
warn "W: $act: $_\n";
}
foreach my $t (qw/dir_prop file_prop/) {
- $h = $untracked->{$t} or next;
+ $h = $ed->{$t} or next;
foreach my $path (sort keys %$h) {
my $ppath = $path eq '' ? '.' : $path;
foreach my $prop (sort keys %{$h->{$path}}) {
next if $SKIP_PROP{$prop};
my $v = $h->{$path}->{$prop};
+ my $t_ppath_prop = "$t: " .
+ uri_encode($ppath) . ' ' .
+ uri_encode($prop);
if (defined $v) {
- print $fh " +$t: ",
- uri_encode($ppath), ' ',
- uri_encode($prop), ' ',
- uri_encode($v), "\n"
- or croak $!;
+ push @out, " +$t_ppath_prop " .
+ uri_encode($v);
} else {
- print $fh " -$t: ",
- uri_encode($ppath), ' ',
- uri_encode($prop), "\n"
- or croak $!;
+ push @out, " -$t_ppath_prop";
}
}
}
}
foreach my $t (qw/absent_file absent_directory/) {
- $h = $untracked->{$t} or next;
+ $h = $ed->{$t} or next;
foreach my $parent (sort keys %$h) {
foreach my $path (sort @{$h->{$parent}}) {
- print $fh " $t: ",
- uri_encode("$parent/$path"), "\n"
- or croak $!;
+ push @out, " $t: " .
+ uri_encode("$parent/$path");
warn "W: $t: $parent/$path ",
"Insufficient permissions?\n";
}
}
}
+ \@out;
}
sub parse_svn_date {
}
sub make_log_entry {
- my ($self, $rev, $parents, $untracked) = @_;
- my $rp = $self->ra->rev_proplist($rev);
- my %log_entry = ( parents => $parents || [], revision => $rev,
- revprops => $rp, log => '');
+ my ($self, $rev, $parents, $ed) = @_;
+ my $untracked = $self->get_untracked($ed);
+
+ return undef if ($ed->{nr} == 0 && scalar @$untracked == 0);
+
open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!;
- $self->write_untracked($rev, $un, $untracked);
+ print $un "r$rev\n" or croak $!;
+ print $un $_, "\n" foreach @$untracked;
+ my %log_entry = ( parents => $parents || [], revision => $rev,
+ log => '');
+ my $rp = $self->ra->rev_proplist($rev);
foreach (sort keys %$rp) {
my $v = $rp->{$_};
if (/^svn:(author|date|log)$/) {
}
}
close $un or croak $!;
+
$log_entry{date} = parse_svn_date($log_entry{date});
$log_entry{author} = check_author($log_entry{author});
$log_entry{log} .= "\n";
}
sub fetch {
- my ($self, @parents) = @_;
+ my ($self, $min_rev, $max_rev, @parents) = @_;
my ($last_rev, $last_commit) = $self->last_rev_commit;
- my ($base, $head) = $self->parse_revision($last_rev);
+ my ($base, $head) = $self->get_fetch_range($min_rev, $max_rev);
return if ($base > $head);
if (defined $last_commit) {
$self->assert_index_clean($last_commit);
my $inc = 1000;
my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
my $err_handler = $SVN::Error::handler;
- $SVN::Error::handler = \&skip_unknown_revs;
+ my $err;
+ $SVN::Error::handler = sub { ($err) = @_; skip_unknown_revs($err); } ;
while (1) {
my @revs;
- $self->ra->get_log([''], $min, $max, 0, 1, 1, sub {
- my ($paths, $rev, $author, $date, $log) = @_;
- push @revs, [ $paths, $rev ] });
+ $self->ra->get_log([$self->{path}], $min, $max, 0, 1, 1,
+ sub {
+ my ($paths, $rev) = @_;
+ push @revs, [ dup_changed_paths($paths), $rev ];
+ });
+ if (! @revs && $err && $max >= $head) {
+ print STDERR "Branch probably deleted:\n ",
+ $err->expanded_message,
+ "\nWill attempt to follow revisions ",
+ "r$min .. r$max",
+ "committed before the deletion\n";
+ @revs = map { [ undef, $_ ] } ($min .. $max);
+ }
foreach (@revs) {
- my $log_entry = $self->do_fetch(@$_);
- $self->do_git_commit($log_entry, @parents);
+ if (my $log_entry = $self->do_fetch(@$_)) {
+ $self->do_git_commit($log_entry, @parents);
+ }
}
last if $max >= $head;
$min = $max + 1;
$log_entry->{author} = $author;
$self->do_git_commit($log_entry, "$rev=$tree");
} else {
- $self->fetch("$rev=$tree");
+ $self->fetch(undef, undef, "$rev=$tree");
}
}
my $pool = SVN::Pool->new;
my $ed = SVN::Git::Editor->new({ r => $self->{last_rev},
ra => $self->ra->dup,
- svn_path => $self->ra->{svn_path}
+ svn_path => $self->{path}
},
$self->ra->get_commit_editor(
$log_entry->{log}, sub {
croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
}
+# svn_log_changed_path_t objects passed to get_log are likely to be
+# overwritten even if only the refs are copied to an external variable,
+# so we should dup the structures in their entirety. Using an externally
+# passed pool (instead of our temporary and quickly cleared pool in
+# Git::SVN::Ra) does not help matters at all...
+sub dup_changed_paths {
+ my ($paths) = @_;
+ return undef unless $paths;
+ my %ret;
+ foreach my $p (keys %$paths) {
+ my $i = $paths->{$p};
+ my %s = map { $_ => $i->$_ }
+ qw/copyfrom_path copyfrom_rev action/;
+ $ret{$p} = \%s;
+ }
+ \%ret;
+}
+
# rev_db:
# Tie::File seems to be prone to offset errors if revisions get sparse,
# it's not that fast, either. Tie::File is also not in Perl 5.6. So
$repo_id = $Git::SVN::default_repo_id;
}
unless (defined $ref_id && length $ref_id) {
- $_[2] = $ref_id = $repo_id;
+ $_[2] = $ref_id = $Git::SVN::default_ref_id;
}
$_[1] = $repo_id = sanitize_remote_name($repo_id);
my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
$_[3] = $path = '' unless (defined $path);
+ mkpath([$dir]);
+ unless (-f "$dir/.rev_db") {
+ open my $fh, '>>', "$dir/.rev_db" or croak $!;
+ close $fh or croak $!;
+ }
bless { ref_id => $ref_id, dir => $dir, index => "$dir/index",
path => $path,
db_path => "$dir/.rev_db", repo_id => $repo_id }, $class;
my $self = SVN::Delta::Editor->new;
bless $self, $class;
$self->{c} = $git_svn->{last_commit} if exists $git_svn->{last_commit};
- if (length $git_svn->{path}) {
- $self->{path_strip} = qr/\Q$git_svn->{path}\E\/?/;
- }
$self->{empty} = {};
$self->{dir_prop} = {};
$self->{file_prop} = {};
$self->{absent_dir} = {};
$self->{absent_file} = {};
- ($self->{gui}, $self->{ctx}) = $git_svn->tmp_index_do(
- sub { command_input_pipe(qw/update-index -z --index-info/) } );
+ $self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
require Digest::MD5;
$self;
}
+sub set_path_strip {
+ my ($self, $path) = @_;
+ $self->{path_strip} = qr/^\Q$path\E\/?/;
+}
+
sub open_root {
{ path => '' };
}
sub delete_entry {
my ($self, $path, $rev, $pb) = @_;
- my $gui = $self->{gui};
my $gpath = $self->git_path($path);
# remove entire directories.
$self->{c}, '--', $gpath);
local $/ = "\0";
while (<$ls>) {
- print $gui '0 ',0 x 40,"\t",$_ or croak $!;
+ chomp;
+ $self->{gii}->remove($_);
print "\tD\t$_\n" unless $self->{q};
}
print "\tD\t$gpath/\n" unless $self->{q};
command_close_pipe($ls, $ctx);
$self->{empty}->{$path} = 0
} else {
- print $gui '0 ',0 x 40,"\t",$gpath,"\0" or croak $!;
+ $self->{gii}->remove($gpath);
print "\tD\t$gpath\n" unless $self->{q};
}
undef;
$hash = $fb->{blob} or die "no blob information\n";
}
$fb->{pool}->clear;
- my $gui = $self->{gui};
- print $gui "$fb->{mode_b} $hash\t$path\0" or croak $!;
+ $self->{gii}->update($fb->{mode_b}, $hash, $path) or croak $!;
print "\t$fb->{action}\t$path\n" if $fb->{action} && ! $self->{q};
undef;
}
sub abort_edit {
my $self = shift;
- eval { command_close_pipe($self->{gui}, $self->{ctx}) };
+ $self->{nr} = $self->{gii}->{nr};
+ delete $self->{gii};
$self->SUPER::abort_edit(@_);
}
sub close_edit {
my $self = shift;
- command_close_pipe($self->{gui}, $self->{ctx});
$self->{git_commit_ok} = 1;
+ $self->{nr} = $self->{gii}->{nr};
+ delete $self->{gii};
$self->SUPER::close_edit(@_);
}
$self->{pool} = SVN::Pool->new;
$self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) };
$self->{rm} = { };
+ $self->{path_prefix} = length $self->{svn_path} ?
+ "$self->{svn_path}/" : '';
require Digest::MD5;
return $self;
}
}
sub repo_path {
- (defined $_[1] && length $_[1]) ? $_[1] : ''
+ my ($self, $path) = @_;
+ $self->{path_prefix}.(defined $path ? $path : '');
}
sub url_path {
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;
sub gs_do_update {
my ($self, $rev_a, $rev_b, $path, $recurse, $editor) = @_;
my $pool = SVN::Pool->new;
+ $editor->set_path_strip($path);
my $reporter = $self->do_update($rev_b, $path, $recurse,
$editor, $pool);
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
sub gs_do_switch {
my ($self, $rev_a, $rev_b, $path, $recurse, $url_b, $editor) = @_;
my $pool = SVN::Pool->new;
+ $editor->set_path_strip($path);
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->set_path('', $rev_a, 0, @lock, $pool);
$reporter->finish_report($pool);
$pool->clear;
$editor->{git_commit_ok};
}
+sub minimize_url {
+ my ($self) = @_;
+ return $self->{url} if ($self->{url} eq $self->{repos_root});
+ my $url = $self->{repos_root};
+ my @components = split(m!/!, $self->{svn_path});
+ my $c = '';
+ do {
+ $url .= "/$c" if length $c;
+ eval { (ref $self)->new($url)->get_latest_revnum };
+ } while ($@ && ($c = shift @components));
+ $url;
+}
+
sub can_do_switch {
my $self = shift;
unless (defined $can_do_switch) {
my $old_fetch = quotemeta("$x->{old_path}:".
"refs/remotes/$x->{ref_id}");
- command_noisy(qw/repo-config --unset/,
+ command_noisy(qw/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/,
+ command_noisy(qw/config --unset/,
"$pfx.url");
push @emptied, $x->{old_repo_id}
}
minimize_connections() if $_minimize;
}
+package Git::IndexInfo;
+use strict;
+use warnings;
+use Git qw/command_input_pipe command_close_pipe/;
+
+sub new {
+ my ($class) = @_;
+ my ($gui, $ctx) = command_input_pipe(qw/update-index -z --index-info/);
+ bless { gui => $gui, ctx => $ctx, nr => 0}, $class;
+}
+
+sub remove {
+ my ($self, $path) = @_;
+ if (print { $self->{gui} } '0 ', 0 x 40, "\t", $path, "\0") {
+ return ++$self->{nr};
+ }
+ undef;
+}
+
+sub update {
+ my ($self, $mode, $hash, $path) = @_;
+ if (print { $self->{gui} } $mode, ' ', $hash, "\t", $path, "\0") {
+ return ++$self->{nr};
+ }
+ undef;
+}
+
+sub DESTROY {
+ my ($self) = @_;
+ command_close_pipe($self->{gui}, $self->{ctx});
+}
+
__END__
Data structures: