Merge branch 'ps/refs-avoid-chdir-notify-reparent' into seen

The reference backends have been converted to always use absolute
paths internally. This allows dropping the calls to
`chdir_notify_reparent()` and fixes a memory leak in how the
reference database is constructed with an "onbranch" condition.

* ps/refs-avoid-chdir-notify-reparent:
  refs: drop local buffer in `refs_compute_filesystem_location()`
  refs: fix recursing `get_main_ref_store()` with "onbranch" config
  repository: free main reference database
  chdir-notify: drop unused `chdir_notify_reparent()`
  refs: unregister reference stores from "chdir_notify"
  setup: don't apply "GIT_REFERENCE_BACKEND" without a repository
  setup: stop applying repository format twice
  setup: inline `check_and_apply_repository_format()`
This commit is contained in:
Junio C Hamano
2026-06-18 13:39:46 -07:00
8 changed files with 112 additions and 102 deletions
-26
View File
@@ -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;
+1 -5
View File
@@ -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);
/*
*
+20 -8
View File
@@ -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);
}
+19 -3
View File
@@ -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,
+15 -1
View File
@@ -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);
}
+15 -1
View File
@@ -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,
+5
View File
@@ -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);
+37 -58
View File
@@ -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;
}