Merge branch 'jx/relative-path-regression-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Oct 2013 17:42:29 +0000 (10:42 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Oct 2013 17:42:30 +0000 (10:42 -0700)
* jx/relative-path-regression-fix:
Use simpler relative_path when set_git_dir
relative_path should honor dos-drive-prefix
test: use unambigous leading path (/foo) for MSYS

cache.h
path.c
setup.c
t/t0060-path-utils.sh
diff --git a/cache.h b/cache.h
index 5e3fc72fd40f49638c24c632121810468fb3acea..70ad174dd98c82503d246f8d9e5a253805e44076 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -751,6 +751,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
+const char *remove_leading_path(const char *in, const char *prefix);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
 int normalize_path_copy(char *dst, const char *src);
diff --git a/path.c b/path.c
index 9fd28bcd080ef0a64f0b8d8a5d9d680a741d75c3..24594c41120bebab1e04f5bc4bd8fc484ea71b97 100644 (file)
--- a/path.c
+++ b/path.c
@@ -434,6 +434,16 @@ int adjust_shared_perm(const char *path)
        return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+       int is_abs1, is_abs2;
+
+       is_abs1 = is_absolute_path(path1);
+       is_abs2 = is_absolute_path(path2);
+       return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) ||
+              (!is_abs1 && !is_abs2);
+}
+
 /*
  * Give path as relative to prefix.
  *
@@ -454,6 +464,16 @@ const char *relative_path(const char *in, const char *prefix,
        else if (!prefix_len)
                return in;
 
+       if (have_same_root(in, prefix)) {
+               /* bypass dos_drive, for "c:" is identical to "C:" */
+               if (has_dos_drive_prefix(in)) {
+                       i = 2;
+                       j = 2;
+               }
+       } else {
+               return in;
+       }
+
        while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
                if (is_dir_sep(prefix[i])) {
                        while (is_dir_sep(prefix[i]))
@@ -530,6 +550,51 @@ const char *relative_path(const char *in, const char *prefix,
        return sb->buf;
 }
 
+/*
+ * A simpler implementation of relative_path
+ *
+ * Get relative path by removing "prefix" from "in". This function
+ * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
+ * to increase performance when traversing the path to work_tree.
+ */
+const char *remove_leading_path(const char *in, const char *prefix)
+{
+       static char buf[PATH_MAX + 1];
+       int i = 0, j = 0;
+
+       if (!prefix || !prefix[0])
+               return in;
+       while (prefix[i]) {
+               if (is_dir_sep(prefix[i])) {
+                       if (!is_dir_sep(in[j]))
+                               return in;
+                       while (is_dir_sep(prefix[i]))
+                               i++;
+                       while (is_dir_sep(in[j]))
+                               j++;
+                       continue;
+               } else if (in[j] != prefix[i]) {
+                       return in;
+               }
+               i++;
+               j++;
+       }
+       if (
+           /* "/foo" is a prefix of "/foo" */
+           in[j] &&
+           /* "/foo" is not a prefix of "/foobar" */
+           !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
+          )
+               return in;
+       while (is_dir_sep(in[j]))
+               j++;
+       if (!in[j])
+               strcpy(buf, ".");
+       else
+               strcpy(buf, in + j);
+       return buf;
+}
+
 /*
  * It is okay if dst == src, but they should not overlap otherwise.
  *
diff --git a/setup.c b/setup.c
index f08dd649761f4d53abef66da052340a3c78d0fe5..dbf41387204766981ebda444bc56a0884f9341f5 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -227,7 +227,6 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
-       struct strbuf sb = STRBUF_INIT;
        const char *work_tree, *git_dir;
        static int initialized = 0;
 
@@ -247,10 +246,8 @@ void setup_work_tree(void)
        if (getenv(GIT_WORK_TREE_ENVIRONMENT))
                setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-       set_git_dir(relative_path(git_dir, work_tree, &sb));
+       set_git_dir(remove_leading_path(git_dir, work_tree));
        initialized = 1;
-
-       strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
index 2bd5e3274549c7a50375e90e58fa5a4dc393491d..07c10c8dca80b9d289431a72d5bf120ca442113d 100755 (executable)
@@ -190,33 +190,37 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
-relative_path /a/b/c/  /a/b/           c/
-relative_path /a/b/c/  /a/b            c/
-relative_path /a//b//c/        //a/b//         c/      POSIX
-relative_path /a/b     /a/b            ./
-relative_path /a/b/    /a/b            ./
-relative_path /a       /a/b            ../
-relative_path /                /a/b/           ../../
-relative_path /a/c     /a/b/           ../c
-relative_path /a/c     /a/b            ../c
-relative_path /x/y     /a/b/           ../../x/y
-relative_path /a/b     "<empty>"       /a/b
-relative_path /a/b     "<null>"        /a/b
-relative_path a/b/c/   a/b/            c/
-relative_path a/b/c/   a/b             c/
-relative_path a/b//c   a//b            c
-relative_path a/b/     a/b/            ./
-relative_path a/b/     a/b             ./
-relative_path a                a/b             ../
-relative_path x/y      a/b             ../../x/y
-relative_path a/c      a/b             ../c
-relative_path a/b      "<empty>"       a/b
-relative_path a/b      "<null>"        a/b
-relative_path "<empty>"        /a/b            ./
-relative_path "<empty>"        "<empty>"       ./
-relative_path "<empty>"        "<null>"        ./
-relative_path "<null>" "<empty>"       ./
-relative_path "<null>" "<null>"        ./
-relative_path "<null>" /a/b            ./
+relative_path /foo/a/b/c/      /foo/a/b/       c/
+relative_path /foo/a/b/c/      /foo/a/b        c/
+relative_path /foo/a//b//c/    ///foo/a/b//    c/              POSIX
+relative_path /foo/a/b         /foo/a/b        ./
+relative_path /foo/a/b/                /foo/a/b        ./
+relative_path /foo/a           /foo/a/b        ../
+relative_path /                        /foo/a/b/       ../../../
+relative_path /foo/a/c         /foo/a/b/       ../c
+relative_path /foo/a/c         /foo/a/b        ../c
+relative_path /foo/x/y         /foo/a/b/       ../../x/y
+relative_path /foo/a/b         "<empty>"       /foo/a/b
+relative_path /foo/a/b                 "<null>"        /foo/a/b
+relative_path foo/a/b/c/       foo/a/b/        c/
+relative_path foo/a/b/c/       foo/a/b         c/
+relative_path foo/a/b//c       foo/a//b        c
+relative_path foo/a/b/         foo/a/b/        ./
+relative_path foo/a/b/         foo/a/b         ./
+relative_path foo/a            foo/a/b         ../
+relative_path foo/x/y          foo/a/b         ../../x/y
+relative_path foo/a/c          foo/a/b         ../c
+relative_path foo/a/b          /foo/x/y        foo/a/b
+relative_path /foo/a/b         foo/x/y         /foo/a/b
+relative_path d:/a/b           D:/a/c          ../b            MINGW
+relative_path C:/a/b           D:/a/c          C:/a/b          MINGW
+relative_path foo/a/b          "<empty>"       foo/a/b
+relative_path foo/a/b          "<null>"        foo/a/b
+relative_path "<empty>"                /foo/a/b        ./
+relative_path "<empty>"                "<empty>"       ./
+relative_path "<empty>"                "<null>"        ./
+relative_path "<null>"         "<empty>"       ./
+relative_path "<null>"         "<null>"        ./
+relative_path "<null>"         /foo/a/b        ./
 
 test_done