setup.c: support multi-checkout repo setup

The repo setup procedure is updated to detect $GIT_DIR/commondir and set $GIT_COMMON_DIR properly. The core.worktree is ignored when $GIT_COMMON_DIR is set. This is because the config file is shared in multi-checkout setup, but checkout directories _are_ different. Making core.worktree effective in all checkouts mean it's back to a single checkout. Helped-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Nov 30, 2014 at 15:24 UTC 31e26ebcb512ad234d9725120fb4f5670ff75625
9 files changed +115 -14
Documentation/config.txt
+2
@@ -393,6 +393,8 @@ false), while all other repositories are assumed to be bare (bare
393
394 core.worktree::
395 Set the path to the root of the working tree.
396 + If GIT_COMMON_DIR environment variable is set, core.worktree
397 + is ignored and not used for determining the root of working tree.
398 This can be overridden by the GIT_WORK_TREE environment
399 variable and the '--work-tree' command-line option.
400 The value can be an absolute path or relative to the path to
Documentation/git-rev-parse.txt
+3
@@ -216,6 +216,9 @@ If `$GIT_DIR` is not defined and the current directory
216 is not detected to lie in a Git repository or work tree
217 print a message to stderr and exit with nonzero status.
218
219 +--git-common-dir::
220 + Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
221 +
222 --is-inside-git-dir::
223 When the current working directory is below the repository
224 directory print "true", otherwise "false".
builtin/rev-parse.c
+4
@@ -762,6 +762,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
762 free(cwd);
763 continue;
764 }
765 + if (!strcmp(arg, "--git-common-dir")) {
766 + puts(get_git_common_dir());
767 + continue;
768 + }
769 if (!strcmp(arg, "--resolve-git-dir")) {
770 const char *gitdir = argv[++i];
771 if (!gitdir)
cache.h
+1
@@ -437,6 +437,7 @@ extern char *get_object_directory(void);
437 extern char *get_index_file(void);
438 extern char *get_graft_file(void);
439 extern int set_git_dir(const char *path);
440 +extern int get_common_dir(struct strbuf *sb, const char *gitdir);
441 extern const char *get_git_namespace(void);
442 extern const char *strip_namespace(const char *namespaced_ref);
443 extern const char *get_git_work_tree(void);
environment.c
+3 -5
@@ -142,6 +142,7 @@ static char *git_path_from_env(const char *envvar, const char *git_dir,
142
143 static void setup_git_env(void)
144 {
145 + struct strbuf sb = STRBUF_INIT;
146 const char *gitfile;
147 const char *shallow_file;
148
@@ -150,12 +151,9 @@ static void setup_git_env(void)
151 git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
152 gitfile = read_gitfile(git_dir);
153 git_dir = xstrdup(gitfile ? gitfile : git_dir);
153 - git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
154 - if (git_common_dir) {
154 + if (get_common_dir(&sb, git_dir))
155 git_common_dir_env = 1;
156 - git_common_dir = xstrdup(git_common_dir);
157 - } else
158 - git_common_dir = git_dir;
156 + git_common_dir = strbuf_detach(&sb, NULL);
157 git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir,
158 "objects", &git_db_env);
159 git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,
setup.c
+24 -9
@@ -224,14 +224,15 @@ void verify_non_filename(const char *prefix, const char *arg)
224 "'git <command> [<revision>...] -- [<file>...]'", arg);
225 }
226
227 -static void get_common_dir(struct strbuf *sb, const char *gitdir)
227 +int get_common_dir(struct strbuf *sb, const char *gitdir)
228 {
229 struct strbuf data = STRBUF_INIT;
230 struct strbuf path = STRBUF_INIT;
231 const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
232 + int ret = 0;
233 if (git_common_dir) {
234 strbuf_addstr(sb, git_common_dir);
234 - return;
235 + return 1;
236 }
237 strbuf_addf(&path, "%s/commondir", gitdir);
238 if (file_exists(path.buf)) {
@@ -246,10 +247,12 @@ static void get_common_dir(struct strbuf *sb, const char *gitdir)
247 strbuf_addf(&path, "%s/", gitdir);
248 strbuf_addbuf(&path, &data);
249 strbuf_addstr(sb, real_path(path.buf));
250 + ret = 1;
251 } else
252 strbuf_addstr(sb, gitdir);
253 strbuf_release(&data);
254 strbuf_release(&path);
255 + return ret;
256 }
257
258 /*
@@ -340,13 +343,26 @@ void setup_work_tree(void)
343 initialized = 1;
344 }
345
346 +static int check_repo_format(const char *var, const char *value, void *cb)
347 +{
348 + if (strcmp(var, "core.repositoryformatversion") == 0)
349 + repository_format_version = git_config_int(var, value);
350 + else if (strcmp(var, "core.sharedrepository") == 0)
351 + shared_repository = git_config_perm(var, value);
352 + return 0;
353 +}
354 +
355 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
356 {
357 struct strbuf sb = STRBUF_INIT;
358 const char *repo_config;
359 + config_fn_t fn;
360 int ret = 0;
361
349 - get_common_dir(&sb, gitdir);
362 + if (get_common_dir(&sb, gitdir))
363 + fn = check_repo_format;
364 + else
365 + fn = check_repository_format_version;
366 strbuf_addstr(&sb, "/config");
367 repo_config = sb.buf;
368
@@ -359,7 +375,7 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
375 * Use a gentler version of git_config() to check if this repo
376 * is a good one.
377 */
362 - git_config_early(check_repository_format_version, NULL, repo_config);
378 + git_config_early(fn, NULL, repo_config);
379 if (GIT_REPO_VERSION < repository_format_version) {
380 if (!nongit_ok)
381 die ("Expected git repo version <= %d, found %d",
@@ -841,11 +857,10 @@ int git_config_perm(const char *var, const char *value)
857
858 int check_repository_format_version(const char *var, const char *value, void *cb)
859 {
844 - if (strcmp(var, "core.repositoryformatversion") == 0)
845 - repository_format_version = git_config_int(var, value);
846 - else if (strcmp(var, "core.sharedrepository") == 0)
847 - shared_repository = git_config_perm(var, value);
848 - else if (strcmp(var, "core.bare") == 0) {
860 + int ret = check_repo_format(var, value, cb);
861 + if (ret)
862 + return ret;
863 + if (strcmp(var, "core.bare") == 0) {
864 is_bare_repository_cfg = git_config_bool(var, value);
865 if (is_bare_repository_cfg == 1)
866 inside_work_tree = -1;
t/t1501-worktree.sh
+76
@@ -346,4 +346,80 @@ test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
346 test_cmp expected actual
347 '
348
349 +test_expect_success 'Multi-worktree setup' '
350 + mkdir work &&
351 + mkdir -p repo.git/repos/foo &&
352 + cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
353 + sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
354 +'
355 +
356 +test_expect_success 'GIT_DIR set (1)' '
357 + echo "gitdir: repo.git/repos/foo" >gitfile &&
358 + echo ../.. >repo.git/repos/foo/commondir &&
359 + (
360 + cd work &&
361 + GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
362 + test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
363 + test_cmp expect actual
364 + )
365 +'
366 +
367 +test_expect_success 'GIT_DIR set (2)' '
368 + echo "gitdir: repo.git/repos/foo" >gitfile &&
369 + echo "$(pwd)/repo.git" >repo.git/repos/foo/commondir &&
370 + (
371 + cd work &&
372 + GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
373 + test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
374 + test_cmp expect actual
375 + )
376 +'
377 +
378 +test_expect_success 'Auto discovery' '
379 + echo "gitdir: repo.git/repos/foo" >.git &&
380 + echo ../.. >repo.git/repos/foo/commondir &&
381 + (
382 + cd work &&
383 + git rev-parse --git-common-dir >actual &&
384 + test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
385 + test_cmp expect actual &&
386 + echo haha >data1 &&
387 + git add data1 &&
388 + git ls-files --full-name :/ | grep data1 >actual &&
389 + echo work/data1 >expect &&
390 + test_cmp expect actual
391 + )
392 +'
393 +
394 +test_expect_success '$GIT_DIR/common overrides core.worktree' '
395 + mkdir elsewhere &&
396 + git --git-dir=repo.git config core.worktree "$TRASH_DIRECTORY/elsewhere" &&
397 + echo "gitdir: repo.git/repos/foo" >.git &&
398 + echo ../.. >repo.git/repos/foo/commondir &&
399 + (
400 + cd work &&
401 + git rev-parse --git-common-dir >actual &&
402 + test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
403 + test_cmp expect actual &&
404 + echo haha >data2 &&
405 + git add data2 &&
406 + git ls-files --full-name :/ | grep data2 >actual &&
407 + echo work/data2 >expect &&
408 + test_cmp expect actual
409 + )
410 +'
411 +
412 +test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
413 + echo "gitdir: repo.git/repos/foo" >.git &&
414 + echo ../.. >repo.git/repos/foo/commondir &&
415 + (
416 + cd work &&
417 + echo haha >data3 &&
418 + git --git-dir=../.git --work-tree=. add data3 &&
419 + git ls-files --full-name -- :/ | grep data3 >actual &&
420 + echo data3 >expect &&
421 + test_cmp expect actual
422 + )
423 +'
424 +
425 test_done
t/t1510-repo-setup.sh
+1
@@ -106,6 +106,7 @@ setup_env () {
106 expect () {
107 cat >"$1/expected" <<-EOF
108 setup: git_dir: $2
109 + setup: git_common_dir: $2
110 setup: worktree: $3
111 setup: cwd: $4
112 setup: prefix: $5
trace.c
+1
@@ -312,6 +312,7 @@ void trace_repo_setup(const char *prefix)
312 prefix = "(null)";
313
314 trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
315 + trace_printf_key(&key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_dir()));
316 trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
317 trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
318 trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));