]> granicus.if.org Git - git/commitdiff
config: handle conditional include when $GIT_DIR is not set up
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Mon, 17 Apr 2017 10:10:01 +0000 (17:10 +0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 18 Apr 2017 02:18:43 +0000 (19:18 -0700)
If setup_git_directory() and friends have not been called,
get_git_dir() (because of includeIf.gitdir:XXX) would lead to

    die("BUG: setup_git_env called without repository");

There are two cases when a config file could be read before $GIT_DIR
is located.

The first one is check_repository_format(), where we read just the one
file $GIT_DIR/config to check if we could understand this
repository. This case should be safe. We do not parse include
directives, which can only be triggered from git_config_with_options,
but setup code uses a lower-level function. The concerned variables
should never be hidden away behind includes anyway.

The second one is triggered in check_pager_config() when we're about
to run an external git command. We might be able to find $GIT_DIR in
this case, which is exactly what read_early_config() does (and also is
what check_pager_config() uses). Conditional includes and
get_git_dir() could be triggered by the first
git_config_with_options() call there, before discover_git_directory()
is used as a fallback $GIT_DIR detection.

Detect this special "early reading" case, pass down the $GIT_DIR,
either from previous setup or detected by discover_git_directory(),
and make conditional include use it.

Noticed-by: Bert Wesarg <bert.wesarg@googlemail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
cache.h
config.c
t/t1305-config-include.sh

diff --git a/cache.h b/cache.h
index a6294d257375ac8ee1fe04d6ca7c1c37f4cdc511..878e1d441f9a319f6608908a03397c087084890e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1887,6 +1887,7 @@ enum config_origin_type {
 
 struct config_options {
        unsigned int respect_includes : 1;
+       const char *git_dir;
 };
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
index 042321a3a0a3d6441ce2f34b89da47ea96a2a974..14f041746027ee18ac2959434e26f8501e3582da 100644 (file)
--- a/config.c
+++ b/config.c
@@ -207,13 +207,22 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        return prefix;
 }
 
-static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
+static int include_by_gitdir(const struct config_options *opts,
+                            const char *cond, size_t cond_len, int icase)
 {
        struct strbuf text = STRBUF_INIT;
        struct strbuf pattern = STRBUF_INIT;
        int ret = 0, prefix;
+       const char *git_dir;
 
-       strbuf_add_absolute_path(&text, get_git_dir());
+       if (opts->git_dir)
+               git_dir = opts->git_dir;
+       else if (have_git_dir())
+               git_dir = get_git_dir();
+       else
+               goto done;
+
+       strbuf_add_absolute_path(&text, git_dir);
        strbuf_add(&pattern, cond, cond_len);
        prefix = prepare_include_condition_pattern(&pattern);
 
@@ -242,13 +251,14 @@ done:
        return ret;
 }
 
-static int include_condition_is_true(const char *cond, size_t cond_len)
+static int include_condition_is_true(const struct config_options *opts,
+                                    const char *cond, size_t cond_len)
 {
 
        if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
-               return include_by_gitdir(cond, cond_len, 0);
+               return include_by_gitdir(opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
-               return include_by_gitdir(cond, cond_len, 1);
+               return include_by_gitdir(opts, cond, cond_len, 1);
 
        /* unknown conditionals are always false */
        return 0;
@@ -273,7 +283,7 @@ int git_config_include(const char *var, const char *value, void *data)
                ret = handle_path_include(value, inc);
 
        if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
-           (cond && include_condition_is_true(cond, cond_len)) &&
+           (cond && include_condition_is_true(inc->opts, cond, cond_len)) &&
            !strcmp(key, "path"))
                ret = handle_path_include(value, inc);
 
@@ -1603,10 +1613,12 @@ void read_early_config(config_fn_t cb, void *data)
 {
        struct config_options opts = {0};
        struct strbuf buf = STRBUF_INIT;
+       char *to_free = NULL;
 
        opts.respect_includes = 1;
-       git_config_with_options(cb, data, NULL, &opts);
 
+       if (have_git_dir())
+               opts.git_dir = get_git_dir();
        /*
         * When setup_git_directory() was not yet asked to discover the
         * GIT_DIR, we ask discover_git_directory() to figure out whether there
@@ -1615,7 +1627,12 @@ void read_early_config(config_fn_t cb, void *data)
         * notably, the current working directory is still the same after the
         * call).
         */
-       if (!have_git_dir() && discover_git_directory(&buf)) {
+       else if (discover_git_directory(&buf))
+               opts.git_dir = to_free = xstrdup(buf.buf);
+
+       git_config_with_options(cb, data, NULL, &opts);
+
+       if (!have_git_dir() && opts.git_dir) {
                struct git_config_source repo_config;
 
                memset(&repo_config, 0, sizeof(repo_config));
@@ -1624,6 +1641,7 @@ void read_early_config(config_fn_t cb, void *data)
                git_config_with_options(cb, data, &repo_config, &opts);
        }
        strbuf_release(&buf);
+       free(to_free);
 }
 
 static void git_config_check_init(void);
index e8339393203c65fac29a647c5a0f827cf24a4cbb..fddb47bafa62d967e9964c65363afc42c848397d 100755 (executable)
@@ -208,6 +208,17 @@ test_expect_success 'conditional include, both unanchored, icase' '
        )
 '
 
+test_expect_success 'conditional include, early config reading' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:foo/\"]path=bar6" >>.git/config &&
+               echo "[test]six=6" >.git/bar6 &&
+               echo 6 >expect &&
+               test-config read_early_config test.six >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'include cycles are detected' '
        cat >.gitconfig <<-\EOF &&
        [test]value = gitconfig