setup.c: support multi-checkout repo setup
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Sun, 30 Nov 2014 08:24:44 +0000 (15:24 +0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Dec 2014 19:00:15 +0000 (11:00 -0800)
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>
Documentation/config.txt
Documentation/git-rev-parse.txt
builtin/rev-parse.c
cache.h
environment.c
setup.c
t/t1501-worktree.sh
t/t1510-repo-setup.sh
trace.c
index 922072596fdd80eaaa47746fba5e5591223c5108..e1623ec6d13b64e80263943d0ca037b8b20e7b4e 100644 (file)
@@ -393,6 +393,8 @@ false), while all other repositories are assumed to be bare (bare
 
 core.worktree::
        Set the path to the root of the working tree.
+       If GIT_COMMON_DIR environment variable is set, core.worktree
+       is ignored and not used for determining the root of working tree.
        This can be overridden by the GIT_WORK_TREE environment
        variable and the '--work-tree' command-line option.
        The value can be an absolute path or relative to the path to
index 2103bbf48a38f7f27d57258677804d2823ebfb7a..97fc703f4c0db968151b39de537fc641e5a19c32 100644 (file)
@@ -216,6 +216,9 @@ If `$GIT_DIR` is not defined and the current directory
 is not detected to lie in a Git repository or work tree
 print a message to stderr and exit with nonzero status.
 
+--git-common-dir::
+       Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
+
 --is-inside-git-dir::
        When the current working directory is below the repository
        directory print "true", otherwise "false".
index 4cd44aa45d89ea27cac509ec7279d43a9278d893..5799f3558d47ed071c1910bb349b86458ee1e361 100644 (file)
@@ -762,6 +762,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                free(cwd);
                                continue;
                        }
+                       if (!strcmp(arg, "--git-common-dir")) {
+                               puts(get_git_common_dir());
+                               continue;
+                       }
                        if (!strcmp(arg, "--resolve-git-dir")) {
                                const char *gitdir = argv[++i];
                                if (!gitdir)
diff --git a/cache.h b/cache.h
index bce1a7d44b67de5c320a60d6654b57170653f3fb..9dc6ae0b57d443e1804984f335d57c5d42992d0b 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -437,6 +437,7 @@ extern char *get_object_directory(void);
 extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern int set_git_dir(const char *path);
+extern int get_common_dir(struct strbuf *sb, const char *gitdir);
 extern const char *get_git_namespace(void);
 extern const char *strip_namespace(const char *namespaced_ref);
 extern const char *get_git_work_tree(void);
index 050f1607d531460969e2f7d49d526c4176a9679b..8351007b30c7a7a1f74866f799efb8c2e9f38cfb 100644 (file)
@@ -142,6 +142,7 @@ static char *git_path_from_env(const char *envvar, const char *git_dir,
 
 static void setup_git_env(void)
 {
+       struct strbuf sb = STRBUF_INIT;
        const char *gitfile;
        const char *shallow_file;
 
@@ -150,12 +151,9 @@ static void setup_git_env(void)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        gitfile = read_gitfile(git_dir);
        git_dir = xstrdup(gitfile ? gitfile : git_dir);
-       git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
-       if (git_common_dir) {
+       if (get_common_dir(&sb, git_dir))
                git_common_dir_env = 1;
-               git_common_dir = xstrdup(git_common_dir);
-       } else
-               git_common_dir = git_dir;
+       git_common_dir = strbuf_detach(&sb, NULL);
        git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir,
                                           "objects", &git_db_env);
        git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,
diff --git a/setup.c b/setup.c
index 1d4f1aad3911313ee25ea73ec4517fffefbb0615..b99bdd30e65dfa612a432da8cc4121126724b480 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -224,14 +224,15 @@ 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)
+int 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);
+       int ret = 0;
        if (git_common_dir) {
                strbuf_addstr(sb, git_common_dir);
-               return;
+               return 1;
        }
        strbuf_addf(&path, "%s/commondir", gitdir);
        if (file_exists(path.buf)) {
@@ -246,10 +247,12 @@ static void get_common_dir(struct strbuf *sb, const char *gitdir)
                        strbuf_addf(&path, "%s/", gitdir);
                strbuf_addbuf(&path, &data);
                strbuf_addstr(sb, real_path(path.buf));
+               ret = 1;
        } else
                strbuf_addstr(sb, gitdir);
        strbuf_release(&data);
        strbuf_release(&path);
