Merge branch 'mw/symlinks' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 28 May 2014 22:45:57 +0000 (15:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 28 May 2014 22:45:57 +0000 (15:45 -0700)
* mw/symlinks:
setup: fix windows path buffer over-stepping
setup: don't dereference in-tree symlinks for absolute paths
setup: add abspath_part_inside_repo() function
t0060: add tests for prefix_path when path begins with work tree
t0060: add test for prefix_path when path == work tree
t0060: add test for prefix_path on symlinks via absolute paths
t3004: add test for ls-files on symlinks via absolute paths

setup.c
t/t0060-path-utils.sh
t/t3004-ls-files-basic.sh
diff --git a/setup.c b/setup.c
index 6c3f85ff7a7935ed6f05ab5ab376a2d6c5ca2d46..5e553060830132dcb912d19cea5d88e699cb7230 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -5,6 +5,70 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
+/*
+ * The input parameter must contain an absolute path, and it must already be
+ * normalized.
+ *
+ * Find the part of an absolute path that lies inside the work tree by
+ * dereferencing symlinks outside the work tree, for example:
+ * /dir1/repo/dir2/file   (work tree is /dir1/repo)      -> dir2/file
+ * /dir/file              (work tree is /)               -> dir/file
+ * /dir/symlink1/symlink2 (symlink1 points to work tree) -> symlink2
+ * /dir/repolink/file     (repolink points to /dir/repo) -> file
+ * /dir/repo              (exactly equal to work tree)   -> (empty string)
+ */
+static int abspath_part_inside_repo(char *path)
+{
+       size_t len;
+       size_t wtlen;
+       char *path0;
+       int off;
+       const char *work_tree = get_git_work_tree();
+
+       if (!work_tree)
+               return -1;
+       wtlen = strlen(work_tree);
+       len = strlen(path);
+       off = offset_1st_component(path);
+
+       /* check if work tree is already the prefix */
+       if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
+               if (path[wtlen] == '/') {
+                       memmove(path, path + wtlen + 1, len - wtlen);
+                       return 0;
+               } else if (path[wtlen - 1] == '/' || path[wtlen] == '\0') {
+                       /* work tree is the root, or the whole path */
+                       memmove(path, path + wtlen, len - wtlen + 1);
+                       return 0;
+               }
+               /* work tree might match beginning of a symlink to work tree */
+               off = wtlen;
+       }
+       path0 = path;
+       path += off;
+
+       /* check each '/'-terminated level */
+       while (*path) {
+               path++;
+               if (*path == '/') {
+                       *path = '\0';
+                       if (strcmp(real_path(path0), work_tree) == 0) {
+                               memmove(path0, path + 1, len - (path - path0));
+                               return 0;
+                       }
+                       *path = '/';
+               }
+       }
+
+       /* check whole path */
+       if (strcmp(real_path(path0), work_tree) == 0) {
+               *path0 = '\0';
+               return 0;
+       }
+
+       return -1;
+}
+
 /*
  * Normalize "path", prepending the "prefix" for relative paths. If
  * remaining_prefix is not NULL, return the actual prefix still
@@ -22,11 +86,17 @@ char *prefix_path_gently(const char *prefix, int len,
        const char *orig = path;
        char *sanitized;
        if (is_absolute_path(orig)) {
-               const char *temp = real_path(path);
-               sanitized = xmalloc(len + strlen(temp) + 1);
-               strcpy(sanitized, temp);
+               sanitized = xmalloc(strlen(path) + 1);
                if (remaining_prefix)
                        *remaining_prefix = 0;
+               if (normalize_path_copy_len(sanitized, path, remaining_prefix)) {
+                       free(sanitized);
+                       return NULL;
+               }
+               if (abspath_part_inside_repo(sanitized)) {
+                       free(sanitized);
+                       return NULL;
+               }
        } else {
                sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
@@ -34,26 +104,10 @@ char *prefix_path_gently(const char *prefix, int len,
                strcpy(sanitized + len, path);
                if (remaining_prefix)
                        *remaining_prefix = len;
-       }
-       if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
-               goto error_out;
-       if (is_absolute_path(orig)) {
-               size_t root_len, len, total;
-               const char *work_tree = get_git_work_tree();
-               if (!work_tree)
-                       goto error_out;
-               len = strlen(work_tree);
-               root_len = offset_1st_component(work_tree);
-               total = strlen(sanitized) + 1;
-               if (strncmp(sanitized, work_tree, len) ||
-                   (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
-               error_out:
+               if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
                        free(sanitized);
                        return NULL;
                }
-               if (sanitized[len] == '/')
-                       len++;
-               memmove(sanitized, sanitized + len, total - len);
        }
        return sanitized;
 }
index 07c10c8dca80b9d289431a72d5bf120ca442113d..c0143a0a70b7d3dc213b691aecea6b3fffbc5844 100755 (executable)
@@ -190,6 +190,27 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
+test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
+       ln -s target symlink &&
+       test "$(test-path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
+'
+
+test_expect_success 'prefix_path works with only absolute path to work tree' '
+       echo "" >expected &&
+       test-path-utils prefix_path prefix "$(pwd)" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'prefix_path rejects absolute path to dir with same beginning as work tree' '
+       test_must_fail test-path-utils prefix_path prefix "$(pwd)a"
+'
+
+test_expect_success SYMLINKS 'prefix_path works with absolute path to a symlink to work tree having  same beginning as work tree' '
+       git init repo &&
+       ln -s repo repolink &&
+       test "a" = "$(cd repo && test-path-utils prefix_path prefix "$(pwd)/../repolink/a")"
+'
+
 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
index 8d9bc3c2af6ad84c006010ee7be65146107c2259..9c7adbdbe1cd7bf03e9b8f10a75f0acb4f37a5b2 100755 (executable)
@@ -36,4 +36,21 @@ test_expect_success 'ls-files -h in corrupt repository' '
        test_i18ngrep "[Uu]sage: git ls-files " broken/usage
 '
 
+test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
+       mkdir subs &&
+       ln -s nosuch link &&
+       ln -s ../nosuch subs/link &&
+       git add link subs/link &&
+       git ls-files -s link subs/link >expect &&
+       git ls-files -s "$(pwd)/link" "$(pwd)/subs/link" >actual &&
+       test_cmp expect actual &&
+
+       (
+               cd subs &&
+               git ls-files -s link >../expect &&
+               git ls-files -s "$(pwd)/link" >../actual
+       ) &&
+       test_cmp expect actual
+'
+
 test_done