Subject: [PATCH v3 15/25] setup.c: detect $GIT_COMMON_DIR in is_git_directory()

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Tue, 18 Feb 2014 20:40:04 +0700

If the file "$GIT_DIR/commondir" exists, it contains the value of
$GIT_COMMON_DIR.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 Documentation/gitrepository-layout.txt |  4 ++++
 setup.c                                | 38 ++++++++++++++++++++++++++++------
 strbuf.c                               |  8 +++++++
 strbuf.h                               |  1 +
 4 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index aa03882..9bfe0f1 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -211,6 +211,10 @@ shallow::
 	and maintained by shallow clone mechanism.  See `--depth`
 	option to linkgit:git-clone[1] and linkgit:git-fetch[1].
 
+commondir::
+	If this file exists, $GIT_COMMON_DIR will be set to the path
+	specified in this file if it is not set.
+
 modules::
 	Contains the git-repositories of the submodules.
 
diff --git a/setup.c b/setup.c
index 999225b..4e5711c 100644
--- a/setup.c
+++ b/setup.c
@@ -170,6 +170,30 @@ void verify_non_filename(const char *prefix, const char *arg)
 	    "'git <command> [<revision>...] -- [<file>...]'", arg);
 }
 
+static void get_common_dir(struct strbuf *sb, const char *gitdir)
+{
+	struct strbuf data = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+	if (git_common_dir) {
+		strbuf_addstr(sb, git_common_dir);
+		return;
+	}
+	strbuf_addf(&path, "%s/commondir", gitdir);
+	if (file_exists(path.buf)) {
+		if (strbuf_read_file(&data, path.buf, 0) <= 0)
+			die_errno(_("failed to read %s"), path.buf);
+		strbuf_chomp(&data);
+		strbuf_reset(&path);
+		if (!is_absolute_path(data.buf))
+			strbuf_addf(&path, "%s/", gitdir);
+		strbuf_addbuf(&path, &data);
+		strbuf_addstr(sb, real_path(path.buf));
+	} else
+		strbuf_addstr(sb, gitdir);
+	strbuf_release(&data);
+	strbuf_release(&path);
+}
 
 /*
  * Test if it looks like we're at a git directory.
@@ -188,14 +212,20 @@ int is_git_directory(const char *suspect)
 	int ret = 0;
 	size_t len;
 
-	strbuf_addstr(&path, suspect);
+	strbuf_addf(&path, "%s/HEAD", suspect);
+	if (validate_headref(path.buf))
+		goto done;
+
+	strbuf_reset(&path);
+	get_common_dir(&path, suspect);
 	len = path.len;
+
 	if (getenv(DB_ENVIRONMENT)) {
 		if (access(getenv(DB_ENVIRONMENT), X_OK))
 			goto done;
 	}
 	else {
-		strbuf_addstr(&path, "/objects");
+		strbuf_addstr_at(&path, len, "/objects");
 		if (access(path.buf, X_OK))
 			goto done;
 	}
@@ -204,10 +234,6 @@ int is_git_directory(const char *suspect)
 	if (access(path.buf, X_OK))
 		goto done;
 
-	strbuf_addstr_at(&path, len, "/HEAD");
-	if (validate_headref(path.buf))
-		goto done;
-
 	ret = 1;
 done:
 	strbuf_release(&path);
diff --git a/strbuf.c b/strbuf.c
index 83caf4a..e17c358 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -588,3 +588,11 @@ int fprintf_ln(FILE *fp, const char *fmt, ...)
 		return -1;
 	return ret + 1;
 }
+
+void strbuf_chomp(struct strbuf *sb)
+{
+	while (sb->len && (sb->buf[sb->len - 1] == '\n' ||
+			   sb->buf[sb->len - 1] == '\r'))
+		sb->len--;
+	sb->buf[sb->len] = '\0';
+}
diff --git a/strbuf.h b/strbuf.h
index aec9fdb..cd9578f 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -109,6 +109,7 @@ extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
 /* splice pos..pos+len with given data */
 extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
                           const void *, size_t);
+extern void strbuf_chomp(struct strbuf *sb);
 
 extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size);
 
-- 
1.8.5.2.240.g8478abd