Worktrees present an interesting problem when it comes to the config. Historically we could assume that the per-repository config lives at 'gitdir/config', but since worktrees were introduced this isn't the case anymore. There is currently no way to specify per-worktree configuration, and as such the repository config is shared with all worktrees and is located at 'commondir/config'.
Many users of the config machinery correctly set 'config_options.git_dir' with the repository's commondir, allowing the config to be properly loaded when operating in a worktree. But other's, like 'read_early_config()', set 'config_options.git_dir' with the repository's gitdir which can be incorrect when using worktrees. To fix this issue, and to make things less ambiguous, lets add a 'commondir' field to the 'config_options' struct and have all callers properly set both the 'git_dir' and 'commondir' fields so that the config machinery is able to properly find the repository's config. Signed-off-by: Brandon Williams <bmw...@google.com> --- builtin/config.c | 6 ++++-- config.c | 18 ++++++++++++------ config.h | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index f06a8dff2..8b6e227c5 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -539,8 +539,10 @@ int cmd_config(int argc, const char **argv, const char *prefix) config_options.respect_includes = !given_config_source.file; else config_options.respect_includes = respect_includes_opt; - if (!nongit) - config_options.git_dir = get_git_common_dir(); + if (!nongit) { + config_options.commondir = get_git_common_dir(); + config_options.git_dir = get_git_dir(); + } if (end_null) { term = '\0'; diff --git a/config.c b/config.c index 9aa9b9715..81151fb54 100644 --- a/config.c +++ b/config.c @@ -1544,8 +1544,8 @@ static int do_git_config_sequence(const struct config_options *opts, char *user_config = expand_user_path("~/.gitconfig", 0); char *repo_config; - if (opts->git_dir) - repo_config = mkpathdup("%s/config", opts->git_dir); + if (opts->commondir) + repo_config = mkpathdup("%s/config", opts->commondir); else repo_config = NULL; @@ -1609,8 +1609,11 @@ static void git_config_raw(config_fn_t fn, void *data) struct config_options opts = {0}; opts.respect_includes = 1; - if (have_git_dir()) - opts.git_dir = get_git_common_dir(); + if (have_git_dir()) { + opts.commondir = get_git_common_dir(); + opts.git_dir = get_git_dir(); + } + if (git_config_with_options(fn, data, NULL, &opts) < 0) /* * git_config_with_options() normally returns only @@ -1657,7 +1660,8 @@ void read_early_config(config_fn_t cb, void *data) opts.respect_includes = 1; - if (have_git_dir()) + if (have_git_dir()) { + opts.commondir = get_git_common_dir(); opts.git_dir = get_git_dir(); /* * When setup_git_directory() was not yet asked to discover the @@ -1667,8 +1671,10 @@ void read_early_config(config_fn_t cb, void *data) * notably, the current working directory is still the same after the * call). */ - else if (discover_git_directory(&commondir, &gitdir)) + } else if (discover_git_directory(&commondir, &gitdir)) { + opts.commondir = commondir.buf; opts.git_dir = gitdir.buf; + } git_config_with_options(cb, data, NULL, &opts); diff --git a/config.h b/config.h index c70599bd5..63b92784c 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,7 @@ enum config_origin_type { struct config_options { unsigned int respect_includes : 1; + const char *commondir; const char *git_dir; }; -- 2.13.1.518.g3df882009-goog