]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs: receive and use the reference storage payload
authorKarthik Nayak <karthik.188@gmail.com>
Wed, 25 Feb 2026 09:40:44 +0000 (10:40 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Feb 2026 17:27:12 +0000 (09:27 -0800)
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>
refs.c
refs/files-backend.c
refs/packed-backend.c
refs/packed-backend.h
refs/refs-internal.h
refs/reftable-backend.c

diff --git a/refs.c b/refs.c
index c83af63dc5f7c19a8a64840feab2ae7e6b1b44ed..ba2573eb7a339a953d918eb8b0d44c19c8592ff3 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -5,6 +5,7 @@
 #define USE_THE_REPOSITORY_VARIABLE
 
 #include "git-compat-util.h"
+#include "abspath.h"
 #include "advice.h"
 #include "config.h"
 #include "environment.h"
@@ -2290,7 +2291,7 @@ static struct ref_store *ref_store_init(struct repository *repo,
        if (!be)
                BUG("reference backend is unknown");
 
-       refs = be->init(repo, gitdir, flags);
+       refs = be->init(repo, NULL, gitdir, flags);
        return refs;
 }
 
@@ -3468,3 +3469,40 @@ const char *ref_transaction_error_msg(enum ref_transaction_error err)
                return "unknown failure";
        }
 }
+
+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) {
+               /*
+                * We can use the 'gitdir' as the 'refdir' without appending the
+                * worktree path, as the 'gitdir' here is already the worktree
+                * path and is different from 'commondir' denoted by 'ref_common_dir'.
+                */
+               strbuf_addstr(refdir, gitdir);
+               return;
+       }
+
+       if (!is_absolute_path(payload)) {
+               strbuf_addf(&sb, "%s/%s", ref_common_dir->buf, payload);
+               strbuf_realpath(ref_common_dir, sb.buf, 1);
+       } else {
+               strbuf_realpath(ref_common_dir, payload, 1);
+       }
+
+       strbuf_addbuf(refdir, ref_common_dir);
+
+       if (*is_worktree) {
+               const char *wt_id = strrchr(gitdir, '/');
+               if (!wt_id)
+                       BUG("worktree path does not contain slash");
+               strbuf_addf(refdir, "/worktrees/%s", wt_id + 1);
+       }
+
+       strbuf_release(&sb);
+}
index d3f64232610ae33afdbf12772292da7b4f33df59..9cde3ba724f8e1fc3fa8521a41ef8beeb4a87fb6 100644 (file)
@@ -106,19 +106,24 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
  * set of caches.
  */
 static struct ref_store *files_ref_store_init(struct repository *repo,
+                                             const char *payload,
                                              const char *gitdir,
                                              unsigned int flags)
 {
        struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
-       struct strbuf sb = STRBUF_INIT;
+       struct strbuf ref_common_dir = STRBUF_INIT;
+       struct strbuf refdir = STRBUF_INIT;
+       bool is_worktree;
+
+       refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir,
+                                        &ref_common_dir);
 
-       base_ref_store_init(ref_store, repo, gitdir, &refs_be_files);
+       base_ref_store_init(ref_store, repo, refdir.buf, &refs_be_files);
        refs->store_flags = flags;
-       get_common_dir_noenv(&sb, gitdir);
-       refs->gitcommondir = strbuf_detach(&sb, NULL);
+       refs->gitcommondir = strbuf_detach(&ref_common_dir, NULL);
        refs->packed_ref_store =
-               packed_ref_store_init(repo, refs->gitcommondir, flags);
+               packed_ref_store_init(repo, NULL, refs->gitcommondir, flags);
        refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
        repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs);
 
@@ -126,6 +131,8 @@ static struct ref_store *files_ref_store_init(struct repository *repo,
        chdir_notify_reparent("files-backend $GIT_COMMONDIR",
                              &refs->gitcommondir);
 
+       strbuf_release(&refdir);
+
        return ref_store;
 }
 
index 4ea0c1229946bdd6554fb20894ecf7bbe2c20317..e7bb9f10f9df1c84445a0803bed7dcd302567395 100644 (file)
@@ -211,7 +211,12 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot)
        return snapshot->refs->base.repo->hash_algo->hexsz;
 }
 