+       return ret;
 }
 
 /*
@@ -340,13 +343,26 @@ void setup_work_tree(void)
        initialized = 1;
 }
 
+static int check_repo_format(const char *var, const char *value, void *cb)
+{
+       if (strcmp(var, "core.repositoryformatversion") == 0)
+               repository_format_version = git_config_int(var, value);
+       else if (strcmp(var, "core.sharedrepository") == 0)
+               shared_repository = git_config_perm(var, value);
+       return 0;
+}
+
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 {
        struct strbuf sb = STRBUF_INIT;
        const char *repo_config;
+       config_fn_t fn;
        int ret = 0;
 
-       get_common_dir(&sb, gitdir);
+       if (get_common_dir(&sb, gitdir))
+               fn = check_repo_format;
+       else
+               fn = check_repository_format_version;
        strbuf_addstr(&sb, "/config");
        repo_config = sb.buf;
 
@@ -359,7 +375,7 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
         * Use a gentler version of git_config() to check if this repo
         * is a good one.
         */
-       git_config_early(check_repository_format_version, NULL, repo_config);
+       git_config_early(fn, NULL, repo_config);
        if (GIT_REPO_VERSION < repository_format_version) {
                if (!nongit_ok)
                        die ("Expected git repo version <= %d, found %d",
@@ -841,11 +857,10 @@ int git_config_perm(const char *var, const char *value)
 
 int check_repository_format_version(const char *var, const char *value, void *cb)
 {
-       if (strcmp(var, "core.repositoryformatversion") == 0)
-               repository_format_version = git_config_int(var, value);
-       else if (strcmp(var, "core.sharedrepository") == 0)
-               shared_repository = git_config_perm(var, value);
-       else if (strcmp(var, "core.bare") == 0) {
+       int ret = check_repo_format(var, value, cb);
+       if (ret)
+               return ret;
+       if (strcmp(var, "core.bare") == 0) {
                is_bare_repository_cfg = git_config_bool(var, value);
                if (is_bare_repository_cfg == 1)
                        inside_work_tree = -1;
index 8f36aa9fc4d2bdd8b590e3241eccf88609db4ee2..4df7a2f212c6704d2fd52af66893bc84a9aa3cb6 100755 (executable)
@@ -346,4 +346,80 @@ test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
        test_cmp expected actual
 '
 
+test_expect_success 'Multi-worktree setup' '
+       mkdir work &&
+       mkdir -p repo.git/repos/foo &&
+       cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
+       sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'GIT_DIR set (1)' '
+       echo "gitdir: repo.git/repos/foo" >gitfile &&
+       echo ../.. >repo.git/repos/foo/commondir &&
+       (
+               cd work &&
+               GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+               test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'GIT_DIR set (2)' '
+       echo "gitdir: repo.git/repos/foo" >gitfile &&
+       echo "$(pwd)/repo.git" >repo.git/repos/foo/commondir &&
+       (
+               cd work &&
+               GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+               test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'Auto discovery' '
+       echo "gitdir: repo.git/repos/foo" >.git &&
+       echo ../.. >repo.git/repos/foo/commondir &&
+       (
+               cd work &&
+               git rev-parse --git-common-dir >actual &&
+               test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+               test_cmp expect actual &&
+               echo haha >data1 &&
+               git add data1 &&
+               git ls-files --full-name :/ | grep data1 >actual &&
+               echo work/data1 >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '$GIT_DIR/common overrides core.worktree' '
+       mkdir elsewhere &&
+       git --git-dir=repo.git config core.worktree "$TRASH_DIRECTORY/elsewhere" &&
+       echo "gitdir: repo.git/repos/foo" >.git &&
+       echo ../.. >repo.git/repos/foo/commondir &&
+       (
+               cd work &&
+               git rev-parse --git-common-dir >actual &&
+               test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+               test_cmp expect actual &&
+               echo haha >data2 &&
+               git add data2 &&
+               git ls-files --full-name :/ | grep data2 >actual &&
+               echo work/data2 >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
+       echo "gitdir: repo.git/repos/foo" >.git &&
+       echo ../.. >repo.git/repos/foo/commondir &&
+       (
+               cd work &&
+               echo haha >data3 &&
+               git --git-dir=../.git --work-tree=. add data3 &&
+               git ls-files --full-name -- :/ | grep data3 >actual &&
+               echo data3 >expect &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index e1b2a99f105f8700e72974428c126ea049247194..33c1a587b3b9f28ecc824a17650467bdc6011bc6 100755 (executable)
@@ -106,6 +106,7 @@ setup_env () {
 expect () {
        cat >"$1/expected" <<-EOF
        setup: git_dir: $2
+       setup: git_common_dir: $2
        setup: worktree: $3
        setup: cwd: $4
        setup: prefix: $5
diff --git a/trace.c b/trace.c
index 477860894196e32d7db990459cef47175277c441..bc3f53d29abf56209284248c6524961a8c1f4b5e 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -312,6 +312,7 @@ void trace_repo_setup(const char *prefix)
                prefix = "(null)";
 
        trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+       trace_printf_key(&key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_dir()));
        trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
        trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
        trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));