Subject: [PATCH/POC 3/7] setup.c: add split-repo support to .git files

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 11 Dec 2013 21:15:29 +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 | 40 +++++++++++++++++++++++++++++++++-------
 2 files changed, 34 insertions(+), 7 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 5432a31..84362a6 100644
--- a/setup.c
+++ b/setup.c
@@ -281,16 +281,23 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 /*
  * 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;
+	struct strbuf sb = STRBUF_INIT;
+	char *buf, *to_free;
 	char *dir;
 	const char *slash;
 	struct stat st;
 	int fd;
 	ssize_t len;
 
+	if (super)
+		*super = NULL;
 	if (stat(path, &st))
 		return NULL;
 	if (!S_ISREG(st.st_mode))
@@ -298,12 +305,19 @@ const char *read_gitfile(const char *path)
 	fd = open(path, O_RDONLY);
 	if (fd < 0)
 		die_errno("Error opening '%s'", path);
-	buf = xmalloc(st.st_size + 1);
+	to_free = buf = xmalloc(st.st_size + 1);
 	len = read_in_full(fd, buf, st.st_size);
 	close(fd);
 	if (len != st.st_size)
 		die("Error reading %s", path);
 	buf[len] = '\0';
+	if (super &&!prefixcmp(buf, "gitsuper: ")) {
+		char *p = strchr(buf, '\n');
+		*super = buf + strlen("gitsuper: ");
+		*p = '\0';
+		len -= (p + 1) - buf;
+		buf = p + 1;
+	}
 	if (prefixcmp(buf, "gitdir: "))
 		die("Invalid gitfile format: %s", path);
 	while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
@@ -312,6 +326,11 @@ const char *read_gitfile(const char *path)
 		die("No path in gitfile: %s", path);
 	buf[len] = '\0';
 	dir = buf + 8;
+	if (super && *super) {
+		strbuf_addf(&sb, "%s/repos/%s", *super, dir);
+		dir = sb.buf;
+		*super = xstrdup(real_path(*super));
+	}
 
 	if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
 		size_t pathlen = slash+1 - path;
@@ -320,18 +339,25 @@ const char *read_gitfile(const char *path)
 		strncpy(dir, path, pathlen);
 		strncpy(dir + pathlen, buf + 8, len - 8);
 		dir[dirlen] = '\0';
-		free(buf);
-		buf = dir;
+		free(to_free);
+		to_free = buf = dir;
 	}
 
-	if (!is_git_directory(dir))
+	if (!is_git_directory_super(dir, super ? *super : dir))
 		die("Not a git repository: %s", dir);
 	path = real_path(dir);
 
-	free(buf);
+	free(to_free);
+	strbuf_release(&sb);
 	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