Skip to content

Commit 825b9b0

Browse files
KarthikNayakgitster
authored andcommitted
refs: parse and use the reference storage payload
The previous commit extended the 'extensions.refStorage' config to add support for a reference storage payload. The payload provides backend specific information on where to store references for a given directory. Propagate this information to individual backends when initializing them via the 'init()' function. Both the files and reftable backends will parse the information to be filesystem paths to store references. 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). Finally, for linked worktrees, traditionally references were stored in the '$GIT_DIR/worktrees/<wt_id>' path. But when using an alternate reference storage path, it doesn't make sense to store main worktree references in the new path, and linked worktree references in the $GIT_DIR path. So, let's store linked worktree references in '$ALTERNATE_REFERENCE_DIR/worktrees/<wt_id'. To do this, create the necessary files and folders and also add stubs in the $GIT_DIR path to ensure that it is still considered a Git directory. Since this commit adds the required linking, also add the necessary documentation and tests. 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>
1 parent dcb972a commit 825b9b0

10 files changed

Lines changed: 290 additions & 17 deletions

File tree

Documentation/config/extensions.adoc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,24 @@ For historical reasons, this extension is respected regardless of the
5757
`core.repositoryFormatVersion` setting.
5858
5959
refStorage:::
60-
Specify the ref storage format to use. The acceptable values are:
60+
Specify the ref storage format and location to use. The value can be
61+
either a format name or a URI:
6162
+
6263
--
64+
* A format name alone (e.g., `reftable` or `files`) uses the default
65+
location (the repository's common directory).
66+
67+
* A URI format `<format>://<location>` explicitly specifies both the
68+
format and payload (e.g., `reftable:///foo/bar`).
69+
70+
Supported format names are:
71+
+
6372
include::../ref-storage-format.adoc[]
73+
+
74+
The payload is passed directly to the reference backend. For the files and
75+
reftable backends, this must be a filesystem path. Relative paths are resolved
76+
relative to the $GIT_DIR. Future backends may support other payload schemes,
77+
e.g., postgres://127.0.0.1:5432?database=myrepo.
6478
--
6579
+
6680
Note that this setting should only be set by linkgit:git-init[1] or

builtin/worktree.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,40 @@ static int make_worktree_orphan(const char * ref, const struct add_opts *opts,
425425
return run_command(&cp);
426426
}
427427

428+
/*
429+
* References for worktress are generally stored in '$GIT_DIR/worktrees/<wt_id>'.
430+
* But when using alternate reference directories, we want to store the worktree
431+
* references in '$ALTERNATE_REFERENCE_DIR/worktrees/<wt_id>'.
432+
*
433+
* Create the necessary folder structure to facilitate the same. But to ensure
434+
* that the former path is still considered a Git directory, add stubs (similar
435+
* to how we do in the reftable backend).
436+
*/
437+
static void setup_alternate_ref_dir(struct worktree *wt, const char *wt_git_path)
438+
{
439+
struct strbuf sb = STRBUF_INIT;
440+
char *path;
441+
442+
path = wt->repo->ref_storage_payload;
443+
if (!path)
444+
return;
445+
446+
if (!is_absolute_path(path))
447+
strbuf_addf(&sb, "%s/", wt->repo->commondir);
448+
449+
strbuf_addf(&sb, "%s/worktrees", path);
450+
safe_create_dir(wt->repo, sb.buf, 1);
451+
strbuf_addf(&sb, "/%s", wt->id);
452+
safe_create_dir(wt->repo, sb.buf, 1);
453+
strbuf_reset(&sb);
454+
455+
strbuf_addf(&sb, "this worktree stores references in %s/worktrees/%s",
456+
path, wt->id);
457+
refs_create_refdir_stubs(wt->repo, wt_git_path, sb.buf);
458+
459+
strbuf_release(&sb);
460+
}
461+
428462
static int add_worktree(const char *path, const char *refname,
429463
const struct add_opts *opts)
430464
{
@@ -518,6 +552,7 @@ static int add_worktree(const char *path, const char *refname,
518552
ret = error(_("could not find created worktree '%s'"), name);
519553
goto done;
520554
}
555+
setup_alternate_ref_dir(wt, sb_repo.buf);
521556
wt_refs = get_worktree_ref_store(wt);
522557

523558
ret = ref_store_create_on_disk(wt_refs, REF_STORE_CREATE_ON_DISK_IS_WORKTREE, &sb);

refs.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define USE_THE_REPOSITORY_VARIABLE
66

77
#include "git-compat-util.h"
8+
#include "abspath.h"
89
#include "advice.h"
910
#include "config.h"
1011
#include "environment.h"
@@ -2224,7 +2225,11 @@ static struct ref_store *ref_store_init(struct repository *repo,
22242225
if (!be)
22252226
BUG("reference backend is unknown");
22262227

2227-
refs = be->init(repo, gitdir, flags);
2228+
/*
2229+
* TODO Send in a 'struct worktree' instead of a 'gitdir', and
2230+
* allow the backend to handle how it wants to deal with worktrees.
2231+
*/
2232+
refs = be->init(repo, repo->ref_storage_payload, gitdir, flags);
22282233
return refs;
22292234
}
22302235

@@ -3426,3 +3431,33 @@ void refs_create_refdir_stubs(struct repository *repo, const char *refdir,
34263431

34273432
strbuf_release(&path);
34283433
}
3434+
3435+
void refs_compute_filesystem_location(const char *gitdir, const char *payload,
3436+
bool *is_worktree, struct strbuf *refdir,
3437+
struct strbuf *ref_common_dir)
3438+
{
3439+
struct strbuf sb = STRBUF_INIT;
3440+
3441+
strbuf_addstr(refdir, gitdir);
3442+
*is_worktree = get_common_dir_noenv(ref_common_dir, gitdir);
3443+
3444+
if (!payload)
3445+
return;
3446+
3447+
if (!is_absolute_path(payload)) {
3448+
strbuf_addf(&sb, "%s/%s", ref_common_dir->buf, payload);
3449+
strbuf_realpath(ref_common_dir, sb.buf, 1);
3450+
} else {
3451+
strbuf_realpath(ref_common_dir, payload, 1);
3452+
}
3453+
3454+
strbuf_reset(refdir);
3455+
strbuf_addbuf(refdir, ref_common_dir);
3456+
3457+
if (*is_worktree) {
3458+
char *wt_id = strrchr(gitdir, '/') + 1;
3459+
strbuf_addf(refdir, "/worktrees/%s", wt_id);
3460+
}
3461+
3462+
strbuf_release(&sb);
3463+
}

refs/files-backend.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,26 +106,34 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
106106
* set of caches.
107107
*/
108108
static struct ref_store *files_ref_store_init(struct repository *repo,
109+
const char *payload,
109110
const char *gitdir,
110111
unsigned int flags)
111112
{
112113
struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
113114
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);
115121

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);
117123
refs->store_flags = flags;
118-
get_common_dir_noenv(&sb, gitdir);
119-
refs->gitcommondir = strbuf_detach(&sb, NULL);
124+
refs->gitcommondir = xstrdup(ref_common_dir.buf);
120125
refs->packed_ref_store =
121-
packed_ref_store_init(repo, refs->gitcommondir, flags);
126+
packed_ref_store_init(repo, payload, ref_common_dir.buf, flags);
122127
refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
123128
repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs);
124129

