Merge branch 'ps/setup-centralize-odb-creation' into next

The setup logic to discover and configure repositories has been
refactored, and the initialization of the object database has been
centralized.

* ps/setup-centralize-odb-creation:
  setup: construct object database in `apply_repository_format()`
  repository: stop reading loose object map twice on repo init
  setup: stop initializing object database without repository
  setup: stop creating the object database in `setup_git_env()`
  repository: stop initializing the object database in `repo_set_gitdir()`
  setup: deduplicate logic to apply repository format
  setup: drop `setup_git_env()`
  t0001: plug test gaps for git-init(1) with GIT_OBJECT_DIRECTORY
This commit is contained in:
Junio C Hamano
2026-06-09 10:11:12 +09:00
8 changed files with 118 additions and 100 deletions

View File

@@ -740,13 +740,13 @@ static struct commit_graph *prepare_commit_graph(struct repository *r)
struct odb_source *source;
/*
* Early return if there is no git dir or if the commit graph is
* Early return if there is no object database or if the commit graph is
* disabled.
*
* This must come before the "already attempted?" check below, because
* we want to disable even an already-loaded graph file.
*/
if (!r->gitdir || r->commit_graph_disabled)
if (!r->objects || r->commit_graph_disabled)
return NULL;
if (r->objects->commit_graph_attempted)

View File

@@ -130,13 +130,6 @@ void repo_config_values_init(struct repo_config_values *cfg);
* `the_repository`. We should eventually get rid of these and make the
* dependency on a repository explicit:
*
* - `setup_git_env()` ideally shouldn't exist as it modifies global state,
* namely the environment. The current process shouldn't ever access that
* state via envvars though, but should instead consult a `struct
* repository`. When spawning new processes, we would ideally also pass a
* `struct repository` and then set up the environment variables for the
* child process, only.
*
* - `have_git_dir()` should not have to exist at all. Instead, we should
* decide on whether or not we have a `struct repository`.
*
@@ -147,6 +140,7 @@ void repo_config_values_init(struct repo_config_values *cfg);
* Please do not add new global config variables here.
*/
# ifdef USE_THE_REPOSITORY_VARIABLE
/*
* Returns true iff we have a configured git repository (either via
* setup_git_directory, or in the environment via $GIT_DIR).

3
refs.c
View File

@@ -126,7 +126,8 @@ struct ref_namespace_info ref_namespace[] = {
* points to the content of another. Unlike the other
* ref namespaces, this one can be changed by the
* GIT_REPLACE_REF_BASE environment variable. This
* .namespace value will be overwritten in setup_git_env().
* .namespace value will be overwritten during repository
* setup.
*/
.ref = "refs/replace/",
.decoration = DECORATION_GRAFTED,

View File

