Subject: [PATCH/POC 7/7] init: add --split-repo with the same functionality as git-new-workdir

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 11 Dec 2013 21:15:33 +0700

"git init --split-repo abc" will create abc/.git file with the content

gitsuper: `git rev-parse --git-dir`
gitdir: abc

and a new directory in current .git/repos/abc with a valid HEAD.

This is enough to start checking out and do stuff. We should probably
take a branch name and check that branch out though. If that's the
case, this feature may better be parked in "git checkout" instead of
here..

So far it's not any better than git-new-workdir, except that info from
all worktrees created this way is centralized in the super repo's
.git/repos, which makes it possible to ensure what worktree does not
step on one another. In particular:

 - If a worktree updates a ref, we could check if any other worktrees
   are also checking out that ref. Detach those worktrees.

 - prune/gc should be taught about the extra HEADs in .git/repos. fsck
   on the super repo should be taught about extra indexes in
   .git/repos

 - ...

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 builtin/init-db.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/builtin/init-db.c b/builtin/init-db.c
index 78aa387..9c7139a 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -426,6 +426,38 @@ int init_db(const char *template_dir, unsigned int flags)
 	return 0;
 }
 
+static int create_new_worktree(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *name;
+	unsigned char sha1[20];
+	FILE *fp;
+
+	for (name = path + strlen(path) - 1; name > path; name--)
+		if (is_dir_sep(*name)) {
+			name++;
+			break;
+		}
+
+	strbuf_addf(&sb, "%s/.git", path);
+	safe_create_leading_directories_const(sb.buf);
+	fp = fopen(sb.buf, "w");
+	fprintf(fp, "gitsuper: %s\ngitdir: %s\n",
+		real_path(get_git_dir()), name);
+	fclose(fp);
+	safe_create_leading_directories_const(git_path("repos/%s/HEAD",
+						       name));
+	fp = fopen(git_path("repos/%s/HEAD", name), "w");
+	get_sha1("HEAD", sha1);
+	fprintf(fp, "%s\n", sha1_to_hex(sha1));
+	fclose(fp);
+#if 0
+	chdir(path);
+	system("git reset --hard");
+#endif
+	return 0;
+}
+
 static int guess_repository_type(const char *git_dir)
 {
 	char cwd[PATH_MAX];
@@ -481,6 +513,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	const char *work_tree;
 	const char *template_dir = NULL;
 	unsigned int flags = 0;
+	int split_repo = 0;
 	const struct option init_db_options[] = {
 		OPT_STRING(0, "template", &template_dir, N_("template-directory"),
 				N_("directory from which templates will be used")),
@@ -491,6 +524,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 			N_("specify that the git repository is to be shared amongst several users"),
 			PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
 		OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
+		OPT_BOOL(0, "split-repo", &split_repo, N_("git-new-workdir")),
 		OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
 			   N_("separate git dir from working tree")),
 		OPT_END()
@@ -498,6 +532,14 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
 
+	if (split_repo) {
+		if (real_git_dir)
+			die(_("--split-repo and --separate-git-dir are incompatible"));
+		if (!argv[0])
+			die(_("--split-repo requires a path"));
+		return create_new_worktree(argv[0]);
+	}
+
 	if (real_git_dir && !is_absolute_path(real_git_dir))
 		real_git_dir = xstrdup(real_path(real_git_dir));
 
-- 
1.8.5.1.77.g42c48fa