Subject: [PATCH v2 09/21] setup.c: add split-repo support to .git files

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Sat, 14 Dec 2013 17:54:55 +0700

If a .git file contains

gitsuper: <path>
gitdir: <id>

then we set GIT_SUPER_DIR to <path> and GIT_DIR to
$GIT_SUPER_DIR/repos/<id>.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 cache.h |  1 +
 setup.c | 41 +++++++++++++++++++++++++++++++++++++----
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/cache.h b/cache.h
index 823582f..f85ee70 100644
--- a/cache.h
+++ b/cache.h
@@ -410,6 +410,7 @@ extern const char *get_git_namespace(void);
 extern const char *strip_namespace(const char *namespaced_ref);
 extern const char *get_git_work_tree(void);
 extern const char *read_gitfile(const char *path);
+extern const char *read_gitfile_super(const char *path, char **super);
 extern const char *resolve_gitdir(const char *suspect);
 extern void set_git_work_tree(const char *tree);
 
diff --git a/setup.c b/setup.c
index c040981..e9f0ef6 100644
--- a/setup.c
+++ b/setup.c
@@ -299,14 +299,20 @@ static char *path_from_gitfile(const char *path, const char *buf, int len)
 /*
  * Try to read the location of the git directory from the .git file,
  * return path to git directory if found.
+ *
+ * If "gitsuper: " line is found and super is not NULL, *super points
+ * to the absolute path of the given path. The next line contains the
+ * repo id.
  */
-const char *read_gitfile(const char *path)
+const char *read_gitfile_super(const char *path, char **super)
 {
 	char *buf, *dir;
 	struct stat st;
 	int fd;
 	ssize_t len;
 
+	if (super)
+		*super = NULL;
 	if (stat(path, &st))
 		return NULL;
 	if (!S_ISREG(st.st_mode))
@@ -323,9 +329,30 @@ const char *read_gitfile(const char *path)
 		len--;
 	buf[len] = '\0';
 
-	if (prefixcmp(buf, "gitdir: "))
-		die("Invalid gitfile format: %s", path);
-	dir = path_from_gitfile(path, buf + 8, len - 8);
+	if (super && !prefixcmp(buf, "gitsuper: ")) {
+		char *p = buf, *end = buf + len;
+		while (p < end && *p != '\n' && *p != '\r')
+			p++;
+		if (p == end)
+			die("Invalid gitfile format: %s", path);
+		*super = buf + strlen("gitsuper: ");
+		*p = '\0';
+		*super = path_from_gitfile(path, *super, p - *super);
+		p++;
+		while (p < end && (*p == '\n' || *p == '\r'))
+			p++;
+		if (prefixcmp(p, "gitdir: "))
+			die("Invalid gitfile format: %s", path);
+		p += 8;
+		if (p == end)
+			die("Invalid gitfile format: %s", path);
+		dir = xmalloc(strlen(*super) + strlen("/repos/") + (end - p) + 1);
+		sprintf(dir, "%s/repos/%s", *super, p);
+	} else {
+		if (prefixcmp(buf, "gitdir: "))
+			die("Invalid gitfile format: %s", path);
+		dir = path_from_gitfile(path, buf + 8, len - 8);
+	}
 
 	if (!is_git_directory(dir))
 		die("Not a git repository: %s", dir);
@@ -336,6 +363,12 @@ const char *read_gitfile(const char *path)
 	return path;
 }
 
+const char *read_gitfile(const char *path)
+{
+	return read_gitfile_super(path, NULL);
+}
+
+
 static const char *setup_explicit_git_dir(const char *gitdirenv,
 					  char *cwd, int len,
 					  int *nongit_ok)
-- 
1.8.5.1.77.g42c48fa