refs: receive and use the reference storage payload

An upcoming commit will add support for providing an URI via the 'extensions.refStorage' config. The URI will contain the reference backend and a corresponding payload. The payload can be then used for providing an alternate locations for the reference backend. To prepare for this, modify the existing backends to accept such an argument when initializing via the 'init()' function. Both the files and reftable backends will parse the information to be filesystem paths to store references. Given that no callers pass any payload yet this is essentially a no-op change for now. To enable this, provide a 'refs_compute_filesystem_location()' function which will parse the current 'gitdir' and the 'payload' to provide the final reference directory and common reference directory (if working in a linked worktree). The documentation and tests will be added alongside the extension of the config variable. Helped-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Feb 25, 2026 at 10:40 UTC d74aacd7c41573e586c1a9d7204aaaebf9901bd1
6 files changed +85 -16
refs.c
+39 -1
@@ -5,6 +5,7 @@
5 #define USE_THE_REPOSITORY_VARIABLE
6
7 #include "git-compat-util.h"
8 +#include "abspath.h"
9 #include "advice.h"
10 #include "config.h"
11 #include "environment.h"
@@ -2290,7 +2291,7 @@ static struct ref_store *ref_store_init(struct repository *repo,
2291 if (!be)
2292 BUG("reference backend is unknown");
2293
2293 - refs = be->init(repo, gitdir, flags);
2294 + refs = be->init(repo, NULL, gitdir, flags);
2295 return refs;
2296 }
2297
@@ -3468,3 +3469,40 @@ const char *ref_transaction_error_msg(enum ref_transaction_error err)
3469 return "unknown failure";
3470 }
3471 }
3472 +
3473 +void refs_compute_filesystem_location(const char *gitdir, const char *payload,
3474 + bool *is_worktree, struct strbuf *refdir,
3475 + struct strbuf *ref_common_dir)
3476 +{
3477 + struct strbuf sb = STRBUF_INIT;
3478 +
3479 + *is_worktree = get_common_dir_noenv(ref_common_dir, gitdir);
3480 +
3481 + if (!payload) {
3482 + /*
3483 + * We can use the 'gitdir' as the 'refdir' without appending the
3484 + * worktree path, as the 'gitdir' here is already the worktree
3485 + * path and is different from 'commondir' denoted by 'ref_common_dir'.
3486 + */
3487 + strbuf_addstr(refdir, gitdir);
3488 + return;
3489 + }
3490 +
3491 + if (!is_absolute_path(payload)) {
3492 + strbuf_addf(&sb, "%s/%s", ref_common_dir->buf, payload);
3493 + strbuf_realpath(ref_common_dir, sb.buf, 1);
3494 + } else {
3495 + strbuf_realpath(ref_common_dir, payload, 1);
3496 + }
3497 +
3498 + strbuf_addbuf(refdir, ref_common_dir);
3499 +
3500 + if (*is_worktree) {
3501 + const char *wt_id = strrchr(gitdir, '/');
3502 + if (!wt_id)
3503 + BUG("worktree path does not contain slash");
3504 + strbuf_addf(refdir, "/worktrees/%s", wt_id + 1);
3505 + }
3506 +
3507 + strbuf_release(&sb);
3508 +}
refs/files-backend.c
+12 -5
@@ -106,19 +106,24 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
106 * set of caches.
107 */
108 static struct ref_store *files_ref_store_init(struct repository *repo,
109 + const char *payload,
110 const char *gitdir,
111 unsigned int flags)
112 {
113 struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
114 struct ref_store *ref_store = (struct ref_store *)refs;
114 - struct strbuf sb = STRBUF_INIT;
115 + struct strbuf ref_common_dir = STRBUF_INIT;
116 + struct strbuf refdir = STRBUF_INIT;
117 + bool is_worktree;
118 +
119 + refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir,
120 + &ref_common_dir);
121
116 - base_ref_store_init(ref_store, repo, gitdir, &refs_be_files);
122 + base_ref_store_init(ref_store, repo, refdir.buf, &refs_be_files);
123 refs->store_flags = flags;
118 - get_common_dir_noenv(&sb, gitdir);
119 - refs->gitcommondir = strbuf_detach(&sb, NULL);
124 + refs->gitcommondir = strbuf_detach(&ref_common_dir, NULL);
125 refs->packed_ref_store =
121 - packed_ref_store_init(repo, refs->gitcommondir, flags);
126 + packed_ref_store_init(repo, NULL, refs->gitcommondir, flags);
127 refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
128 repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs);
129
@@ -126,6 +131,8 @@ static struct ref_store *files_ref_store_init(struct repository *repo,
131 chdir_notify_reparent("files-backend $GIT_COMMONDIR",
132 &refs->gitcommondir);
133
134 + strbuf_release(&refdir);
135 +
136 return ref_store;
137 }
138
refs/packed-backend.c
+5
@@ -211,7 +211,12 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot)
211 return snapshot->refs->base.repo->hash_algo->hexsz;
212 }
213
214 +/*
215 + * Since packed-refs is only stored in the common dir, don't parse the
216 + * payload and rely on the files-backend to set 'gitdir' correctly.
217 + */
218 struct ref_store *packed_ref_store_init(struct repository *repo,
219 + const char *payload UNUSED,
220 const char *gitdir,
221 unsigned int store_flags)
222 {
refs/packed-backend.h
+1
@@ -14,6 +14,7 @@ struct ref_transaction;
14 */
15
16 struct ref_store *packed_ref_store_init(struct repository *repo,
17 + const char *payload,
18 const char *gitdir,
19 unsigned int store_flags);
20
refs/refs-internal.h
+14
@@ -389,6 +389,7 @@ struct ref_store;
389 * the ref_store and to record the ref_store for later lookup.
390 */
391 typedef struct ref_store *ref_store_init_fn(struct repository *repo,
392 + const char *payload,
393 const char *gitdir,
394 unsigned int flags);
395 /*
@@ -666,4 +667,17 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
667 unsigned int initial_transaction,
668 struct strbuf *err);
669
670 +/*
671 + * Given a gitdir and the reference storage payload provided, retrieve the
672 + * 'refdir' and 'ref_common_dir'. The former is where references should be
673 + * stored for the current worktree, the latter is the common reference
674 + * directory if working with a linked worktree. If working with the main
675 + * worktree, both values will be the same.
676 + *
677 + * This is used by backends that store references in the repository directly.
678 + */
679 +void refs_compute_filesystem_location(const char *gitdir, const char *payload,
680 + bool *is_worktree, struct strbuf *refdir,
681 + struct strbuf *ref_common_dir);
682 +
683 #endif /* REFS_REFS_INTERNAL_H */
refs/reftable-backend.c
+14 -10
@@ -372,18 +372,24 @@ static int reftable_be_fsync(int fd)
372 }
373
374 static struct ref_store *reftable_be_init(struct repository *repo,
375 + const char *payload,
376 const char *gitdir,
377 unsigned int store_flags)
378 {
379 struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
380 + struct strbuf ref_common_dir = STRBUF_INIT;
381 + struct strbuf refdir = STRBUF_INIT;
382 struct strbuf path = STRBUF_INIT;
380 - int is_worktree;
383 + bool is_worktree;
384 mode_t mask;
385
386 mask = umask(0);
387 umask(mask);
388
386 - base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable);
389 + refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir,
390 + &ref_common_dir);
391 +
392 + base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable);
393 strmap_init(&refs->worktree_backends);
394 refs->store_flags = store_flags;
395 refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
@@ -419,14 +425,11 @@ static struct ref_store *reftable_be_init(struct repository *repo,
425 /*
426 * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
427 * This stack contains both the shared and the main worktree refs.
422 - *
423 - * Note that we don't try to resolve the path in case we have a
424 - * worktree because `get_common_dir_noenv()` already does it for us.
428 */
426 - is_worktree = get_common_dir_noenv(&path, gitdir);
429 + strbuf_addbuf(&path, &ref_common_dir);
430 if (!is_worktree) {
431 strbuf_reset(&path);
429 - strbuf_realpath(&path, gitdir, 0);
432 + strbuf_realpath(&path, ref_common_dir.buf, 0);
433 }
434 strbuf_addstr(&path, "/reftable");
435 refs->err = reftable_backend_init(&refs->main_backend, path.buf,
@@ -443,10 +446,9 @@ static struct ref_store *reftable_be_init(struct repository *repo,
446 * do it efficiently.
447 */
448 if (is_worktree) {
446 - strbuf_reset(&path);
447 - strbuf_addf(&path, "%s/reftable", gitdir);
449 + strbuf_addstr(&refdir, "/reftable");
450
449 - refs->err = reftable_backend_init(&refs->worktree_backend, path.buf,
451 + refs->err = reftable_backend_init(&refs->worktree_backend, refdir.buf,
452 &refs->write_options);
453 if (refs->err)
454 goto done;
@@ -456,6 +458,8 @@ static struct ref_store *reftable_be_init(struct repository *repo,
458
459 done:
460 assert(refs->err != REFTABLE_API_ERROR);
461 + strbuf_release(&ref_common_dir);
462 + strbuf_release(&refdir);
463 strbuf_release(&path);
464 return &refs->base;
465 }