X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=git-svn.perl;h=df0ed9027df2c3b8b8f9d7f4f4046f3a0b1ae7f1;hb=847d10f56d7853cd0e139a7c2e6ad0ad2de5c464;hp=4c9c59bc3ffb9ed2f8e808cf6849562669adfd18;hpb=7ac749c96d143ba4f76723959892cbaddbe8ed07;p=git.git diff --git a/git-svn.perl b/git-svn.perl index 4c9c59bc3..df0ed9027 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -261,7 +261,7 @@ sub usage { my $fd = $exit ? \*STDERR : \*STDOUT; print $fd <<""; git-svn - bidirectional operations between a single Subversion tree and git -Usage: $0 [options] [arguments]\n +Usage: git svn [options] [arguments]\n print $fd "Available commands:\n" unless $cmd; @@ -537,13 +537,13 @@ sub cmd_find_rev { my $head = shift; $head ||= 'HEAD'; my @refs; - my (undef, undef, undef, $gs) = working_head_info($head, \@refs); + my (undef, undef, $uuid, $gs) = working_head_info($head, \@refs); unless ($gs) { die "Unable to determine upstream SVN information from ", "$head history\n"; } my $desired_revision = substr($revision_or_hash, 1); - $result = $gs->rev_map_get($desired_revision); + $result = $gs->rev_map_get($desired_revision, $uuid); } else { my (undef, $rev, undef) = cmt_metadata($revision_or_hash); $result = $rev; @@ -643,6 +643,8 @@ sub canonicalize_path { $path =~ s#/[^/]+/\.\.##g; $path =~ s#/$##g; $path =~ s#^\./## if $dot_slash_added; + $path =~ s#^/##; + $path =~ s#^\.$##; return $path; } @@ -794,8 +796,8 @@ sub cmd_commit_diff { } sub cmd_info { - my $path = canonicalize_path(shift or "."); - unless (scalar(@_) == 0) { + my $path = canonicalize_path(defined($_[0]) ? $_[0] : "."); + if (exists $_[1]) { die "Too many arguments specified\n"; } @@ -811,6 +813,10 @@ sub cmd_info { die "Unable to determine upstream SVN information from ", "working tree history\n"; } + + # canonicalize_path() will return "" to make libsvn 1.5.x happy, + $path = "." if $path eq ""; + my $full_url = $url . ($path eq "." ? "" : "/$path"); if ($_url) { @@ -1160,7 +1166,7 @@ sub working_head_info { if (defined $url && defined $rev) { next if $max{$url} and $max{$url} < $rev; if (my $gs = Git::SVN->find_by_url($url)) { - my $c = $gs->rev_map_get($rev); + my $c = $gs->rev_map_get($rev, $uuid); if ($c && $c eq $hash) { close $fh; # break the pipe return ($url, $rev, $uuid, $gs); @@ -1224,7 +1230,7 @@ sub linearize_history { sub find_file_type_and_diff_status { my ($path) = @_; - return ('dir', '') if $path eq '.'; + return ('dir', '') if $path eq ''; my $diff_output = command_oneline(qw(diff --cached --name-status --), $path) || ""; @@ -1414,11 +1420,21 @@ sub fetch_all { sub read_all_remotes { my $r = {}; + my $use_svm_props = eval { command_oneline(qw/config --bool + svn.useSvmProps/) }; + $use_svm_props = $use_svm_props eq 'true' if $use_svm_props; foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) { - if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) { - my ($remote, $local_ref, $remote_ref) = ($1, $2, $3); + if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) { + my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3); + die("svn-remote.$remote: remote ref '$_remote_ref' " + . "must start with 'refs/remotes/'\n") + unless $_remote_ref =~ m{^refs/remotes/(.+)}; + my $remote_ref = $1; $local_ref =~ s{^/}{}; $r->{$remote}->{fetch}->{$local_ref} = $remote_ref; + $r->{$remote}->{svm} = {} if $use_svm_props; + } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) { + $r->{$1}->{svm} = {}; } elsif (m!^(.+)\.url=\s*(.*)\s*$!) { $r->{$1}->{url} = $2; } elsif (m!^(.+)\.(branches|tags)= @@ -1435,6 +1451,23 @@ sub read_all_remotes { } } } + + map { + if (defined $r->{$_}->{svm}) { + my $svm; + eval { + my $section = "svn-remote.$_"; + $svm = { + source => tmp_config('--get', + "$section.svm-source"), + replace => tmp_config('--get', + "$section.svm-replace"), + } + }; + $r->{$_}->{svm} = $svm; + } + } keys %$r; + $r; } @@ -1462,13 +1495,6 @@ sub verify_remotes_sanity { } } -# we allow more chars than remotes2config.sh... -sub sanitize_remote_name { - my ($name) = @_; - $name =~ tr{A-Za-z0-9:,/+-}{.}c; - $name; -} - sub find_existing_remote { my ($url, $remotes) = @_; return undef if $no_reuse_existing; @@ -1568,13 +1594,21 @@ sub find_by_url { # repos_root and, path are optional } my $p = $path; my $rwr = rewrite_root({repo_id => $repo_id}); + my $svm = $remotes->{$repo_id}->{svm} + if defined $remotes->{$repo_id}->{svm}; unless (defined $p) { $p = $full_url; my $z = $u; + my $prefix = ''; if ($rwr) { $z = $rwr; + } elsif (defined $svm) { + $z = $svm->{source}; + $prefix = $svm->{replace}; + $prefix =~ s#^\Q$u\E(?:/|$)##; + $prefix =~ s#/$##; } - $p =~ s#^\Q$z\E(?:/|$)## or next; + $p =~ s#^\Q$z\E(?:/|$)#$prefix# or next; } foreach my $f (keys %$fetch) { next if $f ne $p; @@ -2853,7 +2887,7 @@ sub _new { unless (defined $ref_id && length $ref_id) { $_[2] = $ref_id = $Git::SVN::default_ref_id; } - $_[1] = $repo_id = sanitize_remote_name($repo_id); + $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; $_[3] = $path = '' unless (defined $path); mkpath(["$ENV{GIT_DIR}/svn"]); @@ -3243,7 +3277,9 @@ sub close_file { my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1); my $result; while ($result = sysread($fh, my $string, 1024)) { - syswrite($tmp_fh, $string, $result); + my $wrote = syswrite($tmp_fh, $string, $result); + defined($wrote) && $wrote == $result + or croak("write $tmp_filename: $!\n"); } defined $result or croak $!; close $tmp_fh or croak $!; @@ -3251,6 +3287,7 @@ sub close_file { close $fh or croak $!; $hash = $::_repository->hash_and_insert_object($tmp_filename); + unlink($tmp_filename); $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n"; close $fb->{base} or croak $!; } else { @@ -3311,6 +3348,7 @@ sub new { $self->{rm} = { }; $self->{path_prefix} = length $self->{svn_path} ? "$self->{svn_path}/" : ''; + $self->{config} = $opts->{config}; return $self; } @@ -3499,6 +3537,57 @@ sub ensure_path { return $bat->{$c}; } +# Subroutine to convert a globbing pattern to a regular expression. +# From perl cookbook. +sub glob2pat { + my $globstr = shift; + my %patmap = ('*' => '.*', '?' => '.', '[' => '[', ']' => ']'); + $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge; + return '^' . $globstr . '$'; +} + +sub check_autoprop { + my ($self, $pattern, $properties, $file, $fbat) = @_; + # Convert the globbing pattern to a regular expression. + my $regex = glob2pat($pattern); + # Check if the pattern matches the file name. + if($file =~ m/($regex)/) { + # Parse the list of properties to set. + my @props = split(/;/, $properties); + foreach my $prop (@props) { + # Parse 'name=value' syntax and set the property. + if ($prop =~ /([^=]+)=(.*)/) { + my ($n,$v) = ($1,$2); + for ($n, $v) { + s/^\s+//; s/\s+$//; + } + $self->change_file_prop($fbat, $n, $v); + } + } + } +} + +sub apply_autoprops { + my ($self, $file, $fbat) = @_; + my $conf_t = ${$self->{config}}{'config'}; + no warnings 'once'; + # Check [miscellany]/enable-auto-props in svn configuration. + if (SVN::_Core::svn_config_get_bool( + $conf_t, + $SVN::_Core::SVN_CONFIG_SECTION_MISCELLANY, + $SVN::_Core::SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, + 0)) { + # Auto-props are enabled. Enumerate them to look for matches. + my $callback = sub { + $self->check_autoprop($_[0], $_[1], $file, $fbat); + }; + SVN::_Core::svn_config_enumerate( + $conf_t, + $SVN::_Core::SVN_CONFIG_SECTION_AUTO_PROPS, + $callback); + } +} + sub A { my ($self, $m) = @_; my ($dir, $file) = split_path($m->{file_b}); @@ -3506,6 +3595,7 @@ sub A { my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, undef, -1); print "\tA\t$m->{file_b}\n" unless $::_q; + $self->apply_autoprops($file, $fbat); $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); } @@ -4621,7 +4711,7 @@ sub migrate_from_v1 { mkpath([$svn_dir]); print STDERR "Data from a previous version of git-svn exists, but\n\t", "$svn_dir\n\t(required for this version ", - "($::VERSION) of git-svn) does not. exist\n"; + "($::VERSION) of git-svn) does not exist.\n"; my ($fh, $ctx) = command_output_pipe(qw/rev-parse --symbolic --all/); while (<$fh>) { my $x = $_; @@ -4704,8 +4794,7 @@ sub minimize_connections { # 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)) { + ($ra->{repos_root} eq $repo_id)) { $root_repos->{$ra->{url}} = $repo_id; next; } @@ -4744,8 +4833,7 @@ sub minimize_connections { 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 $repo_id = $root_repos->{$url} || $url; my $fetch = $new_urls->{$url}; foreach my $path (keys %$fetch) {