Skip to content

Commit 7d3a110

Browse files
KarthikNayakgitster
authored andcommitted
refs: allow reference location in refstorage config
The 'extensions.refStorage' config is used to specify the reference backend for a given repository. Both the 'files' and 'reftable' backends utilize the $GIT_DIR as the reference folder by default in `get_main_ref_store()`. Since the reference backends are pluggable, this means that they could work with out-of-tree reference directories too. Extend the 'refStorage' config to also support taking an URI input, where users can specify the reference backend and the location. Add the required changes to obtain and propagate this value to the individual backends. Add the necessary documentation and tests. Traditionally, for linked worktrees, 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 the main worktree references in the new path, and the linked worktree references in the $GIT_DIR. So, let's store linked worktree references in '$ALTERNATE_REFERENCE_DIR/worktrees/<wt_id>'. To do this, create the necessary files and folders while also adding stubs in the $GIT_DIR path to ensure that it is still considered a Git directory. Ideally, we would want to pass in a `struct worktree *` to individual backends, instead of passing the `gitdir`. This allows them to handle worktree specific logic. Currently, that is not possible since the worktree code is: - Tied to using the global `the_repository` variable. - Is not setup before the reference database during initialization of the repository. Add a TODO in 'refs.c' to ensure we can eventually make that change. 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 94a851d commit 7d3a110

11 files changed

Lines changed: 461 additions & 21 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 a corresponding payload. The value
61+
can be either a format name or a URI:
6162
+
6263
--
64+
* A format name alone (e.g., `reftable` or `files`).
65+
66+
* A URI format `<format>://<payload>` explicitly specifies both the
67+
format and payload (e.g., `reftable:///foo/bar`).
68+
69+
Supported format names are:
70+
+
6371
include::../ref-storage-format.adoc[]
72+
+
73+
The payload is passed directly to the reference backend. For the files and
74+
reftable backends, this must be a filesystem path where the references will
75+
be stored. Defaulting to the commondir when no payload is provided. Relative
76+
paths are resolved relative to the $GIT_DIR. Future backends may support
77+
other payload schemes, 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+
details. Overrides the config variable when used.
591+
587592
Git Commits
588593
~~~~~~~~~~~
589594
`GIT_AUTHOR_NAME`::

builtin/worktree.c

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

