Merge branch 'ps/refstorage-extension'

Introduce a new extension "refstorage" so that we can mark a
repository that uses a non-default ref backend, like reftable.

* ps/refstorage-extension:
  t9500: write "extensions.refstorage" into config
  builtin/clone: introduce `--ref-format=` value flag
  builtin/init: introduce `--ref-format=` value flag
  builtin/rev-parse: introduce `--show-ref-format` flag
  t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar
  setup: introduce GIT_DEFAULT_REF_FORMAT envvar
  setup: introduce "extensions.refStorage" extension
  setup: set repository's formats on init
  setup: start tracking ref storage format
  refs: refactor logic to look up storage backends
  worktree: skip reading HEAD when repairing worktrees
  t: introduce DEFAULT_REPO_FORMAT prereq
This commit is contained in:
Junio C Hamano 2024-01-16 10:11:57 -08:00
commit 32c6fc3e30
29 changed files with 335 additions and 35 deletions

View File

@ -7,6 +7,17 @@ Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.
extensions.refStorage::
Specify the ref storage format to use. The acceptable values are:
+
include::../ref-storage-format.txt[]
+
It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
+
Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.
extensions.worktreeConfig::
If enabled, then worktrees will load config settings from the
`$GIT_DIR/config.worktree` file in addition to the

View File

@ -311,6 +311,12 @@ or `--mirror` is given)
The result is Git repository can be separated from working
tree.
--ref-format=<ref-format::
Specify the given ref storage format for the repository. The valid values are:
+
include::ref-storage-format.txt[]
-j <n>::
--jobs <n>::
The number of submodules fetched at the same time.

View File

