summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael G. Schwern <schwern@pobox.com>2012-07-28 02:38:29 -0700
committerEric Wong <normalperson@yhbt.net>2012-08-02 21:44:04 +0000
commitca475a61f8c07d475c505bf64d219f7e9d61e728 (patch)
treeb204f8e670d44dec484bd7f6af427e2fd87afc5d
parent280ad88aa0e851b2f2945222edb8e7b681a7574b (diff)
downloadgit-ca475a61f8c07d475c505bf64d219f7e9d61e728.tar.gz
git-svn: add join_paths() to safely concatenate paths
Otherwise you might wind up with things like... my $path1 = undef; my $path2 = 'foo'; my $path = $path1 . '/' . $path2; creating '/foo'. Or this... my $path1 = 'foo/'; my $path2 = 'bar'; my $path = $path1 . '/' . $path2; creating 'foo//bar'. Could have used File::Spec, but I'm shying away from it due to SVN 1.7's pickiness about paths. Felt it would be better to have our own we can control completely. [ew: commit title] Signed-off-by: Eric Wong <normalperson@yhbt.net>
-rwxr-xr-xgit-svn.perl3
-rw-r--r--perl/Git/SVN.pm10
-rw-r--r--perl/Git/SVN/Utils.pm32
-rw-r--r--t/Git-SVN/Utils/join_paths.t32
4 files changed, 72 insertions, 5 deletions
diff --git a/git-svn.perl b/git-svn.perl
index a857484c58..6e3e240473 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -34,6 +34,7 @@ use Git::SVN::Utils qw(
can_compress
canonicalize_path
canonicalize_url
+ join_paths
);
use Git qw(
@@ -1275,7 +1276,7 @@ sub get_svnprops {
$path = $cmd_dir_prefix . $path;
fatal("No such file or directory: $path") unless -e $path;
my $is_dir = -d $path ? 1 : 0;
- $path = $gs->{path} . '/' . $path;
+ $path = join_paths($gs->{path}, $path);
# canonicalize the path (otherwise libsvn will abort or fail to
# find the file)
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index fc1ac07136..ff747821fb 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -23,7 +23,11 @@ use Git qw(
command_output_pipe
command_close_pipe
);
-use Git::SVN::Utils qw(fatal can_compress);
+use Git::SVN::Utils qw(
+ fatal
+ can_compress
+ join_paths
+);
my $can_use_yaml;
BEGIN {
@@ -316,9 +320,7 @@ sub init_remote_config {
}
my $old_path = $self->path;
$url =~ s!^\Q$min_url\E(/|$)!!;
- if (length $old_path) {
- $url .= "/$old_path";
- }
+ $url = join_paths($url, $old_path);
$self->path($url);
$url = $min_url;
}
diff --git a/perl/Git/SVN/Utils.pm b/perl/Git/SVN/Utils.pm
index 4925410dd1..4005da9d7d 100644
--- a/perl/Git/SVN/Utils.pm
+++ b/perl/Git/SVN/Utils.pm
@@ -12,6 +12,7 @@ our @EXPORT_OK = qw(
can_compress
canonicalize_path
canonicalize_url
+ join_paths
);
@@ -134,4 +135,35 @@ sub _canonicalize_url_ourselves {
}
+=head3 join_paths
+
+ my $new_path = join_paths(@paths);
+
+Appends @paths together into a single path. Any empty paths are ignored.
+
+=cut
+
+sub join_paths {
+ my @paths = @_;
+
+ @paths = grep { defined $_ && length $_ } @paths;
+
+ return '' unless @paths;
+ return $paths[0] if @paths == 1;
+
+ my $new_path = shift @paths;
+ $new_path =~ s{/+$}{};
+
+ my $last_path = pop @paths;
+ $last_path =~ s{^/+}{};
+
+ for my $path (@paths) {
+ $path =~ s{^/+}{};
+ $path =~ s{/+$}{};
+ $new_path .= "/$path";
+ }
+
+ return $new_path .= "/$last_path";
+}
+
1;
diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t
new file mode 100644
index 0000000000..d4488e7162
--- /dev/null
+++ b/t/Git-SVN/Utils/join_paths.t
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils qw(
+ join_paths
+);
+
+# A reference cannot be a hash key, so we use an array.
+my @tests = (
+ [] => '',
+ ["/x.com", "bar"] => '/x.com/bar',
+ ["x.com", ""] => 'x.com',
+ ["/x.com/foo/", undef, "bar"] => '/x.com/foo/bar',
+ ["x.com/foo/", "/bar/baz/"] => 'x.com/foo/bar/baz/',
+ ["foo", "bar"] => 'foo/bar',
+ ["/foo/bar", "baz", "/biff"] => '/foo/bar/baz/biff',
+ ["", undef, "."] => '.',
+ [] => '',
+
+);
+
+while(@tests) {
+ my($have, $want) = splice @tests, 0, 2;
+
+ my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have;
+ my $name = "join_paths($args) eq '$want'";
+ is join_paths(@$have), $want, $name;
+}