+/*
+ * 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.
+ */
 struct ref_store *packed_ref_store_init(struct repository *repo,
+                                       const char *payload UNUSED,
                                        const char *gitdir,
                                        unsigned int store_flags)
 {
index 9481d5e7c2e2f51efaf86a1b64436b0fadf7307f..2c2377a35653ec7c57848c39b43d04c96994860d 100644 (file)
@@ -14,6 +14,7 @@ struct ref_transaction;
  */
 
 struct ref_store *packed_ref_store_init(struct repository *repo,
+                                       const char *payload,
                                        const char *gitdir,
                                        unsigned int store_flags);
 
index c7d2a6e50b7696cc3eba626a7538dcbc0dba1880..4fb8fdb872f4429fca3d2cc35a966f9350b43588 100644 (file)
@@ -389,6 +389,7 @@ struct ref_store;
  * the ref_store and to record the ref_store for later lookup.
  */
 typedef struct ref_store *ref_store_init_fn(struct repository *repo,
+                                           const char *payload,
                                            const char *gitdir,
                                            unsigned int flags);
 /*
@@ -666,4 +667,17 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
                                          unsigned int initial_transaction,
                                          struct strbuf *err);
 
+/*
+ * Given a gitdir and the reference storage payload provided, retrieve the
+ * 'refdir' and 'ref_common_dir'. The former is where references should be
+ * stored for the current worktree, the latter is the common reference
+ * directory if working with a linked worktree. If working with the main
+ * worktree, both values will be the same.
+ *
+ * This is used by backends that store references in the repository directly.
+ */
+void refs_compute_filesystem_location(const char *gitdir, const char *payload,
+                                     bool *is_worktree, struct strbuf *refdir,
+                                     struct strbuf *ref_common_dir);
+
 #endif /* REFS_REFS_INTERNAL_H */
index 6ce7f9bb8edb984841d93a3627865a195900064b..0e220d6bb53aadff1382ba18e50a65d2f8b682b7 100644 (file)
@@ -372,18 +372,24 @@ static int reftable_be_fsync(int fd)
 }
 
 static struct ref_store *reftable_be_init(struct repository *repo,
+                                         const char *payload,
                                          const char *gitdir,
                                          unsigned int store_flags)
 {
        struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
+       struct strbuf ref_common_dir = STRBUF_INIT;
+       struct strbuf refdir = STRBUF_INIT;
        struct strbuf path = STRBUF_INIT;
-       int is_worktree;
+       bool is_worktree;
        mode_t mask;
 
        mask = umask(0);
        umask(mask);
 
-       base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable);
+       refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir,
+                                        &ref_common_dir);
+
+       base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable);
        strmap_init(&refs->worktree_backends);
        refs->store_flags = store_flags;
        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,
        /*
         * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
         * This stack contains both the shared and the main worktree refs.
-        *
-        * Note that we don't try to resolve the path in case we have a
-        * worktree because `get_common_dir_noenv()` already does it for us.
         */
-       is_worktree = get_common_dir_noenv(&path, gitdir);
+       strbuf_addbuf(&path, &ref_common_dir);
        if (!is_worktree) {
                strbuf_reset(&path);
-               strbuf_realpath(&path, gitdir, 0);
+               strbuf_realpath(&path, ref_common_dir.buf, 0);
        }
        strbuf_addstr(&path, "/reftable");
        refs->err = reftable_backend_init(&refs->main_backend, path.buf,
@@ -443,10 +446,9 @@ static struct ref_store *reftable_be_init(struct repository *repo,
         * do it efficiently.
         */
        if (is_worktree) {
-               strbuf_reset(&path);
-               strbuf_addf(&path, "%s/reftable", gitdir);
+               strbuf_addstr(&refdir, "/reftable");
 
-               refs->err = reftable_backend_init(&refs->worktree_backend, path.buf,
+               refs->err = reftable_backend_init(&refs->worktree_backend, refdir.buf,
                                                  &refs->write_options);
                if (refs->err)
                        goto done;
@@ -456,6 +458,8 @@ static struct ref_store *reftable_be_init(struct repository *repo,
 
 done:
        assert(refs->err != REFTABLE_API_ERROR);
+       strbuf_release(&ref_common_dir);
+       strbuf_release(&refdir);
        strbuf_release(&path);
        return &refs->base;
 }