{
int ret = refs->be->create_on_disk(refs, flags, err);
- if (!ret &&
- 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);
+ 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 ret = refs->be->remove_on_disk(refs, err);
- if (!ret &&
- ref_storage_format_by_name(refs->be->name) != REF_STORAGE_FORMAT_FILES) {
+ 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",
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
const char *prefix = NULL;
+ const char *ref_backend_uri;
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
/*
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
}
+ /*
+ * The env variable should override the repository config
+ * for 'extensions.refStorage'.
+ */
+ ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
+ if (ref_backend_uri) {
+ char *backend, *payload;
+ enum ref_storage_format format;
+
+ parse_reference_uri(ref_backend_uri, &backend, &payload);
+ format = ref_storage_format_by_name(backend);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN)
+ die(_("unknown ref storage format: '%s'"), backend);
+ repo_set_ref_storage_format(the_repository, format, payload);
+
+ free(backend);
+ free(payload);
+ }
+
setup_original_cwd();
strbuf_release(&dir);
* the remote repository's format.
*/
if (hash_algo != GIT_HASH_SHA1_LEGACY ||
- ref_storage_format != REF_STORAGE_FORMAT_FILES)
+ ref_storage_format != REF_STORAGE_FORMAT_FILES ||
+ the_repository->ref_storage_payload)
target_version = GIT_REPO_VERSION_READ;
if (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN)
else if (reinit)
repo_config_set_gently(the_repository, "extensions.objectformat", NULL);
- if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+ if (the_repository->ref_storage_payload) {
+ struct strbuf ref_uri = STRBUF_INIT;
+
+ strbuf_addf(&ref_uri, "%s://%s",
+ ref_storage_format_to_name(ref_storage_format),
+ the_repository->ref_storage_payload);
+ repo_config_set(the_repository, "extensions.refstorage", ref_uri.buf);
+ strbuf_release(&ref_uri);
+ } else if (ref_storage_format != REF_STORAGE_FORMAT_FILES) {
repo_config_set(the_repository, "extensions.refstorage",
ref_storage_format_to_name(ref_storage_format));
- else if (reinit)
+ } else if (reinit) {
repo_config_set_gently(the_repository, "extensions.refstorage", NULL);
+ }
if (reinit) {
struct strbuf config = STRBUF_INIT;
.ignore_repo = 1,
.ignore_worktree = 1,
};
+ const char *ref_backend_uri;
const char *env;
config_with_options(read_default_format_config, &cfg, NULL, NULL, &opts);
} else {
repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT;
}
+
+
+ ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
+ if (ref_backend_uri) {
+ char *backend, *payload;
+ enum ref_storage_format format;
+
+ parse_reference_uri(ref_backend_uri, &backend, &payload);
+ format = ref_storage_format_by_name(backend);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN)
+ die(_("unknown ref storage format: '%s'"), backend);
+
+ repo_fmt->ref_storage_format = format;
+ repo_fmt->ref_storage_payload = payload;
+
+ free(backend);
+ }
+
repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format,
repo_fmt->ref_storage_payload);
}
# <backend> is the original ref storage of the repo.
# <uri> is the new URI to be set for the ref storage.
# <cmd> is the git subcommand to be run in the repository.
+# <via> if 'config', set the backend via the 'extensions.refStorage' config.
+# if 'env', set the backend via the 'GIT_REFERENCE_BACKEND' env.
run_with_uri () {
repo=$1 &&
backend=$2 &&
uri=$3 &&
cmd=$4 &&
+ via=$5 &&
- git -C "$repo" config set core.repositoryformatversion 1
- git -C "$repo" config set extensions.refStorage "$uri" &&
- git -C "$repo" $cmd &&
- git -C "$repo" config set extensions.refStorage "$backend"
+ git -C "$repo" config set core.repositoryformatversion 1 &&
+ if test "$via" = "env"
+ then
+ test_env GIT_REFERENCE_BACKEND="$uri" git -C "$repo" $cmd
+ elif test "$via" = "config"
+ then
+ git -C "$repo" config set extensions.refStorage "$uri" &&
+ git -C "$repo" $cmd &&
+ git -C "$repo" config set extensions.refStorage "$backend"
+ fi
}
# Test a repository with a given reference storage by running and comparing
# <repo> is the relative path to the repo to run the command in.
# <backend> is the original ref storage of the repo.
# <uri> is the new URI to be set for the ref storage.
+# <via> if 'config', set the backend via the 'extensions.refStorage' config.
+# if 'env', set the backend via the 'GIT_REFERENCE_BACKEND' env.
# <err_msg> (optional) if set, check if 'git-refs(1)' failed with the provided msg.
test_refs_backend () {
repo=$1 &&
backend=$2 &&
uri=$3 &&
- err_msg=$4 &&
+ via=$4 &&
+ err_msg=$5 &&
+
- git -C "$repo" config set core.repositoryformatversion 1 &&
if test -n "$err_msg";
then
- git -C "$repo" config set extensions.refStorage "$uri" &&
- test_must_fail git -C "$repo" refs list 2>err &&
- test_grep "$err_msg" err
+ if test "$via" = "env"
+ then
+ test_env GIT_REFERENCE_BACKEND="$uri" test_must_fail git -C "$repo" refs list 2>err
+ elif test "$via" = "config"
+ then
+ git -C "$repo" config set extensions.refStorage "$uri" &&
+ test_must_fail git -C "$repo" refs list 2>err &&
+ test_grep "$err_msg" err
+ fi
else
git -C "$repo" refs list >expect &&
- run_with_uri "$repo" "$backend" "$uri" "refs list" >actual &&
+ run_with_uri "$repo" "$backend" "$uri" "refs list" "$via">actual &&
test_cmp expect actual
fi
}
-test_expect_success 'URI is invalid' '
+# Verify that the expected files are present in the gitdir and the refsdir.
+# Usage: verify_files_exist <gitdir> <refdir>
+# <gitdir> is the path for the gitdir.
+# <refdir> is the path for the refdir.
+verify_files_exist () {
+ gitdir=$1 &&
+ refdir=$2 &&
+
+ # verify that the stubs were added to the $GITDIR.
+ echo "repository uses alternate refs storage" >expect &&
+ test_cmp expect $gitdir/refs/heads &&
+ echo "ref: refs/heads/.invalid" >expect &&
+ test_cmp expect $gitdir/HEAD
+
+ # verify that backend specific files exist.
+ case "$GIT_DEFAULT_REF_FORMAT" in
+ files)
+ test_path_is_dir $refdir/refs/heads &&
+ test_path_is_file $refdir/HEAD;;
+ reftable)
+ test_path_is_dir $refdir/reftable &&
+ test_path_is_file $refdir/reftable/tables.list;;
+ *)
+ BUG "unhandled ref format $GIT_DEFAULT_REF_FORMAT";;
+ esac
+}
+
+methods="config env"
+for method in $methods
+do
+
+test_expect_success "$method: URI is invalid" '
test_when_finished "rm -rf repo" &&
git init repo &&
- test_refs_backend repo files "reftable@/home/reftable" \
+ test_refs_backend repo files "reftable@/home/reftable" "$method" \
"invalid value for ${SQ}extensions.refstorage${SQ}"
'
-test_expect_success 'URI ends with colon' '
+test_expect_success "$method: URI ends with colon" '
test_when_finished "rm -rf repo" &&
git init repo &&
- test_refs_backend repo files "reftable:" \
+ test_refs_backend repo files "reftable:" "$method" \
"invalid value for ${SQ}extensions.refstorage${SQ}"
'
-test_expect_success 'unknown reference backend' '
+test_expect_success "$method: unknown reference backend" '
test_when_finished "rm -rf repo" &&
git init repo &&
- test_refs_backend repo files "db://.git" \
+ test_refs_backend repo files "db://.git" "$method" \
"invalid value for ${SQ}extensions.refstorage${SQ}"
'
for dir in "$(pwd)/repo/.git" "."
do
- test_expect_success "read from $to_format backend, $dir dir" '
+ test_expect_success "$method: read from $to_format backend, $dir dir" '
test_when_finished "rm -rf repo" &&
git init --ref-format=$from_format repo &&
(
)
'
- test_expect_success "write to $to_format backend, $dir dir" '
+ test_expect_success "$method: write to $to_format backend, $dir dir" '
test_when_finished "rm -rf repo" &&
git init --ref-format=$from_format repo &&
(
git refs migrate --dry-run --ref-format=$to_format >out &&
BACKEND_PATH="$dir/$(sed "s/.* ${SQ}.git\/\(.*\)${SQ}/\1/" out)" &&
- test_refs_backend . $from_format "$to_format://$BACKEND_PATH" &&
+ test_refs_backend . $from_format "$to_format://$BACKEND_PATH" "$method" &&
git refs list >expect &&
- run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" "tag -d 1" &&
+ run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \
+ "tag -d 1" "$method" &&
git refs list >actual &&
test_cmp expect actual &&
git refs list | grep -v "refs/tags/1" >expect &&
- run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" "refs list" >actual &&
+ run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \
+ "refs list" "$method" >actual &&
test_cmp expect actual
)
'
- test_expect_success "with worktree and $to_format backend, $dir dir" '
+ test_expect_success "$method: with worktree and $to_format backend, $dir dir" '
test_when_finished "rm -rf repo wt" &&
git init --ref-format=$from_format repo &&
(
git refs migrate --dry-run --ref-format=$to_format >out &&
BACKEND_PATH="$dir/$(sed "s/.* ${SQ}.git\/\(.*\)${SQ}/\1/" out)" &&
- git config set core.repositoryformatversion 1 &&
- git config set extensions.refStorage "$to_format://$BACKEND_PATH" &&
-
- git worktree add ../wt 2
- ) &&
+ run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \
+ "worktree add ../wt 2" "$method" &&
- git -C repo for-each-ref --include-root-refs >expect &&
- git -C wt for-each-ref --include-root-refs >expect &&
- ! test_cmp expect actual &&
+ run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \
+ "for-each-ref --include-root-refs" "$method" >actual &&
+ run_with_uri ../wt "$from_format" "$to_format://$BACKEND_PATH" \
+ "for-each-ref --include-root-refs" "$method" >expect &&
+ ! test_cmp expect actual &&
- git -C wt rev-parse 2 >expect &&
- git -C wt rev-parse HEAD >actual &&
- test_cmp expect actual
+ run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \
+ "rev-parse 2" "$method" >actual &&
+ run_with_uri ../wt "$from_format" "$to_format://$BACKEND_PATH" \
+ "rev-parse HEAD" "$method" >expect &&
+ test_cmp expect actual
+ )
'
done # closes dir
+
+ test_expect_success "migrating repository to $to_format with alternate refs directory" '
+ test_when_finished "rm -rf repo refdir" &&
+ mkdir refdir &&
+ GIT_REFERENCE_BACKEND="${from_format}://$(pwd)/refdir" git init repo &&
+ (
+ cd repo &&
+
+ test_commit 1 &&
+ test_commit 2 &&
+ test_commit 3 &&
+
+ git refs migrate --ref-format=$to_format &&
+ git refs list >out &&
+ test_grep "refs/tags/1" out &&
+ test_grep "refs/tags/2" out &&
+ test_grep "refs/tags/3" out
+ )
+ '
+
done # closes to_format
done # closes from_format
+done # closes method
+
+test_expect_success 'initializing repository with alt ref directory' '
+ test_when_finished "rm -rf repo refdir" &&
+ mkdir refdir &&
+ BACKEND="$(test_detect_ref_format)://$(pwd)/refdir" &&
+ GIT_REFERENCE_BACKEND=$BACKEND git init repo &&
+ verify_files_exist repo/.git refdir &&
+ (
+ cd repo &&
+
+ git config get extensions.refstorage >actual &&
+ echo $BACKEND >expect &&
+ test_cmp expect actual &&
+
+ test_commit 1 &&
+ test_commit 2 &&
+ test_commit 3 &&
+ git refs list >out &&
+ test_grep "refs/tags/1" out &&
+ test_grep "refs/tags/2" out &&
+ test_grep "refs/tags/3" out
+ )
+'
+
+test_expect_success 'cloning repository with alt ref directory' '
+ test_when_finished "rm -rf source repo refdir" &&
+ mkdir refdir &&
+
+ git init source &&
+ test_commit -C source 1 &&
+ test_commit -C source 2 &&
+ test_commit -C source 3 &&
+
+ BACKEND="$(test_detect_ref_format)://$(pwd)/refdir" &&
+ GIT_REFERENCE_BACKEND=$BACKEND git clone source repo &&
+
+ git -C repo config get extensions.refstorage >actual &&
+ echo $BACKEND >expect &&
+ test_cmp expect actual &&
+
+ verify_files_exist repo/.git refdir &&
+
+ git -C source for-each-ref refs/tags/ >expect &&
+ git -C repo for-each-ref refs/tags/ >actual &&
+ test_cmp expect actual
+'
+
test_done