setup: return correct prefix if worktree is '/'
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Sat, 26 Mar 2011 09:04:24 +0000 (16:04 +0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Mar 2011 00:01:15 +0000 (17:01 -0700)
The same old problem reappears after setup code is reworked.  We tend
to assume there is at least one path component in a path and forget
that path can be simply '/'.

Reported-by: Matthijs Kooijman <matthijs@stdin.nl>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dir.c
dir.h
setup.c

diff --git a/dir.c b/dir.c
index 570b651a17520cbb0273b9247ab0fcffc0129477..b0100f553323998e74005edacdab44b87b5ca315 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1048,6 +1048,37 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
        }
 }
 
+ * Given two normalized paths (a trailing slash is ok), if subdir is
+ * outside dir, return -1.  Otherwise return the offset in subdir that
+ * can be used as relative path to dir.
+ */
+int dir_inside_of(const char *subdir, const char *dir)
+{
+       int offset = 0;
+
+       assert(dir && subdir && *dir && *subdir);
+
+       while (*dir && *subdir && *dir == *subdir) {
+               dir++;
+               subdir++;
+               offset++;
+       }
+
+       /* hel[p]/me vs hel[l]/yeah */
+       if (*dir && *subdir)
+               return -1;
+
+       if (!*subdir)
+               return !*dir ? offset : -1; /* same dir */
+
+       /* foo/[b]ar vs foo/[] */
+       if (is_dir_sep(dir[-1]))
+               return is_dir_sep(subdir[-1]) ? offset : -1;
+
+       /* foo[/]bar vs foo[] */
+       return is_dir_sep(*subdir) ? offset + 1 : -1;
+}
+
 int is_inside_dir(const char *dir)
 {
        char buffer[PATH_MAX];
diff --git a/dir.h b/dir.h
index 72a764ed84828e4a6336d2880f2167fbc9d3143a..86a99ed9c1be3dce3434f989cde8f1501f50472a 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -83,6 +83,7 @@ extern int file_exists(const char *);
 
 extern char *get_relative_cwd(char *buffer, int size, const char *dir);
 extern int is_inside_dir(const char *dir);
+extern int dir_inside_of(const char *subdir, const char *dir);
 
 static inline int is_dot_or_dotdot(const char *name)
 {
diff --git a/setup.c b/setup.c
index dadc66659a4037b614b215b7f812c4df8969562b..f0468c6864195e0ff9532f9ec5251d5288b38397 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -322,6 +322,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        const char *worktree;
        char *gitfile;
+       int offset;
 
        if (PATH_MAX - 40 < strlen(gitdirenv))
                die("'$%s' too big", GIT_DIR_ENVIRONMENT);
@@ -387,15 +388,15 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                return NULL;
        }
 
-       if (!prefixcmp(cwd, worktree) &&
-           cwd[strlen(worktree)] == '/') { /* cwd inside worktree */
+       offset = dir_inside_of(cwd, worktree);
+       if (offset >= 0) {      /* cwd inside worktree? */
                set_git_dir(make_absolute_path(gitdirenv));
                if (chdir(worktree))
                        die_errno("Could not chdir to '%s'", worktree);
                cwd[len++] = '/';
                cwd[len] = '\0';
                free(gitfile);
-               return cwd + strlen(worktree) + 1;
+               return cwd + offset;
        }
 
        /* cwd outside worktree */