Skip to content

Commit ec7d86a

Browse files
KarthikNayakgitster
authored andcommitted
refs: add GIT_REF_URI to specify reference backend and directory
Git allows setting a different object directory via 'GIT_OBJECT_DIRECTORY', but provides no equivalent for references. This asymmetry makes it difficult to test different reference backends or use alternative reference storage locations without modifying the repository structure. Add a new environment variable 'GIT_REF_URI' that specifies both the reference backend and directory path using a URI format: <ref_backend>://<path> When set, this variable is used to obtain the main reference store for all Git commands. The variable is checked in `get_main_ref_store()` when lazily assigning `repo->refs_private`. We cannot initialize this earlier in `repo_set_gitdir()` because the repository's hash algorithm isn't known at that point, and the reftable backend requires this information during initialization. When used with worktrees, the specified directory is treated as the reference directory for all worktree operations. Add a new test file 't1423-ref-backend.sh' to test this environment variable. Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0f4f1f1 commit ec7d86a

File tree

5 files changed

+171
-1
lines changed

5 files changed

+171
-1
lines changed

Documentation/git.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,14 @@ 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_REF_URI`::
588+
Specify which reference backend and path to be used, if not specified the
589+
backend is inferred from the configuration and $GIT_DIR is used as the
590+
path.
591+
+
592+
Expects the format '<ref_backend>://<path>', where the 'backend' specifies the
593+
reference backend and the 'path' specifies the directory used by the backend.
594+
587595
Git Commits
588596
~~~~~~~~~~~
589597
`GIT_AUTHOR_NAME`::

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_REF_URI_ENVIRONMENT "GIT_REF_URI"
4546

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

refs.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,15 +2186,66 @@ static struct ref_store *get_ref_store_for_dir(struct repository *r,
21862186
return maybe_debug_wrap_ref_store(dir, ref_store);
21872187
}
21882188

2189+
static struct ref_store *get_ref_store_from_uri(struct repository *repo,
2190+
const char *uri)
2191+
{
2192+
struct string_list ref_backend_info = STRING_LIST_INIT_DUP;
2193+
enum ref_storage_format format;
2194+
struct ref_store *store = NULL;
2195+
char *format_string;
2196+
char *dir;
2197+
2198+
if (!uri || !uri[0]) {
2199+
error("reference backend uri is empty");
2200+
goto cleanup;
2201+
}
2202+
2203+
if (string_list_split(&ref_backend_info, uri, ":", 2) != 2) {
2204+
error("invalid reference backend uri format '%s'", uri);
2205+
goto cleanup;
2206+
}
2207+
2208+
format_string = ref_backend_info.items[0].string;
2209+
dir = ref_backend_info.items[1].string + 2;
2210+
2211+
if (!dir || !dir[0]) {
2212+
error("invalid path in uri '%s'", uri);
2213+
goto cleanup;
2214+
}
2215+
2216+
format = ref_storage_format_by_name(format_string);
2217+
if (format == REF_STORAGE_FORMAT_UNKNOWN) {
2218+
error("unknown reference backend '%s'", format_string);
2219+
goto cleanup;
2220+
}
2221+
2222+
store = get_ref_store_for_dir(repo, dir, format);
2223+
2224+
cleanup:
2225+
string_list_clear(&ref_backend_info, 0);
2226+
return store;
2227+
}
2228+
21892229
struct ref_store *get_main_ref_store(struct repository *r)
21902230
{
2231+
char *ref_uri;
2232+
21912233
if (r->refs_private)
21922234
return r->refs_private;
21932235

21942236
if (!r->gitdir)
21952237
BUG("attempting to get main_ref_store outside of repository");
21962238

2197-
r->refs_private = get_ref_store_for_dir(r, r->gitdir, r->ref_storage_format);
2239+
ref_uri = getenv(GIT_REF_URI_ENVIRONMENT);
2240+
if (ref_uri) {
2241+
r->refs_private = get_ref_store_from_uri(r, ref_uri);
2242+
if (!r->refs_private)
2243+
die("failed to initialize ref store from URI: %s", ref_uri);
2244+
2245+
} else {
2246+
r->refs_private = get_ref_store_for_dir(r, r->gitdir,
2247+
r->ref_storage_format);
2248+
}
21982249
return r->refs_private;
21992250
}
22002251

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ integration_tests = [
208208
't1420-lost-found.sh',
209209
't1421-reflog-write.sh',
210210
't1422-show-ref-exists.sh',
211+
't1423-ref-backend.sh',
211212
't1430-bad-ref-name.sh',
212213
't1450-fsck.sh',
213214
't1451-fsck-buffer.sh',

t/t1423-ref-backend.sh

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/bin/sh
2+
3+
test_description='Test different reference backend URIs'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'empty uri provided' '
8+
test_when_finished "rm -rf repo" &&
9+
git init --ref-format=files repo &&
10+
(
11+
cd repo &&
12+
GIT_REF_URI="" &&
13+
export GIT_REF_URI &&
14+
! git refs list 2>err &&
15+
test_grep "reference backend uri is empty" err
16+
)
17+
'
18+
19+
test_expect_success 'invalid uri provided' '
20+
test_when_finished "rm -rf repo" &&
21+
git init --ref-format=files repo &&
22+
(
23+
cd repo &&
24+
GIT_REF_URI="reftable@/home/reftable" &&
25+
export GIT_REF_URI &&
26+
! git refs list 2>err &&
27+
test_grep "invalid reference backend uri format" err
28+
)
29+
'
30+
31+
test_expect_success 'empty path in uri' '
32+
test_when_finished "rm -rf repo" &&
33+
git init --ref-format=files repo &&
34+
(
35+
cd repo &&
36+
GIT_REF_URI="reftable://" &&
37+
export GIT_REF_URI &&
38+
! git refs list 2>err &&
39+
test_grep "invalid path in uri" err
40+
)
41+
'
42+
43+
test_expect_success 'unknown reference backend' '
44+
test_when_finished "rm -rf repo" &&
45+
git init --ref-format=files repo &&
46+
(
47+
cd repo &&
48+
GIT_REF_URI="db://.git" &&
49+
export GIT_REF_URI &&
50+
! git refs list 2>err &&
51+
test_grep "unknown reference backend" err
52+
)
53+
'
54+
55+
ref_formats="files reftable"
56+
for from_format in $ref_formats
57+
do
58+
for to_format in $ref_formats
59+
do
60+
if test "$from_format" = "$to_format"
61+
then
62+
continue
63+
fi
64+
65+
test_expect_success 'read from other reference backend' '
66+
test_when_finished "rm -rf repo" &&
67+
git init --ref-format=files repo &&
68+
(
69+
cd repo &&
70+
test_commit 1 &&
71+
test_commit 2 &&
72+
test_commit 3 &&
73+
74+
git refs migrate --dry-run --ref-format=reftable >out &&
75+
REFTABLE_PATH=$(cat out | sed "s/.* ${SQ}\(.*\)${SQ}/\1/") &&
76+
git refs list >expect &&
77+
GIT_REF_URI="reftable://$REFTABLE_PATH" git refs list >actual &&
78+
test_cmp expect actual
79+
)
80+
'
81+
82+
test_expect_success 'write to other reference backend' '
83+
test_when_finished "rm -rf repo" &&
84+
git init --ref-format=files repo &&
85+
(
86+
cd repo &&
87+
test_commit 1 &&
88+
test_commit 2 &&
89+
test_commit 3 &&
90+
91+
git refs migrate --dry-run --ref-format=reftable >out &&
92+
git refs list >expect &&
93+
94+
REFTABLE_PATH=$(cat out | sed "s/.* ${SQ}\(.*\)${SQ}/\1/") &&
95+
GIT_REF_URI="reftable://$REFTABLE_PATH" git tag -d 1 &&
96+
97+
git refs list >actual &&
98+
test_cmp expect actual &&
99+
100+
GIT_REF_URI="reftable://$REFTABLE_PATH" git refs list >expect &&
101+
git refs list >out &&
102+
cat out | grep -v "refs/tags/1" >actual &&
103+
test_cmp expect actual
104+
)
105+
'
106+
done
107+
done
108+
109+
test_done

0 commit comments

Comments
 (0)