@@ -181,12 +181,6 @@ void repo_set_gitdir(struct repository *repo,
free(old_gitdir);
repo_set_commondir(repo, o->commondir);
if (!repo->objects)
repo->objects = odb_new(repo, o->object_dir, o->alternate_db);
else if (!o->skip_initializing_odb)
BUG("cannot reinitialize an already-initialized object directory");
repo->disable_ref_updates = o->disable_ref_updates;
expand_base_dir(&repo->graft_file, o->graft_file,
@@ -262,8 +256,8 @@ void repo_set_worktree(struct repository *repo, const char *path)
trace2_def_repo(repo);
}
static int read_and_verify_repository_format(struct repository_format *format,
const char *commondir)
static int read_repository_format_from_commondir(struct repository_format *format,
const char *commondir)
{
int ret = 0;
struct strbuf sb = STRBUF_INIT;
@@ -272,11 +266,6 @@ static int read_and_verify_repository_format(struct repository_format *format,
read_repository_format(format, sb.buf);
strbuf_reset(&sb);
if (verify_repository_format(format, &sb) < 0) {
warning("%s", sb.buf);
ret = -1;
}
strbuf_release(&sb);
return ret;
}
@@ -290,6 +279,8 @@ int repo_init(struct repository *repo,
const char *worktree)
{
struct repository_format format = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;
memset(repo, 0, sizeof(*repo));
initialize_repository(repo);
@@ -297,33 +288,24 @@ int repo_init(struct repository *repo,
if (repo_init_gitdir(repo, gitdir))
goto error;
if (read_and_verify_repository_format(&format, repo->commondir))
if (read_repository_format_from_commondir(&format, repo->commondir))
goto error;
repo_set_hash_algo(repo, format.hash_algo);
repo_set_compat_hash_algo(repo, format.compat_hash_algo);
repo_set_ref_storage_format(repo, format.ref_storage_format,
format.ref_storage_payload);
repo->repository_format_worktree_config = format.worktree_config;
repo->repository_format_relative_worktrees = format.relative_worktrees;
repo->repository_format_precious_objects = format.precious_objects;
repo->repository_format_submodule_path_cfg = format.submodule_path_cfg;
/* take ownership of format.partial_clone */
repo->repository_format_partial_clone = format.partial_clone;
format.partial_clone = NULL;
if (apply_repository_format(repo, &format, 0, &err) < 0) {
warning("%s", err.buf);
goto error;
}
if (worktree)
repo_set_worktree(repo, worktree);
if (repo->compat_hash_algo)
repo_read_loose_object_map(repo);
clear_repository_format(&format);
strbuf_release(&err);
return 0;
error:
clear_repository_format(&format);
strbuf_release(&err);
repo_clear(repo);
return -1;
}

View File

@@ -221,12 +221,9 @@ const char *repo_get_work_tree(struct repository *repo);
*/
struct set_gitdir_args {
const char *commondir;
const char *object_dir;
const char *graft_file;
const char *index_file;
const char *alternate_db;
bool disable_ref_updates;
bool skip_initializing_odb;
};
void repo_set_gitdir(struct repository *repo, const char *root,

130
setup.c
View File

@@ -750,8 +750,7 @@ static int check_repo_format(const char *var, const char *value,
return read_worktree_config(var, value, ctx, vdata);
}
static int check_repository_format_gently(struct repository *repo,
const char *gitdir,
static int check_repository_format_gently(const char *gitdir,
struct repository_format *candidate,
int *nongit_ok)
{
@@ -765,7 +764,7 @@ static int check_repository_format_gently(struct repository *repo,
strbuf_release(&sb);
/*
* For historical use of check_repository_format() in git-init,
* For historical use of check_and_apply_repository_format() in git-init,
* we treat a missing config as a silent "ok", even when nongit_ok
* is unset.
*/
@@ -782,8 +781,6 @@ static int check_repository_format_gently(struct repository *repo,
die("%s", err.buf);
}
repo->repository_format_precious_objects = candidate->precious_objects;
string_list_clear(&candidate->unknown_extensions, 0);
string_list_clear(&candidate->v1_only_extensions, 0);
@@ -1038,8 +1035,7 @@ cleanup_return:
}
static void setup_git_env_internal(struct repository *repo,
const char *git_dir,
bool skip_initializing_odb)
const char *git_dir)
{
char *git_replace_ref_base;
const char *shallow_file;
@@ -1048,13 +1044,10 @@ static void setup_git_env_internal(struct repository *repo,
struct strvec to_free = STRVEC_INIT;
args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
if (getenv(GIT_QUARANTINE_ENVIRONMENT))
args.disable_ref_updates = true;
args.skip_initializing_odb = skip_initializing_odb;
repo_set_gitdir(repo, git_dir, &args);
strvec_clear(&to_free);
@@ -1074,15 +1067,10 @@ static void setup_git_env_internal(struct repository *repo,
fetch_if_missing = 0;
}
static void setup_git_env(struct repository *repo, const char *git_dir)
{
setup_git_env_internal(repo, git_dir, false);
}
static void set_git_dir_1(struct repository *repo, const char *path, bool skip_initializing_odb)
static void set_git_dir_1(struct repository *repo, const char *path)
{
xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
setup_git_env_internal(repo, path, skip_initializing_odb);
setup_git_env_internal(repo, path);
}
static void update_relative_gitdir(const char *name UNUSED,
@@ -1096,7 +1084,7 @@ static void update_relative_gitdir(const char *name UNUSED,
trace_printf_key(&trace_setup_key,
"setup: move $GIT_DIR to '%s'",
path);
set_git_dir_1(repo, path, true);
set_git_dir_1(repo, path);
free(path);
}
@@ -1109,7 +1097,7 @@ static void set_git_dir(struct repository *repo, const char *path, int make_real
path = realpath.buf;
}
set_git_dir_1(repo, path, false);
set_git_dir_1(repo, path);
if (!is_absolute_path(path))
chdir_notify_register(NULL, update_relative_gitdir, repo);
@@ -1145,7 +1133,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
die(_("not a git repository: '%s'"), gitdirenv);
}
if (check_repository_format_gently(repo, gitdirenv, repo_fmt, nongit_ok)) {
if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
free(gitfile);
return NULL;
}
@@ -1222,7 +1210,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,
struct repository_format *repo_fmt,
int *nongit_ok)
{
if (check_repository_format_gently(repo, gitdir, repo_fmt, nongit_ok))
if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
return NULL;
/* --work-tree is set without --git-dir; use discovered one */
@@ -1270,7 +1258,7 @@ static const char *setup_bare_git_dir(struct repository *repo,
{
int root_len;
if (check_repository_format_gently(repo, ".", repo_fmt, nongit_ok))
if (check_repository_format_gently(".", repo_fmt, nongit_ok))
return NULL;
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
@@ -1762,6 +1750,44 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
return result;
}
int apply_repository_format(struct repository *repo,
const struct repository_format *format,
enum apply_repository_format_flags flags,
struct strbuf *err)
{
char *object_directory = NULL, *alternate_object_directories = NULL;
if (verify_repository_format(format, err) < 0)
return -1;
if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {
object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));
alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
}
repo_set_hash_algo(repo, format->hash_algo);
repo->objects = odb_new(repo, object_directory,
alternate_object_directories);
repo_set_compat_hash_algo(repo, format->compat_hash_algo);
repo_set_ref_storage_format(repo,
format->ref_storage_format,
format->ref_storage_payload);
repo->repository_format_worktree_config =
format->worktree_config;
repo->repository_format_submodule_path_cfg =
format->submodule_path_cfg;
repo->repository_format_relative_worktrees =
format->relative_worktrees;
repo->repository_format_partial_clone =
xstrdup_or_null(format->partial_clone);
repo->repository_format_precious_objects =
format->precious_objects;
free(alternate_object_directories);
free(object_directory);
return 0;
}
/*
* Check the repository format version in the path found in repo_get_git_dir(repo),
* and die if it is a version we don't understand. Generally one would
@@ -1770,26 +1796,21 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
*
* If successful and fmt is not NULL, fill fmt with data.
*/
static void check_repository_format(struct repository *repo, struct repository_format *fmt)
static void check_and_apply_repository_format(struct repository *repo,
struct repository_format *fmt,
enum apply_repository_format_flags flags)
{
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;
if (!fmt)
fmt = &repo_fmt;
check_repository_format_gently(repo, repo_get_git_dir(repo), fmt, NULL);
check_repository_format_gently(repo_get_git_dir(repo), fmt, NULL);
if (apply_repository_format(repo, fmt, flags, &err) < 0)
die("%s", err.buf);
startup_info->have_repository = 1;
repo_set_hash_algo(repo, fmt->hash_algo);
repo_set_compat_hash_algo(repo, fmt->compat_hash_algo);
repo_set_ref_storage_format(repo,
fmt->ref_storage_format,
fmt->ref_storage_payload);
repo->repository_format_worktree_config =
fmt->worktree_config;
repo->repository_format_submodule_path_cfg =
fmt->submodule_path_cfg;
repo->repository_format_relative_worktrees =
fmt->relative_worktrees;
repo->repository_format_partial_clone =
xstrdup_or_null(fmt->partial_clone);
clear_repository_format(&repo_fmt);
}
@@ -1867,7 +1888,8 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
if (is_git_directory(".")) {
set_git_dir(repo, ".", 0);
check_repository_format(repo, NULL);
check_and_apply_repository_format(repo, NULL,
APPLY_REPOSITORY_FORMAT_HONOR_ENV);
return path;
}
@@ -2023,27 +2045,18 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdir)
gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
setup_git_env(repo, gitdir);
setup_git_env_internal(repo, gitdir);
}
if (startup_info->have_repository) {
repo_set_hash_algo(repo, repo_fmt.hash_algo);
repo_set_compat_hash_algo(repo,
repo_fmt.compat_hash_algo);
repo_set_ref_storage_format(repo,
repo_fmt.ref_storage_format,
repo_fmt.ref_storage_payload);
repo->repository_format_worktree_config =
repo_fmt.worktree_config;
repo->repository_format_relative_worktrees =
repo_fmt.relative_worktrees;
repo->repository_format_submodule_path_cfg =
repo_fmt.submodule_path_cfg;
/* take ownership of repo_fmt.partial_clone */
repo->repository_format_partial_clone =
repo_fmt.partial_clone;
repo_fmt.partial_clone = NULL;
repo->repository_format_precious_objects =
repo_fmt.precious_objects;
struct strbuf err = STRBUF_INIT;
if (apply_repository_format(repo, &repo_fmt,
APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
clear_repository_format(&repo_fmt);
strbuf_release(&err);
}
}
/*
@@ -2833,7 +2846,8 @@ int init_db(struct repository *repo,
* config file, so this will not fail. What we are catching
* is an attempt to reinitialize new repository with an old tool.
*/
check_repository_format(repo, &repo_fmt);
check_and_apply_repository_format(repo, &repo_fmt,
APPLY_REPOSITORY_FORMAT_HONOR_ENV);
repository_format_configure(repo, &repo_fmt, hash, ref_storage_format);

20
setup.h
View File

@@ -236,6 +236,26 @@ void clear_repository_format(struct repository_format *format);
int verify_repository_format(const struct repository_format *format,
struct strbuf *err);
enum apply_repository_format_flags {
/*
* Honor environment variables when applying the repository format to
* the repository. For now, this only covers environment variables that
* relate to the object database.
*/
APPLY_REPOSITORY_FORMAT_HONOR_ENV = (1 << 0),
};
/*
* Apply the given repository format to the repo. This initializes extensions
* and basic data structures required for normal operation. Returns 0 on
* success, a negative error code when the format is not valid as determined by
* `verify_repository_format()`.
*/
int apply_repository_format(struct repository *repo,
const struct repository_format *format,
enum apply_repository_format_flags flags,
struct strbuf *err);
const char *get_template_dir(const char *option_template);
#define INIT_DB_QUIET (1 << 0)

View File

@@ -980,4 +980,14 @@ test_expect_success 're-init reads matching includeIf.onbranch' '
test_cmp expect err
'
test_expect_success 'init honors GIT_OBJECT_DIRECTORY' '
test_when_finished "rm -rf init-objdir custom-odb" &&
mkdir custom-odb &&
env GIT_OBJECT_DIRECTORY="$(pwd)/custom-odb" \
git init init-objdir &&
test_path_is_missing init-objdir/.git/objects/pack &&
test_path_is_dir custom-odb/pack &&
test_path_is_dir custom-odb/info
'
test_done