428+
/*
429+
* References for worktrees 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.
435+
*/
436+
static void setup_alternate_ref_dir(struct worktree *wt, const char *wt_git_path)
437+
{
438+
struct strbuf sb = STRBUF_INIT;
439+
char *path;
440+
441+
path = wt->repo->ref_storage_payload;
442+
if (!path)
443+
return;
444+
445+
if (!is_absolute_path(path))
446+
strbuf_addf(&sb, "%s/", wt->repo->commondir);
447+
448+
strbuf_addf(&sb, "%s/worktrees", path);
449+
safe_create_dir(wt->repo, sb.buf, 1);
450+
strbuf_addf(&sb, "/%s", wt->id);
451+
safe_create_dir(wt->repo, sb.buf, 1);
452+
strbuf_reset(&sb);
453+
454+
strbuf_addf(&sb, "this worktree stores references in %s/worktrees/%s",
455+
path, wt->id);
456+
refs_create_refdir_stubs(wt->repo, wt_git_path, sb.buf);
457+
458+
strbuf_release(&sb);
459+
}
460+
428461
static int add_worktree(const char *path, const char *refname,
429462
const struct add_opts *opts)
430463
{
@@ -518,6 +551,7 @@ static int add_worktree(const char *path, const char *refname,
518551
ret = error(_("could not find created worktree '%s'"), name);
519552
goto done;
520553
}
554+
setup_alternate_ref_dir(wt, sb_repo.buf);
521555
wt_refs = get_worktree_ref_store(wt);
522556

523557
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
@@ -42,6 +42,7 @@
4242
#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
4343
#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
4444
#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
45+
#define GIT_REFERENCE_BACKEND_ENVIRONMENT "GIT_REFERENCE_BACKEND"
4546

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

refs.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,13 +2192,17 @@ int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *e
21922192
{
21932193
int ret = refs->be->create_on_disk(refs, flags, err);
21942194

2195-
if (!ret &&
2196-
ref_storage_format_by_name(refs->be->name) != REF_STORAGE_FORMAT_FILES) {
2197-
struct strbuf msg = STRBUF_INIT;
2198-
2199-
strbuf_addf(&msg, "this repository uses the %s format", refs->be->name);
2200-
refs_create_refdir_stubs(refs->repo, refs->gitdir, msg.buf);
2201-
strbuf_release(&msg);
2195+
if (!ret) {
2196+
/* Creation of stubs for linked worktrees are handled in the worktree code. */
2197+
if (!(flags & REF_STORE_CREATE_ON_DISK_IS_WORKTREE) && refs->repo->ref_storage_payload) {
2198+
refs_create_refdir_stubs(refs->repo, refs->repo->gitdir,
2199+
"repository uses alternate refs storage");
2200+
} else if (ref_storage_format_by_name(refs->be->name) != REF_STORAGE_FORMAT_FILES) {
2201+
struct strbuf msg = STRBUF_INIT;
2202+
strbuf_addf(&msg, "this repository uses the %s format", refs->be->name);
2203+
refs_create_refdir_stubs(refs->repo, refs->gitdir, msg.buf);
2204+
strbuf_release(&msg);
2205+
}
22022206
}
22032207

22042208
return ret;
@@ -2208,10 +2212,18 @@ int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err)
22082212
{
22092213
int ret = refs->be->remove_on_disk(refs, err);
22102214

2211-
if (!ret &&
2212-
ref_storage_format_by_name(refs->be->name) != REF_STORAGE_FORMAT_FILES) {
2215+
if (!ret) {
2216+
enum ref_storage_format format = ref_storage_format_by_name(refs->be->name);
22132217
struct strbuf sb = STRBUF_INIT;
22142218

2219+
/* Backends apart from the files backend create stubs. */
2220+
if (format == REF_STORAGE_FORMAT_FILES)
2221+
return ret;
2222+
2223+
/* Alternate refs backend require stubs in the gitdir. */
2224+
if (refs->repo->ref_storage_payload)
2225+
return ret;
2226+
22152227
strbuf_addf(&sb, "%s/HEAD", refs->gitdir);
22162228
if (unlink(sb.buf) < 0) {
22172229
strbuf_addf(err, "could not delete stub HEAD: %s",
@@ -2291,7 +2303,11 @@ static struct ref_store *ref_store_init(struct repository *repo,
22912303
if (!be)
22922304
BUG("reference backend is unknown");
22932305

2294-
refs = be->init(repo, NULL, gitdir, flags);
2306+
/*
2307+
* TODO Send in a 'struct worktree' instead of a 'gitdir', and
2308+
* allow the backend to handle how it wants to deal with worktrees.
2309+
*/
2310+
refs = be->init(repo, repo->ref_storage_payload, gitdir, flags);
22952311
return refs;
22962312
}
22972313

repository.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,12 @@ void repo_set_compat_hash_algo(struct repository *repo, int algo)
193193
}
194194

195195
void repo_set_ref_storage_format(struct repository *repo,
196-
enum ref_storage_format format)
196+
enum ref_storage_format format,
197+
const char *payload)
197198
{
198199
repo->ref_storage_format = format;
200+
free(repo->ref_storage_payload);
201+
repo->ref_storage_payload = xstrdup_or_null(payload);
199202
}
200203

201204
/*
@@ -277,7 +280,8 @@ int repo_init(struct repository *repo,
277280

278281
repo_set_hash_algo(repo, format.hash_algo);
279282
repo_set_compat_hash_algo(repo, format.compat_hash_algo);
280-
repo_set_ref_storage_format(repo, format.ref_storage_format);
283+
repo_set_ref_storage_format(repo, format.ref_storage_format,
284+
format.ref_storage_payload);
281285
repo->repository_format_worktree_config = format.worktree_config;
282286
repo->repository_format_relative_worktrees = format.relative_worktrees;
283287
repo->repository_format_precious_objects = format.precious_objects;
@@ -369,6 +373,7 @@ void repo_clear(struct repository *repo)
369373
FREE_AND_NULL(repo->index_file);
370374
FREE_AND_NULL(repo->worktree);
371375
FREE_AND_NULL(repo->submodule_prefix);
376+
FREE_AND_NULL(repo->ref_storage_payload);
372377

373378
odb_free(repo->objects);
374379
repo->objects = NULL;

repository.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ struct repository {
150150

151151
/* Repository's reference storage format, as serialized on disk. */
152152
enum ref_storage_format ref_storage_format;
153+
/*
154+
* Reference storage information as needed for the backend. This contains
155+
* only the payload from the reference URI without the schema.
156+
*/
157+
char *ref_storage_payload;
153158

154159
/* A unique-id for tracing purposes. */
155160
int trace2_repo_id;
@@ -204,7 +209,8 @@ void repo_set_worktree(struct repository *repo, const char *path);
204209
void repo_set_hash_algo(struct repository *repo, int algo);
205210
void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
206211
void repo_set_ref_storage_format(struct repository *repo,
207-
enum ref_storage_format format);
212+
enum ref_storage_format format,
213+
const char *payload);
208214
void initialize_repository(struct repository *repo);
209215
RESULT_MUST_BE_USED
210216
int repo_init(struct repository *r, const char *gitdir, const char *worktree);

setup.c

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,21 @@ static enum extension_result handle_extension_v0(const char *var,
632632
return EXTENSION_UNKNOWN;
633633
}
634634

635+
static void parse_reference_uri(const char *value, char **format,
636+
char **payload)
637+
{
638+
const char *schema_end;
639+
640+
schema_end = strstr(value, "://");
641+
if (!schema_end) {
642+
*format = xstrdup(value);
643+
*payload = NULL;
644+
} else {
645+
*format = xstrndup(value, schema_end - value);
646+
*payload = xstrdup_or_null(schema_end + 3);
647+
}
648+
}
649+
635650
/*
636651
* Record any new extensions in this function.
637652
*/
@@ -674,10 +689,17 @@ static enum extension_result handle_extension(const char *var,
674689
return EXTENSION_OK;
675690
} else if (!strcmp(ext, "refstorage")) {
676691
unsigned int format;
692+
char *format_str;
677693

678694
if (!value)
679695
return config_error_nonbool(var);
680-
format = ref_storage_format_by_name(value);
696+
697+
parse_reference_uri(value, &format_str,
698+
&data->ref_storage_payload);
699+
700+
format = ref_storage_format_by_name(format_str);
701+
free(format_str);
702+
681703
if (format == REF_STORAGE_FORMAT_UNKNOWN)
682704
return error(_("invalid value for '%s': '%s'"),
683705
"extensions.refstorage", value);
@@ -850,6 +872,7 @@ void clear_repository_format(struct repository_format *format)
850872
string_list_clear(&format->v1_only_extensions, 0);
851873
free(format->work_tree);
852874
free(format->partial_clone);
875+
free(format->ref_storage_payload);
853876
init_repository_format(format);
854877
}
855878

@@ -1815,6 +1838,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
18151838
static struct strbuf cwd = STRBUF_INIT;
18161839
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
18171840
const char *prefix = NULL;
1841+
const char *ref_backend_uri;
18181842
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
18191843

18201844
/*
@@ -1942,7 +1966,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
19421966
repo_set_compat_hash_algo(the_repository,
19431967
repo_fmt.compat_hash_algo);
19441968
repo_set_ref_storage_format(the_repository,
1945-
repo_fmt.ref_storage_format);
1969+
repo_fmt.ref_storage_format,
1970+
repo_fmt.ref_storage_payload);
19461971
the_repository->repository_format_worktree_config =
19471972
repo_fmt.worktree_config;
19481973
the_repository->repository_format_relative_worktrees =
@@ -1971,6 +1996,25 @@ const char *setup_git_directory_gently(int *nongit_ok)
19711996
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
19721997
}
19731998

1999+
/*
2000+
* The env variable should override the repository config
2001+
* for 'extensions.refStorage'.
2002+
*/
2003+
ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
2004+
if (ref_backend_uri) {
2005+
char *backend, *payload;
2006+
enum ref_storage_format format;
2007+
2008+
parse_reference_uri(ref_backend_uri, &backend, &payload);
2009+
format = ref_storage_format_by_name(backend);
2010+
if (format == REF_STORAGE_FORMAT_UNKNOWN)
2011+
die(_("unknown ref storage format: '%s'"), backend);
2012+
repo_set_ref_storage_format(the_repository, format, payload);
2013+
2014+
free(backend);
2015+
free(payload);
2016+
}
2017+
19742018
setup_original_cwd();
19752019

19762020
strbuf_release(&dir);
@@ -2042,7 +2086,8 @@ void check_repository_format(struct repository_format *fmt)
20422086
repo_set_hash_algo(the_repository, fmt->hash_algo);
20432087
repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
20442088
repo_set_ref_storage_format(the_repository,
2045-
fmt->ref_storage_format);
2089+
fmt->ref_storage_format,
2090+
fmt->ref_storage_payload);
20462091
the_repository->repository_format_worktree_config =
20472092
fmt->worktree_config;
20482093
the_repository->repository_format_relative_worktrees =
@@ -2312,7 +2357,8 @@ void initialize_repository_version(int hash_algo,
23122357
* the remote repository's format.
23132358
*/
23142359
if (hash_algo != GIT_HASH_SHA1_LEGACY ||
2315-
ref_storage_format != REF_STORAGE_FORMAT_FILES)
2360+
ref_storage_format != REF_STORAGE_FORMAT_FILES ||
2361+
the_repository->ref_storage_payload)
23162362
target_version = GIT_REPO_VERSION_READ;
23172363

23182364
if (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN)
@@ -2321,11 +2367,20 @@ void initialize_repository_version(int hash_algo,
23212367
else if (reinit)
23222368
repo_config_set_gently(the_repository, "extensions.objectformat", NULL);
23232369

2324-
if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
2370+
if (the_repository->ref_storage_payload) {
2371+
struct strbuf ref_uri = STRBUF_INIT;
2372+
2373+
strbuf_addf(&ref_uri, "%s://%s",
2374+
ref_storage_format_to_name(ref_storage_format),
2375+
the_repository->ref_storage_payload);
2376+
repo_config_set(the_repository, "extensions.refstorage", ref_uri.buf);
2377+
strbuf_release(&ref_uri);
2378+
} else if (ref_storage_format != REF_STORAGE_FORMAT_FILES) {
23252379
repo_config_set(the_repository, "extensions.refstorage",
23262380
ref_storage_format_to_name(ref_storage_format));
2327-
else if (reinit)
2381+
} else if (reinit) {
23282382
repo_config_set_gently(the_repository, "extensions.refstorage", NULL);
2383+
}
23292384

23302385
if (reinit) {
23312386
struct strbuf config = STRBUF_INIT;
@@ -2598,6 +2653,7 @@ static void repository_format_configure(struct repository_format *repo_fmt,
25982653
.ignore_repo = 1,
25992654
.ignore_worktree = 1,
26002655
};
2656+
const char *ref_backend_uri;
26012657
const char *env;
26022658

26032659
config_with_options(read_default_format_config, &cfg, NULL, NULL, &opts);
@@ -2643,7 +2699,26 @@ static void repository_format_configure(struct repository_format *repo_fmt,
26432699
} else {
26442700
repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT;
26452701
}
2646-
repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format);
2702+
2703+
2704+
ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
2705+
if (ref_backend_uri) {
2706+
char *backend, *payload;
2707+
enum ref_storage_format format;
2708+
2709+
parse_reference_uri(ref_backend_uri, &backend, &payload);
2710+
format = ref_storage_format_by_name(backend);
2711+
if (format == REF_STORAGE_FORMAT_UNKNOWN)
2712+
die(_("unknown ref storage format: '%s'"), backend);
2713+
2714+
repo_fmt->ref_storage_format = format;
2715+
repo_fmt->ref_storage_payload = payload;
2716+
2717+
free(backend);
2718+
}
2719+
2720+
repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format,
2721+
repo_fmt->ref_storage_payload);
26472722
}
26482723

26492724
int init_db(const char *git_dir, const char *real_git_dir,

setup.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ struct repository_format {
171171
int hash_algo;
172172
int compat_hash_algo;
173173
enum ref_storage_format ref_storage_format;
174+
char *ref_storage_payload;
174175
int sparse_index;
175176
char *work_tree;
176177
struct string_list unknown_extensions;

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)