@ -11,6 +11,7 @@ SYNOPSIS
[verse]
'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
[--separate-git-dir <git-dir>] [--object-format=<format>]
[--ref-format=<format>]
[-b <branch-name> | --initial-branch=<branch-name>]
[--shared[=<permissions>]] [<directory>]
@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default.
+
include::object-format-disclaimer.txt[]
--ref-format=<format>::
Specify the given ref storage format for the repository. The valid values are:
+
include::ref-storage-format.txt[]
--template=<template-directory>::
Specify the directory from which templates will be used. (See the "TEMPLATE

View File

@ -307,6 +307,9 @@ The following options are unaffected by `--path-format`:
input, multiple algorithms may be printed, space-separated.
If not specified, the default is "storage".
--show-ref-format::
Show the reference storage format used for the repository.
Other Options
~~~~~~~~~~~~~

View File

@ -556,6 +556,11 @@ double-quotes and respecting backslash escapes. E.g., the value
is always used. The default is "sha1".
See `--object-format` in linkgit:git-init[1].
`GIT_DEFAULT_REF_FORMAT`::
If this variable is set, the default reference backend format for new
repositories will be set to this value. The default is "files".
See `--ref-format` in linkgit:git-init[1].
Git Commits
~~~~~~~~~~~
`GIT_AUTHOR_NAME`::

View File

@ -0,0 +1 @@
* `files` for loose files with packed-refs. This is the default.

View File

@ -100,3 +100,8 @@ If set, by default "git config" reads from both "config" and
multiple working directory mode, "config" file is shared while
"config.worktree" is per-working directory (i.e., it's in
GIT_COMMON_DIR/worktrees/<id>/config.worktree)
==== `refStorage`
Specifies the file format for the ref database. The only valid value
is `files` (loose references with a packed-refs file).

View File

@ -71,6 +71,7 @@ static char *remote_name = NULL;
static char *option_branch = NULL;
static struct string_list option_not = STRING_LIST_INIT_NODUP;
static const char *real_git_dir;
static const char *ref_format;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
@ -156,6 +157,8 @@ static struct option builtin_clone_options[] = {
N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING(0, "ref-format", &ref_format, N_("format"),
N_("specify the reference format to use")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
N_("set config inside the new repository")),
OPT_STRING_LIST(0, "server-option", &server_options,
@ -931,6 +934,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int submodule_progress;
int filter_submodules = 0;
int hash_algo;
unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =
@ -956,6 +960,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_single_branch == -1)
option_single_branch = deepen ? 1 : 0;
if (ref_format) {
ref_storage_format = ref_storage_format_by_name(ref_format);
if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format '%s'"), ref_format);
}
if (option_mirror)
option_bare = 1;
@ -1106,7 +1116,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* repository, and reference backends may persist that information into
* their on-disk data structures.
*/
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
ref_storage_format, NULL,
do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
if (real_git_dir) {
@ -1289,9 +1300,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* ours to the same thing.
*/
hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
initialize_repository_version(hash_algo, 1);
initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
repo_set_hash_algo(the_repository, hash_algo);
create_reference_database(NULL, 1);
create_reference_database(the_repository->ref_storage_format, NULL, 1);
/*
* Before fetching from the remote, download and install bundle

View File

@ -10,6 +10,8 @@
#include "object-file.h"
#include "parse-options.h"
#include "path.h"
#include "refs.h"
#include "repository.h"
#include "setup.h"
#include "strbuf.h"
@ -56,6 +58,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)
static const char *const init_db_usage[] = {
N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"),
NULL
@ -75,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *template_dir = NULL;
unsigned int flags = 0;
const char *object_format = NULL;
const char *ref_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@ -94,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
N_("override the name of the initial branch")),
OPT_STRING(0, "object-format", &object_format, N_("hash"),
N_("specify the hash algorithm to use")),
OPT_STRING(0, "ref-format", &ref_format, N_("format"),
N_("specify the reference format to use")),
OPT_END()
};
@ -157,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
die(_("unknown hash algorithm '%s'"), object_format);
}
if (ref_format) {
ref_storage_format = ref_storage_format_by_name(ref_format);
if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format '%s'"), ref_format);
}
if (init_shared_repository != -1)
set_shared_repository(init_shared_repository);
@ -235,5 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, hash_algo,
initial_branch, init_shared_repository, flags);
ref_storage_format, initial_branch,
init_shared_repository, flags);
}

View File

@ -1062,6 +1062,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
puts(the_hash_algo->name);
continue;
}
if (!strcmp(arg, "--show-ref-format")) {
puts(ref_storage_format_to_name(the_repository->ref_storage_format));
continue;
}
if (!strcmp(arg, "--end-of-options")) {
seen_end_of_options = 1;
if (filter & (DO_FLAGS | DO_REVS))

34
refs.c
View File

@ -33,17 +33,33 @@
/*
* List of all available backends
*/
static struct ref_storage_be *refs_backends = &refs_be_files;
static const struct ref_storage_be *refs_backends[] = {
[REF_STORAGE_FORMAT_FILES] = &refs_be_files,
};
static struct ref_storage_be *find_ref_storage_backend(const char *name)
static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
{
struct ref_storage_be *be;
for (be = refs_backends; be; be = be->next)
if (!strcmp(be->name, name))
return be;
if (ref_storage_format < ARRAY_SIZE(refs_backends))
return refs_backends[ref_storage_format];
return NULL;
}
unsigned int ref_storage_format_by_name(const char *name)
{
for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
return i;
return REF_STORAGE_FORMAT_UNKNOWN;
}
const char *ref_storage_format_to_name(unsigned int ref_storage_format)
{
const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
if (!be)
return "unknown";
return be->name;
}
/*
* How to handle various characters in refnames:
* 0: An acceptable character for refs
@ -2082,12 +2098,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
const char *gitdir,
unsigned int flags)
{
const char *be_name = "files";
struct ref_storage_be *be = find_ref_storage_backend(be_name);
const struct ref_storage_be *be;
struct ref_store *refs;
be = find_ref_storage_backend(repo->ref_storage_format);
if (!be)
BUG("reference backend %s is unknown", be_name);
BUG("reference backend is unknown");
refs = be->init(repo, gitdir, flags);
return refs;

3
refs.h
View File

@ -11,6 +11,9 @@ struct string_list;
struct string_list_item;
struct worktree;
unsigned int ref_storage_format_by_name(const char *name);
const char *ref_storage_format_to_name(unsigned int ref_storage_format);
/*
* Resolve a reference, recursively following symbolic refererences.
*

View File

@ -426,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
}
struct ref_storage_be refs_be_debug = {
.next = NULL,
.name = "debug",
.init = NULL,
.init_db = debug_init_db,

View File

@ -3239,7 +3239,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
}
struct ref_storage_be refs_be_files = {
.next = NULL,
.name = "files",
.init = files_ref_store_create,
.init_db = files_init_db,

View File

@ -1704,7 +1704,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
}
struct ref_storage_be refs_be_packed = {
.next = NULL,
.name = "packed",
.init = packed_ref_store_create,
.init_db = packed_init_db,

View File

@ -663,7 +663,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
struct strbuf *referent);
struct ref_storage_be {
struct ref_storage_be *next;
const char *name;
ref_store_init_fn *init;
ref_init_db_fn *init_db;

View File

@ -104,6 +104,11 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
repo->hash_algo = &hash_algos[hash_algo];
}
void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
{
repo->ref_storage_format = format;
}
/*
* Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
* Return 0 upon success and a non-zero value upon failure.
@ -184,6 +189,7 @@ int repo_init(struct repository *repo,
goto error;
repo_set_hash_algo(repo, format.hash_algo);
repo_set_ref_storage_format(repo, format.ref_storage_format);
repo->repository_format_worktree_config = format.worktree_config;
/* take ownership of format.partial_clone */

View File

@ -24,6 +24,9 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP,
};
#define REF_STORAGE_FORMAT_UNKNOWN 0
#define REF_STORAGE_FORMAT_FILES 1
struct repo_settings {
int initialized;
@ -160,6 +163,9 @@ struct repository {
/* Repository's current hash algorithm, as serialized on disk. */
const struct git_hash_algo *hash_algo;
/* Repository's reference storage format, as serialized on disk. */
unsigned int ref_storage_format;
/* A unique-id for tracing purposes. */
int trace2_repo_id;
@ -199,6 +205,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,
const struct set_gitdir_args *extra_args);
void repo_set_worktree(struct repository *repo, const char *path);
void repo_set_hash_algo(struct repository *repo, int algo);
void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
void initialize_the_repository(void);
RESULT_MUST_BE_USED
int repo_init(struct repository *r, const char *gitdir, const char *worktree);

66
setup.c
View File

@ -591,6 +591,17 @@ static enum extension_result handle_extension(const char *var,
"extensions.objectformat", value);
data->hash_algo = format;
return EXTENSION_OK;
} else if (!strcmp(ext, "refstorage")) {
unsigned int format;
if (!value)
return config_error_nonbool(var);
format = ref_storage_format_by_name(value);
if (format == REF_STORAGE_FORMAT_UNKNOWN)
return error(_("invalid value for '%s': '%s'"),
"extensions.refstorage", value);
data->ref_storage_format = format;
return EXTENSION_OK;
}
return EXTENSION_UNKNOWN;
}
@ -1565,6 +1576,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
}
if (startup_info->have_repository) {
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
repo_set_ref_storage_format(the_repository,
repo_fmt.ref_storage_format);
the_repository->repository_format_worktree_config =
repo_fmt.worktree_config;
/* take ownership of repo_fmt.partial_clone */
@ -1658,6 +1671,8 @@ void check_repository_format(struct repository_format *fmt)
check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1;
repo_set_hash_algo(the_repository, fmt->hash_algo);
repo_set_ref_storage_format(the_repository,
fmt->ref_storage_format);
the_repository->repository_format_worktree_config =
fmt->worktree_config;
the_repository->repository_format_partial_clone =
@ -1866,12 +1881,15 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
return 1;
}
void initialize_repository_version(int hash_algo, int reinit)
void initialize_repository_version(int hash_algo,
unsigned int ref_storage_format,
int reinit)
{
char repo_version_string[10];
int repo_version = GIT_REPO_VERSION;
if (hash_algo != GIT_HASH_SHA1)
if (hash_algo != GIT_HASH_SHA1 ||
ref_storage_format != REF_STORAGE_FORMAT_FILES)
repo_version = GIT_REPO_VERSION_READ;
/* This forces creation of new config file */
@ -1884,6 +1902,10 @@ void initialize_repository_version(int hash_algo, int reinit)
hash_algos[hash_algo].name);
else if (reinit)
git_config_set_gently("extensions.objectformat", NULL);
if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
git_config_set("extensions.refstorage",
ref_storage_format_to_name(ref_storage_format));
}
static int is_reinit(void)
@ -1898,7 +1920,8 @@ static int is_reinit(void)
return ret;
}
void create_reference_database(const char *initial_branch, int quiet)
void create_reference_database(unsigned int ref_storage_format,
const char *initial_branch, int quiet)
{
struct strbuf err = STRBUF_INIT;
int reinit = is_reinit();
@ -1918,6 +1941,7 @@ void create_reference_database(const char *initial_branch, int quiet)
safe_create_dir(git_path("refs"), 1);
adjust_shared_perm(git_path("refs"));
repo_set_ref_storage_format(the_repository, ref_storage_format);
if (refs_init_db(&err))
die("failed to set up refs db: %s", err.buf);
@ -2023,7 +2047,7 @@ static int create_default_files(const char *template_path,
adjust_shared_perm(get_git_dir());
}
initialize_repository_version(fmt->hash_algo, 0);
initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
/* Check filemode trustability */
path = git_path_buf(&buf, "config");
@ -2136,8 +2160,29 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
}
}
static void validate_ref_storage_format(struct repository_format *repo_fmt,
unsigned int format)
{
const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
if (repo_fmt->version >= 0 &&
format != REF_STORAGE_FORMAT_UNKNOWN &&
format != repo_fmt->ref_storage_format) {
die(_("attempt to reinitialize repository with different reference storage format"));
} else if (format != REF_STORAGE_FORMAT_UNKNOWN) {
repo_fmt->ref_storage_format = format;
} else if (name) {
format = ref_storage_format_by_name(name);
if (format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format '%s'"), name);
repo_fmt->ref_storage_format = format;
}
}
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash, const char *initial_branch,
const char *template_dir, int hash,
unsigned int ref_storage_format,
const char *initial_branch,
int init_shared_repository, unsigned int flags)
{
int reinit;
@ -2180,13 +2225,22 @@ int init_db(const char *git_dir, const char *real_git_dir,
check_repository_format(&repo_fmt);
validate_hash_algorithm(&repo_fmt, hash);
validate_ref_storage_format(&repo_fmt, ref_storage_format);
reinit = create_default_files(template_dir, original_git_dir,
&repo_fmt, prev_bare_repository,
init_shared_repository);
/*
* Now that we have set up both the hash algorithm and the ref storage
* format we can update the repository's settings accordingly.
*/
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
if (!(flags & INIT_DB_SKIP_REFDB))
create_reference_database(initial_branch, flags & INIT_DB_QUIET);
create_reference_database(repo_fmt.ref_storage_format,
initial_branch, flags & INIT_DB_QUIET);
create_object_directory();
if (get_shared_repository()) {

10
setup.h
View File

@ -115,6 +115,7 @@ struct repository_format {
int worktree_config;
int is_bare;
int hash_algo;
unsigned int ref_storage_format;
int sparse_index;
char *work_tree;
struct string_list unknown_extensions;
@ -131,6 +132,7 @@ struct repository_format {
.version = -1, \
.is_bare = -1, \
.hash_algo = GIT_HASH_SHA1, \
.ref_storage_format = REF_STORAGE_FORMAT_FILES, \
.unknown_extensions = STRING_LIST_INIT_DUP, \
.v1_only_extensions = STRING_LIST_INIT_DUP, \
}
@ -175,10 +177,14 @@ void check_repository_format(struct repository_format *fmt);
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
unsigned int ref_storage_format,
const char *initial_branch, int init_shared_repository,
unsigned int flags);
void initialize_repository_version(int hash_algo, int reinit);
void create_reference_database(const char *initial_branch, int quiet);
void initialize_repository_version(int hash_algo,
unsigned int ref_storage_format,
int reinit);
void create_reference_database(unsigned int ref_storage_format,
const char *initial_branch, int quiet);
/*
* NOTE NOTE NOTE!!

View File

@ -479,6 +479,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
use in the test scripts. Recognized values for <hash-algo> are "sha1"
and "sha256".
GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format
to use in the test scripts. Recognized values for <format> are "files".
GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
'pack.writeReverseIndex' setting.

View File

@ -532,6 +532,76 @@ test_expect_success 'init rejects attempts to initialize with different hash' '
test_must_fail git -C sha256 init --object-format=sha1
'
test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
test_when_finished "rm -rf refstorage" &&
git init refstorage &&
git -C refstorage config extensions.refStorage files &&
test_must_fail git -C refstorage rev-parse 2>err &&
grep "repo version is 0, but v1-only extension found" err
'
test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
test_when_finished "rm -rf refstorage" &&
git init refstorage &&
git -C refstorage config core.repositoryformatversion 1 &&
git -C refstorage config extensions.refStorage files &&
test_commit -C refstorage A &&
git -C refstorage rev-parse --verify HEAD
'
test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
test_when_finished "rm -rf refstorage" &&
git init refstorage &&
git -C refstorage config core.repositoryformatversion 1 &&
git -C refstorage config extensions.refStorage garbage &&
test_must_fail git -C refstorage rev-parse 2>err &&
grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
'
test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' '
test_when_finished "rm -rf refformat" &&
GIT_DEFAULT_REF_FORMAT=files git init refformat &&
echo 0 >expect &&
git -C refformat config core.repositoryformatversion >actual &&
test_cmp expect actual &&
test_must_fail git -C refformat config extensions.refstorage
'
test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' '
test_when_finished "rm -rf refformat" &&
cat >expect <<-EOF &&
fatal: unknown ref storage format ${SQ}garbage${SQ}
EOF
test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err &&
test_cmp expect err
'
test_expect_success 'init with --ref-format=files' '
test_when_finished "rm -rf refformat" &&
git init --ref-format=files refformat &&
echo files >expect &&
git -C refformat rev-parse --show-ref-format >actual &&
test_cmp expect actual
'
test_expect_success 're-init with same format' '
test_when_finished "rm -rf refformat" &&
git init --ref-format=files refformat &&
git init --ref-format=files refformat &&
echo files >expect &&
git -C refformat rev-parse --show-ref-format >actual &&
test_cmp expect actual
'
test_expect_success 'init with --ref-format=garbage' '
test_when_finished "rm -rf refformat" &&
cat >expect <<-EOF &&
fatal: unknown ref storage format ${SQ}garbage${SQ}
EOF
test_must_fail git init --ref-format=garbage refformat 2>err &&
test_cmp expect err
'
test_expect_success MINGW 'core.hidedotfiles = false' '
git config --global core.hidedotfiles false &&
rm -rf newdir &&

View File

@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' '
grep "unknown mode for --show-object-format: squeamish-ossifrage" err
'
test_expect_success 'rev-parse --show-ref-format' '
test_detect_ref_format >expect &&
git rev-parse --show-ref-format >actual &&
test_cmp expect actual
'
test_expect_success 'rev-parse --show-ref-format with invalid storage' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
git config extensions.refstorage broken &&
test_must_fail git rev-parse --show-ref-format 2>err &&
grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err
)
'
test_expect_success '--show-toplevel from subdir of working tree' '
pwd >expect &&
git -C sub/dir rev-parse --show-toplevel >actual &&

View File

@ -519,7 +519,7 @@ EOF
mv .git/config .git/config-saved
test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
git branch -m q q2 &&
git branch -m q2 q
'

View File

@ -157,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' '
'
test_expect_success 'clone with files ref format' '
test_when_finished "rm -rf ref-storage" &&
git clone --ref-format=files --mirror src ref-storage &&
echo files >expect &&
git -C ref-storage rev-parse --show-ref-format >actual &&
test_cmp expect actual
'
test_expect_success 'clone with garbage ref format' '
cat >expect <<-EOF &&
fatal: unknown ref storage format ${SQ}garbage${SQ}
EOF
test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err &&
test_cmp expect err &&
test_path_is_missing ref-storage
'
test_expect_success 'clone to destination with trailing /' '
git clone src target-1/ &&

View File

@ -627,6 +627,7 @@ test_expect_success \
test_expect_success 'setup' '
version=$(git config core.repositoryformatversion) &&
algo=$(test_might_fail git config extensions.objectformat) &&
refstorage=$(test_might_fail git config extensions.refstorage) &&
cat >.git/config <<-\EOF &&
# testing noval and alternate separator
[gitweb]
@ -637,6 +638,10 @@ test_expect_success 'setup' '
if test -n "$algo"
then
git config extensions.objectformat "$algo"
fi &&
if test -n "$refstorage"
then
git config extensions.refstorage "$refstorage"
fi
'

View File

@ -1659,6 +1659,11 @@ test_detect_hash () {
test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
}
# Detect the hash algorithm in use.
test_detect_ref_format () {
echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
}
# Load common hash metadata and common placeholder object IDs for use with
# test_oid.
test_oid_init () {

View File

@ -542,6 +542,8 @@ export EDITOR
GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
export GIT_DEFAULT_HASH
GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
export GIT_DEFAULT_REF_FORMAT
GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
export GIT_TEST_MERGE_ALGORITHM
@ -1745,7 +1747,14 @@ parisc* | hppa*)
;;
esac
test_set_prereq REFFILES
case "$GIT_DEFAULT_REF_FORMAT" in
files)
test_set_prereq REFFILES;;
*)
echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
exit 1
;;
esac
( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
test -z "$NO_CURL" && test_set_prereq LIBCURL
@ -1936,6 +1945,10 @@ test_lazy_prereq SHA1 '
esac
'
test_lazy_prereq DEFAULT_REPO_FORMAT '
test_have_prereq SHA1,REFFILES
'
# Ensure that no test accidentally triggers a Git command
# that runs the actual maintenance scheduler, affecting a user's
# system permanently.

View File

@ -51,7 +51,7 @@ static void add_head_info(struct worktree *wt)
/**
* get the main worktree
*/
static struct worktree *get_main_worktree(void)
static struct worktree *get_main_worktree(int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf worktree_path = STRBUF_INIT;
@ -70,11 +70,13 @@ static struct worktree *get_main_worktree(void)
*/
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
add_head_info(worktree);
if (!skip_reading_head)
add_head_info(worktree);
return worktree;
}
static struct worktree *get_linked_worktree(const char *id)
static struct worktree *get_linked_worktree(const char *id,
int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
@ -93,7 +95,8 @@ static struct worktree *get_linked_worktree(const char *id)
CALLOC_ARRAY(worktree, 1);
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
add_head_info(worktree);
if (!skip_reading_head)
add_head_info(worktree);
done:
strbuf_release(&path);
@ -118,7 +121,14 @@ static void mark_current_worktree(struct worktree **worktrees)
free(git_dir);
}
struct worktree **get_worktrees(void)
/*
* NEEDSWORK: This function exists so that we can look up metadata of a
* worktree without trying to access any of its internals like the refdb. It
* would be preferable to instead have a corruption-tolerant function for
* retrieving worktree metadata that could be used when the worktree is known
* to not be in a healthy state, e.g. when creating or repairing it.
*/
static struct worktree **get_worktrees_internal(int skip_reading_head)
{
struct worktree **list = NULL;
struct strbuf path = STRBUF_INIT;
@ -128,7 +138,7 @@ struct worktree **get_worktrees(void)
ALLOC_ARRAY(list, alloc);
list[counter++] = get_main_worktree();
list[counter++] = get_main_worktree(skip_reading_head);
strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
dir = opendir(path.buf);
@ -137,7 +147,7 @@ struct worktree **get_worktrees(void)
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
struct worktree *linked = NULL;
if ((linked = get_linked_worktree(d->d_name))) {
if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
ALLOC_GROW(list, counter + 1, alloc);
list[counter++] = linked;
}
@ -151,6 +161,11 @@ struct worktree **get_worktrees(void)
return list;
}
struct worktree **get_worktrees(void)
{
return get_worktrees_internal(0);
}
const char *get_worktree_git_dir(const struct worktree *wt)
{
if (!wt)
@ -591,7 +606,7 @@ static void repair_noop(int iserr UNUSED,
void repair_worktrees(worktree_repair_fn fn, void *cb_data)
{
struct worktree **worktrees = get_worktrees();
struct worktree **worktrees = get_worktrees_internal(1);
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
if (!fn)