125130
chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
126131
chdir_notify_reparent("files-backend $GIT_COMMONDIR",
127132
&refs->gitcommondir);
128133

134+
strbuf_release(&ref_common_dir);
135+
strbuf_release(&refdir);
136+
129137
return ref_store;
130138
}
131139

refs/packed-backend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot)
212212
}
213213

214214
struct ref_store *packed_ref_store_init(struct repository *repo,
215+
const char *payload UNUSED,
215216
const char *gitdir,
216217
unsigned int store_flags)
217218
{

refs/packed-backend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct ref_transaction;
1414
*/
1515

1616
struct ref_store *packed_ref_store_init(struct repository *repo,
17+
const char *payload,
1718
const char *gitdir,
1819
unsigned int store_flags);
1920

refs/refs-internal.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ struct ref_store;
389389
* the ref_store and to record the ref_store for later lookup.
390390
*/
391391
typedef struct ref_store *ref_store_init_fn(struct repository *repo,
392+
const char *payload,
392393
const char *gitdir,
393394
unsigned int flags);
394395
/*
@@ -666,4 +667,18 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
666667
unsigned int initial_transaction,
667668
struct strbuf *err);
668669

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 such as {files, reftable} which store references in
678+
* dedicated filesystem paths.
679+
*/
680+
void refs_compute_filesystem_location(const char *gitdir, const char *payload,
681+
bool *is_worktree, struct strbuf *refdir,
682+
struct strbuf *ref_common_dir);
683+
669684
#endif /* REFS_REFS_INTERNAL_H */

refs/reftable-backend.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -372,18 +372,24 @@ static int reftable_be_fsync(int fd)
372372
}
373373

