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
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 linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues. 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:: extensions.worktreeConfig::
If enabled, then worktrees will load config settings from the If enabled, then worktrees will load config settings from the
`$GIT_DIR/config.worktree` file in addition to 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 The result is Git repository can be separated from working
tree. 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>:: -j <n>::
--jobs <n>:: --jobs <n>::
The number of submodules fetched at the same time. The number of submodules fetched at the same time.

View File

@@ -11,6 +11,7 @@ SYNOPSIS
[verse] [verse]
'git init' [-q | --quiet] [--bare] [--template=<template-directory>] 'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
[--separate-git-dir <git-dir>] [--object-format=<format>] [--separate-git-dir <git-dir>] [--object-format=<format>]
[--ref-format=<format>]
[-b <branch-name> | --initial-branch=<branch-name>] [-b <branch-name> | --initial-branch=<branch-name>]
[--shared[=<permissions>]] [<directory>] [--shared[=<permissions>]] [<directory>]
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default.
+ +
include::object-format-disclaimer.txt[] 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>:: --template=<template-directory>::
Specify the directory from which templates will be used. (See the "TEMPLATE 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. input, multiple algorithms may be printed, space-separated.
If not specified, the default is "storage". If not specified, the default is "storage".
--show-ref-format::
Show the reference storage format used for the repository.
Other Options 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". is always used. The default is "sha1".
See `--object-format` in linkgit:git-init[1]. 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 Commits
~~~~~~~~~~~ ~~~~~~~~~~~
`GIT_AUTHOR_NAME`:: `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 multiple working directory mode, "config" file is shared while
"config.worktree" is per-working directory (i.e., it's in "config.worktree" is per-working directory (i.e., it's in
GIT_COMMON_DIR/worktrees/<id>/config.worktree) 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 char *option_branch = NULL;
static struct string_list option_not = STRING_LIST_INIT_NODUP; static struct string_list option_not = STRING_LIST_INIT_NODUP;
static const char *real_git_dir; static const char *real_git_dir;
static const char *ref_format;
static char *option_upload_pack = "git-upload-pack"; static char *option_upload_pack = "git-upload-pack";
static int option_verbosity; static int option_verbosity;
static int option_progress = -1; static int option_progress = -1;
@@ -156,6 +157,8 @@ static struct option builtin_clone_options[] = {
N_("any cloned submodules will be shallow")), N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")), 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"), OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
N_("set config inside the new repository")), N_("set config inside the new repository")),
OPT_STRING_LIST(0, "server-option", &server_options, 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 submodule_progress;
int filter_submodules = 0; int filter_submodules = 0;
int hash_algo; int hash_algo;
unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
const int do_not_override_repo_unix_permissions = -1; const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options = 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) if (option_single_branch == -1)
option_single_branch = deepen ? 1 : 0; 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) if (option_mirror)
option_bare = 1; 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 * repository, and reference backends may persist that information into
* their on-disk data structures. * 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); do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
if (real_git_dir) { if (real_git_dir) {
@@ -1289,9 +1300,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* ours to the same thing. * ours to the same thing.
*/ */
hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); 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); 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 * Before fetching from the remote, download and install bundle

View File

@@ -10,6 +10,8 @@
#include "object-file.h" #include "object-file.h"
#include "parse-options.h" #include "parse-options.h"
#include "path.h" #include "path.h"
#include "refs.h"
#include "repository.h"
#include "setup.h" #include "setup.h"
#include "strbuf.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[] = { static const char *const init_db_usage[] = {
N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n" N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n" " [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n" " [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"), " [--shared[=<permissions>]] [<directory>]"),
NULL NULL
@@ -75,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *template_dir = NULL; const char *template_dir = NULL;
unsigned int flags = 0; unsigned int flags = 0;
const char *object_format = NULL; const char *object_format = NULL;
const char *ref_format = NULL;
const char *initial_branch = NULL; const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN; int hash_algo = GIT_HASH_UNKNOWN;
unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
int init_shared_repository = -1; int init_shared_repository = -1;
const struct option init_db_options[] = { const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"), 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")), N_("override the name of the initial branch")),
OPT_STRING(0, "object-format", &object_format, N_("hash"), OPT_STRING(0, "object-format", &object_format, N_("hash"),
N_("specify the hash algorithm to use")), 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() 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); 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) if (init_shared_repository != -1)
set_shared_repository(init_shared_repository); 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; flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, hash_algo, 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); puts(the_hash_algo->name);
continue; 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")) { if (!strcmp(arg, "--end-of-options")) {
seen_end_of_options = 1; seen_end_of_options = 1;
if (filter & (DO_FLAGS | DO_REVS)) if (filter & (DO_FLAGS | DO_REVS))

34
refs.c
View File

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

3
refs.h
View File

@@ -11,6 +11,9 @@ struct string_list;
struct string_list_item; struct string_list_item;
struct worktree; 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. * 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 = { struct ref_storage_be refs_be_debug = {
.next = NULL,
.name = "debug", .name = "debug",
.init = NULL, .init = NULL,
.init_db = debug_init_db, .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 = { struct ref_storage_be refs_be_files = {
.next = NULL,
.name = "files", .name = "files",
.init = files_ref_store_create, .init = files_ref_store_create,
.init_db = files_init_db, .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 = { struct ref_storage_be refs_be_packed = {
.next = NULL,
.name = "packed", .name = "packed",
.init = packed_ref_store_create, .init = packed_ref_store_create,
.init_db = packed_init_db, .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 strbuf *referent);
struct ref_storage_be { struct ref_storage_be {
struct ref_storage_be *next;
const char *name; const char *name;
ref_store_init_fn *init; ref_store_init_fn *init;
ref_init_db_fn *init_db; 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]; 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'. * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
* Return 0 upon success and a non-zero value upon failure. * Return 0 upon success and a non-zero value upon failure.
@@ -184,6 +189,7 @@ int repo_init(struct repository *repo,
goto error; goto error;
repo_set_hash_algo(repo, format.hash_algo); 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; repo->repository_format_worktree_config = format.worktree_config;
/* take ownership of format.partial_clone */ /* take ownership of format.partial_clone */

View File

@@ -24,6 +24,9 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP, FETCH_NEGOTIATION_NOOP,
}; };
#define REF_STORAGE_FORMAT_UNKNOWN 0
#define REF_STORAGE_FORMAT_FILES 1
struct repo_settings { struct repo_settings {
int initialized; int initialized;
@@ -160,6 +163,9 @@ struct repository {
/* Repository's current hash algorithm, as serialized on disk. */ /* Repository's current hash algorithm, as serialized on disk. */
const struct git_hash_algo *hash_algo; 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. */ /* A unique-id for tracing purposes. */
int trace2_repo_id; 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); const struct set_gitdir_args *extra_args);
void repo_set_worktree(struct repository *repo, const char *path); void repo_set_worktree(struct repository *repo, const char *path);
void repo_set_hash_algo(struct repository *repo, int algo); 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); void initialize_the_repository(void);
RESULT_MUST_BE_USED RESULT_MUST_BE_USED
int repo_init(struct repository *r, const char *gitdir, const char *worktree); 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); "extensions.objectformat", value);
data->hash_algo = format; data->hash_algo = format;
return EXTENSION_OK; 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; return EXTENSION_UNKNOWN;
} }
@@ -1565,6 +1576,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
} }
if (startup_info->have_repository) { if (startup_info->have_repository) {
repo_set_hash_algo(the_repository, repo_fmt.hash_algo); 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 = the_repository->repository_format_worktree_config =
repo_fmt.worktree_config; repo_fmt.worktree_config;
/* take ownership of repo_fmt.partial_clone */ /* 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); check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1; startup_info->have_repository = 1;
repo_set_hash_algo(the_repository, fmt->hash_algo); 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 = the_repository->repository_format_worktree_config =
fmt->worktree_config; fmt->worktree_config;
the_repository->repository_format_partial_clone = 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; 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]; char repo_version_string[10];
int repo_version = GIT_REPO_VERSION; 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; repo_version = GIT_REPO_VERSION_READ;
/* This forces creation of new config file */ /* 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); hash_algos[hash_algo].name);
else if (reinit) else if (reinit)
git_config_set_gently("extensions.objectformat", NULL); 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) static int is_reinit(void)
@@ -1898,7 +1920,8 @@ static int is_reinit(void)
return ret; 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; struct strbuf err = STRBUF_INIT;
int reinit = is_reinit(); 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); safe_create_dir(git_path("refs"), 1);
adjust_shared_perm(git_path("refs")); adjust_shared_perm(git_path("refs"));
repo_set_ref_storage_format(the_repository, ref_storage_format);
if (refs_init_db(&err)) if (refs_init_db(&err))
die("failed to set up refs db: %s", err.buf); 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()); 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 */ /* Check filemode trustability */
path = git_path_buf(&buf, "config"); 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, 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 init_shared_repository, unsigned int flags)
{ {
int reinit; int reinit;
@@ -2180,13 +2225,22 @@ int init_db(const char *git_dir, const char *real_git_dir,
check_repository_format(&repo_fmt); check_repository_format(&repo_fmt);
validate_hash_algorithm(&repo_fmt, hash); validate_hash_algorithm(&repo_fmt, hash);
validate_ref_storage_format(&repo_fmt, ref_storage_format);
reinit = create_default_files(template_dir, original_git_dir, reinit = create_default_files(template_dir, original_git_dir,
&repo_fmt, prev_bare_repository, &repo_fmt, prev_bare_repository,
init_shared_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)) 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(); create_object_directory();
if (get_shared_repository()) { if (get_shared_repository()) {

10
setup.h
View File

@@ -115,6 +115,7 @@ struct repository_format {
int worktree_config; int worktree_config;
int is_bare; int is_bare;
int hash_algo; int hash_algo;
unsigned int ref_storage_format;
int sparse_index; int sparse_index;
char *work_tree; char *work_tree;
struct string_list unknown_extensions; struct string_list unknown_extensions;
@@ -131,6 +132,7 @@ struct repository_format {
.version = -1, \ .version = -1, \
.is_bare = -1, \ .is_bare = -1, \
.hash_algo = GIT_HASH_SHA1, \ .hash_algo = GIT_HASH_SHA1, \
.ref_storage_format = REF_STORAGE_FORMAT_FILES, \
.unknown_extensions = STRING_LIST_INIT_DUP, \ .unknown_extensions = STRING_LIST_INIT_DUP, \
.v1_only_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, int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo, const char *template_dir, int hash_algo,
unsigned int ref_storage_format,
const char *initial_branch, int init_shared_repository, const char *initial_branch, int init_shared_repository,
unsigned int flags); unsigned int flags);
void initialize_repository_version(int hash_algo, int reinit); void initialize_repository_version(int hash_algo,
void create_reference_database(const char *initial_branch, int quiet); 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!! * 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" use in the test scripts. Recognized values for <hash-algo> are "sha1"
and "sha256". 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 GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
'pack.writeReverseIndex' setting. '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_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' ' test_expect_success MINGW 'core.hidedotfiles = false' '
git config --global core.hidedotfiles false && git config --global core.hidedotfiles false &&
rm -rf newdir && 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 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' ' test_expect_success '--show-toplevel from subdir of working tree' '
pwd >expect && pwd >expect &&
git -C sub/dir rev-parse --show-toplevel >actual && git -C sub/dir rev-parse --show-toplevel >actual &&

View File

@@ -519,7 +519,7 @@ EOF
mv .git/config .git/config-saved 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 q q2 &&
git branch -m q2 q 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 /' ' test_expect_success 'clone to destination with trailing /' '
git clone src target-1/ && git clone src target-1/ &&

View File

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

View File

@@ -1659,6 +1659,11 @@ test_detect_hash () {
test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}" 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 # Load common hash metadata and common placeholder object IDs for use with
# test_oid. # test_oid.
test_oid_init () { test_oid_init () {

View File

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

View File

@@ -51,7 +51,7 @@ static void add_head_info(struct worktree *wt)
/** /**
* get the main worktree * 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 worktree *worktree = NULL;
struct strbuf worktree_path = STRBUF_INIT; 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) || worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository(); is_bare_repository();
if (!skip_reading_head)
add_head_info(worktree); add_head_info(worktree);
return 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 worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT; struct strbuf path = STRBUF_INIT;
@@ -93,6 +95,7 @@ static struct worktree *get_linked_worktree(const char *id)
CALLOC_ARRAY(worktree, 1); CALLOC_ARRAY(worktree, 1);
worktree->path = strbuf_detach(&worktree_path, NULL); worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id); worktree->id = xstrdup(id);
if (!skip_reading_head)
add_head_info(worktree); add_head_info(worktree);
done: done:
@@ -118,7 +121,14 @@ static void mark_current_worktree(struct worktree **worktrees)
free(git_dir); 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 worktree **list = NULL;
struct strbuf path = STRBUF_INIT; struct strbuf path = STRBUF_INIT;
@@ -128,7 +138,7 @@ struct worktree **get_worktrees(void)
ALLOC_ARRAY(list, alloc); 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()); strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
dir = opendir(path.buf); dir = opendir(path.buf);
@@ -137,7 +147,7 @@ struct worktree **get_worktrees(void)
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) { while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
struct worktree *linked = 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); ALLOC_GROW(list, counter + 1, alloc);
list[counter++] = linked; list[counter++] = linked;
} }
@@ -151,6 +161,11 @@ struct worktree **get_worktrees(void)
return list; return list;
} }
struct worktree **get_worktrees(void)
{
return get_worktrees_internal(0);
}
const char *get_worktree_git_dir(const struct worktree *wt) const char *get_worktree_git_dir(const struct worktree *wt)
{ {
if (!wt) if (!wt)
@@ -591,7 +606,7 @@ static void repair_noop(int iserr UNUSED,
void repair_worktrees(worktree_repair_fn fn, void *cb_data) 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 */ struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
if (!fn) if (!fn)