From: Derrick Stolee <dstolee@xxxxxxxxxxxxx> Some features, such as the sparse-checkout builtin, require using the worktree config extension. It might seem simple to upgrade the repository format and add extensions.worktreeConfig, and that is what happens in the sparse-checkout builtin. Transitioning from one config file to multiple has some strange side-effects. In particular, if the base repository is bare and the worktree is not, Git knows to treat the worktree as non-bare as a special case when not using worktree config. Once worktree config is enabled, Git stops that special case since the core.bare setting could apply at the worktree config level. This opens the door for bare worktrees. To help resolve this transition, create upgrade_to_worktree_config() to navigate the intricacies of this operation. In particular, we need to look for core.bare=true within the base config file and move that setting into the core repository's config.worktree file. To gain access to the core repository's config and config.worktree file, we reference a repository struct's 'commondir' member. If the repository was a submodule instead of a worktree, then this still applies correctly. Helped-by: Eric Sunshine <sunshine@xxxxxxxxxxxxxx> Signed-off-by: Derrick Stolee <dstolee@xxxxxxxxxxxxx> --- worktree.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ worktree.h | 12 ++++++++++++ 2 files changed, 59 insertions(+) diff --git a/worktree.c b/worktree.c index 2c155b10150..e8ddbe2bfcc 100644 --- a/worktree.c +++ b/worktree.c @@ -5,6 +5,7 @@ #include "worktree.h" #include "dir.h" #include "wt-status.h" +#include "config.h" void free_worktrees(struct worktree **worktrees) { @@ -830,3 +831,49 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, *wtpath = path; return 0; } + +int upgrade_to_worktree_config(struct repository *r) +{ + int res; + int bare = 0; + struct config_set cs = { 0 }; + char *base_config_file = xstrfmt("%s/config", r->commondir); + char *base_worktree_file = xstrfmt("%s/config.worktree", r->commondir); + + git_configset_init(&cs); + git_configset_add_file(&cs, base_config_file); + + /* + * If the base repository is bare, then we need to move core.bare=true + * out of the base config file and into the base repository's + * config.worktree file. + */ + if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare) { + if ((res = git_config_set_in_file_gently(base_worktree_file, + "core.bare", "true"))) { + error(_("unable to set core.bare=true in '%s'"), base_worktree_file); + goto cleanup; + } + + if ((res = git_config_set_in_file_gently(base_config_file, + "core.bare", NULL))) { + error(_("unable to unset core.bare=true in '%s'"), base_config_file); + goto cleanup; + } + } + if (upgrade_repository_format(r, 1) < 0) { + res = error(_("unable to upgrade repository format to enable worktreeConfig")); + goto cleanup; + } + if ((res = git_config_set_gently("extensions.worktreeConfig", "true"))) { + error(_("failed to set extensions.worktreeConfig setting")); + goto cleanup; + } + +cleanup: + git_configset_clear(&cs); + free(base_config_file); + free(base_worktree_file); + trace2_printf("returning %d", res); + return res; +} diff --git a/worktree.h b/worktree.h index 8b7c408132d..170b6b1e1f5 100644 --- a/worktree.h +++ b/worktree.h @@ -182,4 +182,16 @@ void strbuf_worktree_ref(const struct worktree *wt, struct strbuf *sb, const char *refname); +/** + * Upgrade the config of the current repository and its base (if different + * from this repository) to use worktree-config. This might adjust config + * in both repositories, including: + * + * 1. Upgrading the repository format version to 1. + * 2. Adding extensions.worktreeConfig to the base config file. + * 3. Moving core.bare=true from the base config file to the base + * repository's config.worktree file. + */ +int upgrade_to_worktree_config(struct repository *r); + #endif -- gitgitgadget