# import functions from Git into our packages, en masse
no strict 'refs';
foreach (qw/command command_oneline command_noisy command_output_pipe
- command_input_pipe command_close_pipe/) {
+ command_input_pipe command_close_pipe
+ command_bidi_pipe command_close_bidi_pipe/) {
for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
Git::SVN::Migration Git::SVN::Log Git::SVN),
__PACKAGE__) {
my ($_stdin, $_help, $_edit,
$_message, $_file,
$_template, $_shared,
- $_version, $_fetch_all, $_no_rebase,
+ $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
$_merge, $_strategy, $_dry_run, $_local,
$_prefix, $_no_checkout, $_url, $_verbose,
$_git_format, $_commit_url, $_tag);
$Git::SVN::_follow_parent = 1;
+$_q ||= 0;
my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
'config-dir=s' => \$Git::SVN::Ra::config_dir,
'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
'useSvnsyncProps' => \$Git::SVN::_use_svnsync_props,
'log-window-size=i' => \$Git::SVN::Ra::_log_window_size,
'no-checkout' => \$_no_checkout,
- 'quiet|q' => \$_q,
+ 'quiet|q+' => \$_q,
'repack-flags|repack-args|repack-opts=s' =>
\$Git::SVN::_repack_flags,
'use-log-author' => \$Git::SVN::_use_log_author,
fetch => [ \&cmd_fetch, "Download new revisions from SVN",
{ 'revision|r=s' => \$_revision,
'fetch-all|all' => \$_fetch_all,
+ 'parent|p' => \$_fetch_parent,
%fc_opts } ],
clone => [ \&cmd_clone, "Initialize and fetch revisions",
{ 'revision|r=s' => \$_revision,
command_noisy(@init_db);
$_repository = Git->repository(Repository => ".git");
}
+ command_noisy('config', 'core.autocrlf', 'false');
my $set;
my $pfx = "svn-remote.$Git::SVN::default_repo_id";
foreach my $i (keys %icv) {
command_noisy('config', "$pfx.$i", $icv{$i});
$set = $i;
}
+ my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex;
+ command_noisy('config', "$pfx.ignore-paths", $$ignore_regex)
+ if defined $$ignore_regex;
}
sub init_subdir {
}
my ($remote) = @_;
if (@_ > 1) {
- die "Usage: $0 fetch [--all] [svn-remote]\n";
+ die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
}
- $remote ||= $Git::SVN::default_repo_id;
- if ($_fetch_all) {
+ if ($_fetch_parent) {
+ my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+ unless ($gs) {
+ die "Unable to determine upstream SVN information from ",
+ "working tree history\n";
+ }
+ # just fetch, don't checkout.
+ $_no_checkout = 'true';
+ $_fetch_all ? $gs->fetch_all : $gs->fetch;
+ } elsif ($_fetch_all) {
cmd_multi_fetch();
} else {
+ $remote ||= $Git::SVN::default_repo_id;
Git::SVN::fetch_all($remote, Git::SVN::read_all_remotes());
}
}
command(qw/cat-file commit/, shift)))[-1]);
}
+sub cmt_sha2rev_batch {
+ my %s2r;
+ my ($pid, $in, $out, $ctx) = command_bidi_pipe(qw/cat-file --batch/);
+ my $list = shift;
+
+ foreach my $sha (@{$list}) {
+ my $first = 1;
+ my $size = 0;
+ print $out $sha, "\n";
+
+ while (my $line = <$in>) {
+ if ($first && $line =~ /^[[:xdigit:]]{40}\smissing$/) {
+ last;
+ } elsif ($first &&
+ $line =~ /^[[:xdigit:]]{40}\scommit\s(\d+)$/) {
+ $first = 0;
+ $size = $1;
+ next;
+ } elsif ($line =~ /^(git-svn-id: )/) {
+ my (undef, $rev, undef) =
+ extract_metadata($line);
+ $s2r{$sha} = $rev;
+ }
+
+ $size -= length($line);
+ last if ($size == 0);
+ }
+ }
+
+ command_close_bidi_pipe($pid, $in, $out, $ctx);
+
+ return \%s2r;
+}
+
sub working_head_info {
my ($head, $refs) = @_;
my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
$self->{last_rev} = $log_entry->{revision};
$self->{last_commit} = $commit;
- print "r$log_entry->{revision}";
+ print "r$log_entry->{revision}" unless $::_q > 1;
if (defined $log_entry->{svm_revision}) {
- print " (\@$log_entry->{svm_revision})";
+ print " (\@$log_entry->{svm_revision})" unless $::_q > 1;
$self->rev_map_set($log_entry->{svm_revision}, $commit,
0, $self->svm_uuid);
}
- print " = $commit ($self->{ref_id})\n";
+ print " = $commit ($self->{ref_id})\n" unless $::_q > 1;
if (--$_gc_nr == 0) {
$_gc_nr = $_gc_period;
gc();
if (my $path = $paths->{"/$self->{path}"}) {
return ($path->{action} eq 'D') ? 0 : 1;
}
- $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
+ my $repos_root = $self->ra->{repos_root};
+ my $extended_path = $self->{url} . '/' . $self->{path};
+ $extended_path =~ s#^\Q$repos_root\E(/|$)##;
+ $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//;
if (grep /$self->{path_regex}/, keys %$paths) {
return 1;
}
$self->{empty_symlinks} =
_mark_empty_symlinks($git_svn, $switch_path);
}
+ $self->{ignore_regex} = eval { command_oneline('config', '--get',
+ "svn-remote.$git_svn->{repo_id}.ignore-paths") };
$self->{empty} = {};
$self->{dir_prop} = {};
$self->{file_prop} = {};
# return value: 0 -- don't ignore, 1 -- ignore
sub is_path_ignored {
- my ($path) = @_;
+ my ($self, $path) = @_;
return 1 if in_dot_git($path);
+ return 1 if defined($self->{ignore_regex}) &&
+ $path =~ m!$self->{ignore_regex}!;
return 0 unless defined($_ignore_regex);
return 1 if $path =~ m!$_ignore_regex!o;
return 0;
sub delete_entry {
my ($self, $path, $rev, $pb) = @_;
- return undef if is_path_ignored($path);
+ return undef if $self->is_path_ignored($path);
my $gpath = $self->git_path($path);
return undef if ($gpath eq '');
my ($self, $path, $pb, $rev) = @_;
my ($mode, $blob);
- goto out if is_path_ignored($path);
+ goto out if $self->is_path_ignored($path);
my $gpath = $self->git_path($path);
($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
my $mode;
- if (!is_path_ignored($path)) {
+ if (!$self->is_path_ignored($path)) {
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
delete $self->{empty}->{$dir};
$mode = '100644';
sub add_directory {
my ($self, $path, $cp_path, $cp_rev) = @_;
- goto out if is_path_ignored($path);
+ goto out if $self->is_path_ignored($path);
my $gpath = $self->git_path($path);
if ($gpath eq '') {
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
sub change_dir_prop {
my ($self, $db, $prop, $value) = @_;
- return undef if is_path_ignored($db->{path});
+ return undef if $self->is_path_ignored($db->{path});
$self->{dir_prop}->{$db->{path}} ||= {};
$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
undef;
sub absent_directory {
my ($self, $path, $pb) = @_;
- return undef if is_path_ignored($path);
+ return undef if $self->is_path_ignored($path);
$self->{absent_dir}->{$pb->{path}} ||= [];
push @{$self->{absent_dir}->{$pb->{path}}}, $path;
undef;
sub absent_file {
my ($self, $path, $pb) = @_;
- return undef if is_path_ignored($path);
+ return undef if $self->is_path_ignored($path);
$self->{absent_file}->{$pb->{path}} ||= [];
push @{$self->{absent_file}->{$pb->{path}}}, $path;
undef;
sub change_file_prop {
my ($self, $fb, $prop, $value) = @_;
- return undef if is_path_ignored($fb->{path});
+ return undef if $self->is_path_ignored($fb->{path});
if ($prop eq 'svn:executable') {
if ($fb->{mode_b} != 120000) {
$fb->{mode_b} = defined $value ? 100755 : 100644;
sub apply_textdelta {
my ($self, $fb, $exp) = @_;
- return undef if is_path_ignored($fb->{path});
+ return undef if $self->is_path_ignored($fb->{path});
my $fh = $::_repository->temp_acquire('svn_delta');
# $fh gets auto-closed() by SVN::TxDelta::apply(),
# (but $base does not,) so dup() it for reading in close_file
sub close_file {
my ($self, $fb, $exp) = @_;
- return undef if is_path_ignored($fb->{path});
+ return undef if $self->is_path_ignored($fb->{path});
my $hash;
my $path = $self->git_path($fb->{path});
'--', $path);
my ($sha1);
my %authors;
+ my @buffer;
+ my %dsha; #distinct sha keys
+
while (my $line = <$fh>) {
+ push @buffer, $line;
+ if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
+ $dsha{$1} = 1;
+ }
+ }
+
+ my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
+
+ foreach my $line (@buffer) {
if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
- $sha1 = $1;
- (undef, $rev, undef) = ::cmt_metadata($1);
- $rev = '0' if (!$rev);
+ $rev = $s2r->{$1};
+ $rev = '0' if (!$rev)
}
elsif ($line =~ /^author (.*)/) {
$authors{$rev} = $1;