Skip to content

Commit a1758b5

Browse files
committed
Merge branch 'kn/ref-location' into seen
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: parse and use the reference storage payload refs: extract out `refs_create_refdir_stubs()` refs: allow reference location in refstorage config
2 parents e400268 + c81b3ab commit a1758b5

18 files changed

Lines changed: 433 additions & 44 deletions

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

Documentation/git.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,11 @@ double-quotes and respecting backslash escapes. E.g., the value
584584
repositories will be set to this value. The default is "files".
585585
See `--ref-format` in linkgit:git-init[1].
586586

587+
`GIT_REFERENCE_BACKEND`::
588+
Specify which reference backend to be used along with its URI.
589+
See `extensions.refStorage` option in linkgit:git-config[1] for more
590+
description. Overrides the config variable when used.
591+
587592
Git Commits
588593
~~~~~~~~~~~
589594
`GIT_AUTHOR_NAME`::

builtin/clone.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,12 +1229,7 @@ int cmd_clone(int argc,
12291229
initialize_repository_version(GIT_HASH_UNKNOWN,
12301230
the_repository->ref_storage_format, 1);
12311231

1232-
strbuf_addf(&buf, "%s/HEAD", git_dir);
1233-
write_file(buf.buf, "ref: refs/heads/.invalid");
1234-
1235-
strbuf_reset(&buf);
1236-
strbuf_addf(&buf, "%s/refs", git_dir);
1237-
safe_create_dir(the_repository, buf.buf, 1);
1232+
refs_create_refdir_stubs(the_repository, git_dir, NULL);
12381233

12391234
/*
12401235
* additional config can be injected with -c, make sure it's included
@@ -1446,7 +1441,8 @@ int cmd_clone(int argc,
14461441
hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
14471442
initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
14481443
repo_set_hash_algo(the_repository, hash_algo);
1449-
create_reference_database(the_repository->ref_storage_format, NULL, 1);
1444+
create_reference_database(the_repository->ref_storage_format,
1445+
the_repository->ref_storage_payload, NULL, 1);
14501446

14511447
/*
14521448
* Before fetching from the remote, download and install bundle

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
{
@@ -519,6 +553,7 @@ static int add_worktree(const char *path, const char *refname,
519553
ret = error(_("could not find created worktree '%s'"), name);
520554
goto done;
521555
}
556+
setup_alternate_ref_dir(wt, sb_repo.buf);
522557
wt_refs = get_worktree_ref_store(wt);
523558

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

environment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
4444
#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
4545
#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
46+
#define GIT_REFERENCE_BACKEND_ENVIRONMENT "GIT_REFERENCE_BACKEND"
4647

4748
/*
4849
* Environment variable used to propagate the --no-advice global option to the

refs.c

Lines changed: 60 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"
@@ -2225,7 +2226,11 @@ static struct ref_store *ref_store_init(struct repository *repo,
22252226
if (!be)
22262227
BUG("reference backend is unknown");
22272228

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

@@ -3412,3 +3417,57 @@ const char *ref_transaction_error_msg(enum ref_transaction_error err)
34123417
return "unknown failure";
34133418
}
34143419
}
3420+
3421+
void refs_create_refdir_stubs(struct repository *repo, const char *refdir,
3422+
const char *refs_heads_msg)
3423+
{
3424+
struct strbuf path = STRBUF_INIT;
3425+
3426+
3427+
strbuf_addf(&path, "%s/HEAD", refdir);
3428+
write_file(path.buf, "ref: refs/heads/.invalid");
3429+
adjust_shared_perm(repo, path.buf);
3430+
3431+
strbuf_reset(&path);
3432+
strbuf_addf(&path, "%s/refs", refdir);
3433+
safe_create_dir(repo, path.buf, 1);
3434+
3435+
if (refs_heads_msg) {
3436+
strbuf_reset(&path);
3437+
strbuf_addf(&path, "%s/refs/heads", refdir);
3438+
write_file(path.buf, "%s", refs_heads_msg);
3439+
adjust_shared_perm(repo, path.buf);
3440+
}
3441+
3442+
strbuf_release(&path);
3443+
}
3444+
3445+
void refs_compute_filesystem_location(const char *gitdir, const char *payload,
3446+
bool *is_worktree, struct strbuf *refdir,
3447+
struct strbuf *ref_common_dir)
3448+
{
3449+
struct strbuf sb = STRBUF_INIT;
3450+
3451+
strbuf_addstr(refdir, gitdir);
3452+
*is_worktree = get_common_dir_noenv(ref_common_dir, gitdir);
3453+
3454+
if (!payload)
3455+
return;
3456+
3457+
if (!is_absolute_path(payload)) {
3458+
strbuf_addf(&sb, "%s/%s", ref_common_dir->buf, payload);
3459+
strbuf_realpath(ref_common_dir, sb.buf, 1);
3460+
} else {
3461+
strbuf_realpath(ref_common_dir, payload, 1);
3462+
}
3463+
3464+
strbuf_reset(refdir);
3465+
strbuf_addbuf(refdir, ref_common_dir);
3466+
3467+
if (*is_worktree) {
3468+
char *wt_id = strrchr(gitdir, '/') + 1;
3469+
strbuf_addf(refdir, "/worktrees/%s", wt_id);
3470+
}
3471+
3472+
strbuf_release(&sb);
3473+
}

refs.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,4 +1428,17 @@ void ref_iterator_free(struct ref_iterator *ref_iterator);
14281428
int do_for_each_ref_iterator(struct ref_iterator *iter,
14291429
each_ref_fn fn, void *cb_data);
14301430

1431+
/*
1432+
* Git only recognizes a directory as a repository if it contains:
1433+
* - HEAD file
1434+
* - refs/ folder
1435+
* While it is necessary within the files backend, newer backends may not
1436+
* follow the same structure. To go around this, we create stubs as necessary.
1437+
*
1438+
* If provided with a 'refs_heads_msg', we create the 'refs/heads/head' file
1439+
* with the provided message.
1440+
*/
1441+
void refs_create_refdir_stubs(struct repository *repo, const char *refdir,
1442+
const char *refs_heads_msg);
1443+
14311444
#endif /* REFS_H */

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

0 commit comments

Comments
 (0)