aboutsummaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2026-03-04 10:52:58 -0800
committerJunio C Hamano <gitster@pobox.com>2026-03-04 10:52:59 -0800
commit1d0a2acb78f157d39937a088548e561b27722e8d (patch)
tree4251f1f0f2a4ce4a6ee6f71e75d17d0bdbe4f948 /refs.c
parent50d063e335afd5828fbb9de2f2b2fb44fd884d2b (diff)
parent53592d68e86814fcc4a8df6cc38340597e56fe5a (diff)
downloadgit-1d0a2acb78f157d39937a088548e561b27722e8d.tar.xz
Merge branch 'kn/ref-location'
Allow the directory in which reference backends store their data to be specified. * kn/ref-location: refs: add GIT_REFERENCE_BACKEND to specify reference backend refs: allow reference location in refstorage config refs: receive and use the reference storage payload refs: move out stub modification to generic layer refs: extract out `refs_create_refdir_stubs()` setup: don't modify repo in `create_reference_database()`
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c126
1 files changed, 123 insertions, 3 deletions
diff --git a/refs.c b/refs.c
index 600913b99f..0ad1da990d 100644
--- 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"
@@ -2166,15 +2167,93 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
return NULL;
}
+void refs_create_refdir_stubs(struct repository *repo, const char *refdir,
+ const char *refs_heads_content)
+{
+ struct strbuf path = STRBUF_INIT;
+
+ strbuf_addf(&path, "%s/HEAD", refdir);
+ write_file(path.buf, "ref: refs/heads/.invalid");
+ adjust_shared_perm(repo, path.buf);
+
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/refs", refdir);
+ safe_create_dir(repo, path.buf, 1);
+
+ if (refs_heads_content) {
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/refs/heads", refdir);
+ write_file(path.buf, "%s", refs_heads_content);
+ adjust_shared_perm(repo, path.buf);
+ }
+
+ strbuf_release(&path);
+}
+
/* backend functions */
int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err)
{
- return refs->be->create_on_disk(refs, flags, err);
+ int ret = refs->be->create_on_disk(refs, flags, err);
+
+ if (!ret) {
+ /* Creation of stubs for linked worktrees are handled in the worktree code. */
+ if (!(flags & REF_STORE_CREATE_ON_DISK_IS_WORKTREE) && refs->repo->ref_storage_payload) {
+ refs_create_refdir_stubs(refs->repo, refs->repo->gitdir,
+ "repository uses alternate refs storage");
+ } else if (ref_storage_format_by_name(refs->be->name) != REF_STORAGE_FORMAT_FILES) {
+ struct strbuf msg = STRBUF_INIT;
+ strbuf_addf(&msg, "this repository uses the %s format", refs->be->name);
+ refs_create_refdir_stubs(refs->repo, refs->gitdir, msg.buf);
+ strbuf_release(&msg);
+ }
+ }
+
+ return ret;
}
int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err)
{
- return refs->be->remove_on_disk(refs, err);
+ int ret = refs->be->remove_on_disk(refs, err);
+
+ if (!ret) {
+ enum ref_storage_format format = ref_storage_format_by_name(refs->be->name);
+ struct strbuf sb = STRBUF_INIT;
+
+ /* Backends apart from the files backend create stubs. */
+ if (format == REF_STORAGE_FORMAT_FILES)
+ return ret;
+
+ /* Alternate refs backend require stubs in the gitdir. */
+ if (refs->repo->ref_storage_payload)
+ return ret;
+
+ strbuf_addf(&sb, "%s/HEAD", refs->gitdir);
+ if (unlink(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete stub HEAD: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs/heads", refs->gitdir);
+ if (unlink(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete stub heads: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs", refs->gitdir);
+ if (rmdir(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete refs directory: %s",
+ strerror(errno));
+ ret = -1;
+ }
+
+ strbuf_release(&sb);
+ }
+
+ return ret;
}
int repo_resolve_gitlink_ref(struct repository *r,
@@ -2227,7 +2306,11 @@ static struct ref_store *ref_store_init(struct repository *repo,
if (!be)
BUG("reference backend is unknown");
- refs = be->init(repo, gitdir, flags);
+ /*
+ * TODO Send in a 'struct worktree' instead of a 'gitdir', and
+ * allow the backend to handle how it wants to deal with worktrees.
+ */
+ refs = be->init(repo, repo->ref_storage_payload, gitdir, flags);
return refs;
}
@@ -3410,3 +3493,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);
+}