foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
next unless m#^refs/remotes/$ref->{regex}$#;
my $p = $1;
- my $pathname = $path->full_path($p);
- my $refname = $ref->full_path($p);
+ my $pathname = desanitize_refname($path->full_path($p));
+ my $refname = desanitize_refname($ref->full_path($p));
if (my $existing = $fetch->{$pathname}) {
if ($existing ne $refname) {
die "Refspec conflict:\n",
$self;
}
-sub refname { "refs/remotes/$_[0]->{ref_id}" }
+sub refname {
+ my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
+
+ # It cannot end with a slash /, we'll throw up on this because
+ # SVN can't have directories with a slash in their name, either:
+ if ($refname =~ m{/$}) {
+ die "ref: '$refname' ends with a trailing slash, this is ",
+ "not permitted by git nor Subversion\n";
+ }
+
+ # It cannot have ASCII control character space, tilde ~, caret ^,
+ # colon :, question-mark ?, asterisk *, space, or open bracket [
+ # anywhere.
+ #
+ # Additionally, % must be escaped because it is used for escaping
+ # and we want our escaped refname to be reversible
+ $refname =~ s{([ \%~\^:\?\*\[\t])}{uc sprintf('%%%02x',ord($1))}eg;
+
+ # no slash-separated component can begin with a dot .
+ # /.* becomes /%2E*
+ $refname =~ s{/\.}{/%2E}g;
+
+ # It cannot have two consecutive dots .. anywhere
+ # .. becomes %2E%2E
+ $refname =~ s{\.\.}{%2E%2E}g;
+
+ return $refname;
+}
+
+sub desanitize_refname {
+ my ($refname) = @_;
+ $refname =~ s{%(?:([0-9A-F]{2}))}{chr hex($1)}eg;
+ return $refname;
+}
sub svm_uuid {
my ($self) = @_;
sub git_svn_log_cmd {
my ($r_min, $r_max, @args) = @_;
my $head = 'HEAD';
+ my (@files, @log_opts);
foreach my $x (@args) {
- last if $x eq '--';
- next unless ::verify_ref("$x^0");
- $head = $x;
- last;
+ if ($x eq '--' || @files) {
+ push @files, $x;
+ } else {
+ if (::verify_ref("$x^0")) {
+ $head = $x;
+ } else {
+ push @log_opts, $x;
+ }
+ }
}
my ($url, $rev, $uuid, $gs) = ::working_head_info($head);
push @cmd, '-r' unless $non_recursive;
push @cmd, qw/--raw --name-status/ if $verbose;
push @cmd, '--color' if log_use_color();
- return @cmd unless defined $r_max;
- if ($r_max == $r_min) {
+ push @cmd, @log_opts;
+ if (defined $r_max && $r_max == $r_min) {
push @cmd, '--max-count=1';
if (my $c = $gs->rev_db_get($r_max)) {
push @cmd, $c;
}
- } else {
+ } elsif (defined $r_max) {
my ($c_min, $c_max);
$c_max = $gs->rev_db_get($r_max);
$c_min = $gs->rev_db_get($r_min);
push @cmd, $c_min;
}
}
- return @cmd;
+ return (@cmd, @files);
}
# adapted from pager.c
}
config_pager();
- @args = (git_svn_log_cmd($r_min, $r_max, @args), @args);
+ @args = git_svn_log_cmd($r_min, $r_max, @args);
my $log = command_output_pipe(@args);
run_pager();
my (@k, $c, $d, $stat);