Subject: [PATCH 1/2 v2] setup: return correct prefix if worktree is '/'

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Sat, 26 Mar 2011 16:04:24 +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 <[email-masked]>
Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 It just looks better than previous version.

 dir.c   |   31 +++++++++++++++++++++++++++++++
 dir.h   |    1 +
 setup.c |    7 ++++---
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/dir.c b/dir.c
index 325fb56..6c49a73 100644
--- a/dir.c
+++ b/dir.c
@@ -1152,6 +1152,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 aa511da..83e2992 100644
--- a/dir.h
+++ b/dir.h
@@ -87,6 +87,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 03cd84f..b6e6b5a 100644
--- a/setup.c
+++ b/setup.c
@@ -325,6 +325,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);
@@ -390,15 +391,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(real_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 */
-- 
1.7.4.74.g639db