374374
static struct ref_store *reftable_be_init(struct repository *repo,
375+
const char *payload,
375376
const char *gitdir,
376377
unsigned int store_flags)
377378
{
378379
struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
380+
struct strbuf ref_common_dir = STRBUF_INIT;
381+
struct strbuf refdir = STRBUF_INIT;
379382
struct strbuf path = STRBUF_INIT;
380-
int is_worktree;
383+
bool is_worktree;
381384
mode_t mask;
382385

383386
mask = umask(0);
384387
umask(mask);
385388

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);
387393
strmap_init(&refs->worktree_backends);
388394
refs->store_flags = store_flags;
389395
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,
419425
/*
420426
* Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
421427
* 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.
425428
*/
426-
is_worktree = get_common_dir_noenv(&path, gitdir);
429+
strbuf_addbuf(&path, &ref_common_dir);
427430
if (!is_worktree) {
428431
strbuf_reset(&path);
429-
strbuf_realpath(&path, gitdir, 0);
432+
strbuf_realpath(&path, ref_common_dir.buf, 0);
430433
}
431434
strbuf_addstr(&path, "/reftable");
432435
refs->err = reftable_backend_init(&refs->main_backend, path.buf,
@@ -443,10 +446,9 @@ static struct ref_store *reftable_be_init(struct repository *repo,
443446
* do it efficiently.
444447
*/
445448
if (is_worktree) {
446-
strbuf_reset(&path);
447-
strbuf_addf(&path, "%s/reftable", gitdir);
449+
strbuf_addstr(&refdir, "/reftable");
448450

449-
refs->err = reftable_backend_init(&refs->worktree_backend, path.buf,
451+
refs->err = reftable_backend_init(&refs->worktree_backend, refdir.buf,
450452
&refs->write_options);
451453
if (refs->err)
452454
goto done;
@@ -456,6 +458,8 @@ static struct ref_store *reftable_be_init(struct repository *repo,
456458

457459
done:
458460
assert(refs->err != REFTABLE_API_ERROR);
461+
strbuf_release(&ref_common_dir);
462+
strbuf_release(&refdir);
459463
strbuf_release(&path);
460464
return &refs->base;
461465
}

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ integration_tests = [
210210
't1420-lost-found.sh',
211211
't1421-reflog-write.sh',
212212
't1422-show-ref-exists.sh',
213+
't1423-ref-backend.sh',
213214
't1430-bad-ref-name.sh',
214215
't1450-fsck.sh',
215216
't1451-fsck-buffer.sh',

0 commit comments

Comments
 (0)