git-svn: add join_paths() to safely concatenate paths
authorMichael G. Schwern <schwern@pobox.com>
Sat, 28 Jul 2012 09:38:29 +0000 (02:38 -0700)
committerEric Wong <normalperson@yhbt.net>
Thu, 2 Aug 2012 21:44:04 +0000 (21:44 +0000)
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>
git-svn.perl
perl/Git/SVN.pm
perl/Git/SVN/Utils.pm
t/Git-SVN/Utils/join_paths.t [new file with mode: 0644]
index a857484c582e4010720f6fdd25370b68855e77ba..6e3e2404735bab306c0786e5d5b28454cb80c7bc 100755 (executable)
@@ -34,6 +34,7 @@
        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)
index fc1ac07136af343ba2a373163870b195cea080d4..ff747821fb637c586ea0a67b5aff01f7be26b9e3 100644 (file)
@@ -23,7 +23,11 @@ package Git::SVN;
     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;
                }
index 4925410dd1ebed4c279a124a25557fd4c767cd83..4005da9d7d08c4119c6a0b39c7bb6eb716581518 100644 (file)
@@ -12,6 +12,7 @@ package Git::SVN::Utils;
        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 (file)
index 0000000..d4488e7
--- /dev/null
@@ -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;
+}