diff --git a/chdir-notify.c b/chdir-notify.c index f8bfe3cbef..1237a45e2e 100644 --- a/chdir-notify.c +++ b/chdir-notify.c @@ -43,32 +43,6 @@ void chdir_notify_unregister(const char *name, chdir_notify_callback cb, } } -static void reparent_cb(const char *name, - const char *old_cwd, - const char *new_cwd, - void *data) -{ - char **path = data; - char *tmp = *path; - - if (!tmp) - return; - - *path = reparent_relative_path(old_cwd, new_cwd, tmp); - free(tmp); - - if (name) { - trace_printf_key(&trace_setup_key, - "setup: reparent %s to '%s'", - name, *path); - } -} - -void chdir_notify_reparent(const char *name, char **path) -{ - chdir_notify_register(name, reparent_cb, path); -} - int chdir_notify(const char *new_cwd) { struct strbuf old_cwd = STRBUF_INIT; diff --git a/chdir-notify.h b/chdir-notify.h index 81eb69d846..36b4114472 100644 --- a/chdir-notify.h +++ b/chdir-notify.h @@ -19,10 +19,7 @@ * chdir_notify_register("description", foo, data); * * In practice most callers will want to move a relative path to the new root; - * they can use the reparent_relative_path() helper for that. If that's all - * you're doing, you can also use the convenience function: - * - * chdir_notify_reparent("description", &my_path); + * they can use the reparent_relative_path() helper for that. * * Whenever a chdir event occurs, that will update my_path (if it's relative) * to adjust for the new cwd by freeing any existing string and allocating a @@ -43,7 +40,6 @@ typedef void (*chdir_notify_callback)(const char *name, void chdir_notify_register(const char *name, chdir_notify_callback cb, void *data); void chdir_notify_unregister(const char *name, chdir_notify_callback cb, void *data); -void chdir_notify_reparent(const char *name, char **path); /* * diff --git a/refs.c b/refs.c index d3caa9a633..4912510590 100644 --- a/refs.c +++ b/refs.c @@ -2351,15 +2351,31 @@ void ref_store_release(struct ref_store *ref_store) struct ref_store *get_main_ref_store(struct repository *r) { + enum ref_storage_format format; + if (r->refs_private) return r->refs_private; if (!r->gitdir) BUG("attempting to get main_ref_store outside of repository"); - r->refs_private = ref_store_init(r, r->ref_storage_format, - r->gitdir, REF_STORE_ALL_CAPS); + /* + * When constructing the reference backend we'll end up reading the Git + * configuration. This means we'll also try to evaluate "onbranch" + * conditions. + * + * We cannot read branches when constructing the refdb, so it is not + * possible to evaluate those conditions in the first place. To gate + * their evaluation we check whether or not the reference storage + * format has been configured -- we thus have to temporarily set it to + * UNKNOWN here so that we don't end up recursing. + */ + format = r->ref_storage_format; + r->ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; + r->refs_private = ref_store_init(r, format, r->gitdir, REF_STORE_ALL_CAPS); r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private); + r->ref_storage_format = format; + return r->refs_private; } @@ -3555,8 +3571,6 @@ void refs_compute_filesystem_location(const char *gitdir, const char *payload, bool *is_worktree, struct strbuf *refdir, struct strbuf *ref_common_dir) { - struct strbuf sb = STRBUF_INIT; - *is_worktree = get_common_dir_noenv(ref_common_dir, gitdir); if (!payload) { @@ -3570,8 +3584,8 @@ void refs_compute_filesystem_location(const char *gitdir, const char *payload, } if (!is_absolute_path(payload)) { - strbuf_addf(&sb, "%s/%s", ref_common_dir->buf, payload); - strbuf_realpath(ref_common_dir, sb.buf, 1); + strbuf_addf(ref_common_dir, "/%s", payload); + strbuf_realpath(ref_common_dir, ref_common_dir->buf, 1); } else { strbuf_realpath(ref_common_dir, payload, 1); } @@ -3584,6 +3598,4 @@ void refs_compute_filesystem_location(const char *gitdir, const char *payload, BUG("worktree path does not contain slash"); strbuf_addf(refdir, "/worktrees/%s", wt_id + 1); } - - strbuf_release(&sb); } diff --git a/refs/files-backend.c b/refs/files-backend.c index 2b27091484..cab604b87e 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -100,6 +100,23 @@ static void clear_loose_ref_cache(struct files_ref_store *refs) } } +static void files_ref_store_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *payload) +{ + struct files_ref_store *refs = payload; + char *tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->base.gitdir); + free(refs->base.gitdir); + refs->base.gitdir = tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->gitcommondir); + free(refs->gitcommondir); + refs->gitcommondir = tmp; +} + /* * Create a new submodule ref cache and add it to the internal * set of caches. @@ -128,9 +145,7 @@ static struct ref_store *files_ref_store_init(struct repository *repo, repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs); - chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir); - chdir_notify_reparent("files-backend $GIT_COMMONDIR", - &refs->gitcommondir); + chdir_notify_register(NULL, files_ref_store_reparent, refs); strbuf_release(&refdir); @@ -182,6 +197,7 @@ static void files_ref_store_release(struct ref_store *ref_store) free(refs->gitcommondir); ref_store_release(refs->packed_ref_store); free(refs->packed_ref_store); + chdir_notify_unregister(NULL, files_ref_store_reparent, refs); } static void files_reflog_path(struct files_ref_store *refs, diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 0acde48c45..499cb55dfa 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -211,6 +211,19 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot) return snapshot->refs->base.repo->hash_algo->hexsz; } +static void packed_ref_store_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *payload) +{ + struct packed_ref_store *refs = payload; + char *tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->path); + free(refs->path); + refs->path = tmp; +} + /* * Since packed-refs is only stored in the common dir, don't parse the * payload and rely on the files-backend to set 'gitdir' correctly. @@ -229,7 +242,7 @@ struct ref_store *packed_ref_store_init(struct repository *repo, strbuf_addf(&sb, "%s/packed-refs", gitdir); refs->path = strbuf_detach(&sb, NULL); - chdir_notify_reparent("packed-refs", &refs->path); + chdir_notify_register(NULL, packed_ref_store_reparent, refs); return ref_store; } @@ -274,6 +287,7 @@ static void packed_ref_store_release(struct ref_store *ref_store) clear_snapshot(refs); rollback_lock_file(&refs->lock); delete_tempfile(&refs->tempfile); + chdir_notify_unregister(NULL, packed_ref_store_reparent, refs); free(refs->path); } diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index c151d331e7..d7f92d8808 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -363,6 +363,19 @@ static int reftable_be_config(const char *var, const char *value, return 0; } +static void reftable_be_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *payload) +{ + struct reftable_ref_store *refs = payload; + char *tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->base.gitdir); + free(refs->base.gitdir); + refs->base.gitdir = tmp; +} + static struct ref_store *reftable_be_init(struct repository *repo, const char *payload, const char *gitdir, @@ -445,7 +458,7 @@ static struct ref_store *reftable_be_init(struct repository *repo, goto done; } - chdir_notify_reparent("reftables-backend $GIT_DIR", &refs->base.gitdir); + chdir_notify_register(NULL, reftable_be_reparent, refs); done: assert(refs->err != REFTABLE_API_ERROR); @@ -472,6 +485,7 @@ static void reftable_be_release(struct ref_store *ref_store) free(be); } strmap_clear(&refs->worktree_backends, 0); + chdir_notify_unregister(NULL, reftable_be_reparent, refs); } static int reftable_be_create_on_disk(struct ref_store *ref_store, diff --git a/repository.c b/repository.c index c1e91eb0da..73d80bcffd 100644 --- a/repository.c +++ b/repository.c @@ -422,6 +422,11 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->remote_state); } + if (repo->refs_private) { + ref_store_release(repo->refs_private); + FREE_AND_NULL(repo->refs_private); + } + strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e) ref_store_release(e->value); strmap_clear(&repo->submodule_ref_stores, 1); diff --git a/setup.c b/setup.c index 1eb7dba367..ebcdf422fe 100644 --- a/setup.c +++ b/setup.c @@ -1798,32 +1798,6 @@ int apply_repository_format(struct repository *repo, return 0; } -/* - * Check the repository format version in the path found in repo_get_git_dir(repo), - * and die if it is a version we don't understand. Generally one would - * set_git_dir() before calling this, and use it only for "are we in a valid - * repo?". - * - * If successful and fmt is not NULL, fill fmt with data. - */ -static void check_and_apply_repository_format(struct repository *repo, - struct repository_format *fmt, - enum apply_repository_format_flags flags) -{ - struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; - struct strbuf err = STRBUF_INIT; - - if (!fmt) - fmt = &repo_fmt; - - check_repository_format_gently(repo_get_git_dir(repo), fmt, NULL); - if (apply_repository_format(repo, fmt, flags, &err) < 0) - die("%s", err.buf); - startup_info->have_repository = 1; - - clear_repository_format(&repo_fmt); -} - const char *enter_repo(struct repository *repo, const char *path, unsigned flags) { static struct strbuf validated_path = STRBUF_INIT; @@ -1897,9 +1871,17 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags } if (is_git_directory(".")) { + struct repository_format fmt = REPOSITORY_FORMAT_INIT; + struct strbuf err = STRBUF_INIT; + set_git_dir(repo, ".", 0); - check_and_apply_repository_format(repo, NULL, - APPLY_REPOSITORY_FORMAT_HONOR_ENV); + check_repository_format_gently(".", &fmt, NULL); + if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) + die("%s", err.buf); + startup_info->have_repository = 1; + + clear_repository_format(&fmt); + strbuf_release(&err); return path; } @@ -1934,7 +1916,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) static struct strbuf cwd = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT; const char *prefix = NULL; - const char *ref_backend_uri; struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; /* @@ -2060,6 +2041,25 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) if (startup_info->have_repository) { struct strbuf err = STRBUF_INIT; + const char *ref_backend_uri; + + /* + * The env variable should override the repository config + * for 'extensions.refStorage'. + */ + ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); + if (ref_backend_uri) { + char *format; + + free(repo_fmt.ref_storage_payload); + + parse_reference_uri(ref_backend_uri, &format, &repo_fmt.ref_storage_payload); + repo_fmt.ref_storage_format = ref_storage_format_by_name(format); + if (repo_fmt.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format: '%s'"), format); + + free(format); + } if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) @@ -2085,25 +2085,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) setenv(GIT_PREFIX_ENVIRONMENT, "", 1); } - /* - * The env variable should override the repository config - * for 'extensions.refStorage'. - */ - ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); - if (ref_backend_uri) { - char *backend, *payload; - enum ref_storage_format format; - - parse_reference_uri(ref_backend_uri, &backend, &payload); - format = ref_storage_format_by_name(backend); - if (format == REF_STORAGE_FORMAT_UNKNOWN) - die(_("unknown ref storage format: '%s'"), backend); - repo_set_ref_storage_format(repo, format, payload); - - free(backend); - free(payload); - } - setup_original_cwd(repo); strbuf_release(&dir); @@ -2738,8 +2719,7 @@ out: return ret; } -static void repository_format_configure(struct repository *repo, - struct repository_format *repo_fmt, +static void repository_format_configure(struct repository_format *repo_fmt, int hash, enum ref_storage_format ref_format) { struct default_format_config cfg = { @@ -2776,7 +2756,6 @@ static void repository_format_configure(struct repository *repo, } else if (cfg.hash != GIT_HASH_UNKNOWN) { repo_fmt->hash_algo = cfg.hash; } - repo_set_hash_algo(repo, repo_fmt->hash_algo); env = getenv("GIT_DEFAULT_REF_FORMAT"); if (repo_fmt->version >= 0 && @@ -2814,9 +2793,6 @@ static void repository_format_configure(struct repository *repo, free(backend); } - - repo_set_ref_storage_format(repo, repo_fmt->ref_storage_format, - repo_fmt->ref_storage_payload); } int init_db(struct repository *repo, @@ -2830,6 +2806,7 @@ int init_db(struct repository *repo, int exist_ok = flags & INIT_DB_EXIST_OK; char *original_git_dir = real_pathdup(git_dir, 1); struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; + struct strbuf err = STRBUF_INIT; if (real_git_dir) { struct stat st; @@ -2856,10 +2833,11 @@ int init_db(struct repository *repo, * config file, so this will not fail. What we are catching * is an attempt to reinitialize new repository with an old tool. */ - check_and_apply_repository_format(repo, &repo_fmt, - APPLY_REPOSITORY_FORMAT_HONOR_ENV); - - repository_format_configure(repo, &repo_fmt, hash, ref_storage_format); + check_repository_format_gently(repo_get_git_dir(repo), &repo_fmt, NULL); + repository_format_configure(&repo_fmt, hash, ref_storage_format); + if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) + die("%s", err.buf); + startup_info->have_repository = 1; /* * Ensure `core.hidedotfiles` is processed. This must happen after we @@ -2914,6 +2892,7 @@ int init_db(struct repository *repo, } clear_repository_format(&repo_fmt); + strbuf_release(&err); free(original_git_dir); return 0; }