Merge branch 'ps/odb-eagerly-load-alternates' into ps/odb-alternates-at-creation

* ps/odb-eagerly-load-alternates:
  odb: drop `alternates_db` field
  odb: drop `loaded_alternates` field
  odb: eagerly initialize alternates
  odb: decouple source path comparisons from `the_repository`
  setup: create ref and object databases after config is written
jch
Junio C Hamano 2026-08-25 10:27:03 -07:00
commit 02e44450dd
13 changed files with 92 additions and 93 deletions

View File

@ -1069,7 +1069,6 @@ int cmd_fsck(int argc,
odb_for_each_object(repo->objects, NULL,
mark_object_for_connectivity, repo, 0);
} else {
odb_prepare_alternates(repo->objects);
for (source = repo->objects->sources; source; source = source->next)
fsck_source(repo, source);

@ -1155,7 +1154,6 @@ int cmd_fsck(int argc,
if (repo->settings.core_commit_graph) {
struct child_process commit_graph_verify = CHILD_PROCESS_INIT;

odb_prepare_alternates(repo->objects);
for (source = repo->objects->sources; source; source = source->next) {
child_process_init(&commit_graph_verify);
commit_graph_verify.git_cmd = 1;
@ -1173,7 +1171,6 @@ int cmd_fsck(int argc,
if (repo->settings.core_multi_pack_index) {
struct child_process midx_verify = CHILD_PROCESS_INIT;

odb_prepare_alternates(repo->objects);
for (source = repo->objects->sources; source; source = source->next) {
child_process_init(&midx_verify);
midx_verify.git_cmd = 1;

View File

@ -1780,8 +1780,6 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
*found_offset = 0;
}

odb_prepare_alternates(the_repository->objects);

for (source = the_repository->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
struct multi_pack_index *m = get_multi_pack_index(files->packed);
@ -4520,7 +4518,6 @@ static void add_objects_in_unpacked_packs(void)
.source_infop = &source_info,
};

odb_prepare_alternates(to_pack.repo->objects);
for (source = to_pack.repo->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);


View File

@ -651,8 +651,6 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct object_database *odb,
count = st->st_size / (odb->repo->hash_algo->hexsz + 1);
CALLOC_ARRAY(oids, count);

odb_prepare_alternates(odb);

for (i = 0; i < count; i++) {
struct odb_source *source;

@ -768,7 +766,6 @@ static struct commit_graph *prepare_commit_graph(struct repository *r)
if (!commit_graph_compatible(r))
return NULL;

odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
r->objects->commit_graph = read_commit_graph_one(source);
if (r->objects->commit_graph)
@ -2018,7 +2015,6 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
_("Finding commits for commit graph among packed objects"),
ctx->approx_nr_objects);

odb_prepare_alternates(ctx->r->objects);
for (source = ctx->r->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
odb_source_for_each_object(&files->packed->base, &oi, add_packed_commits_oi,

View File

@ -115,7 +115,6 @@ int repo_read_loose_object_map(struct repository *repo)
{
struct odb_source *source;

odb_prepare_alternates(repo->objects);
for (source = repo->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
if (loose_object_map_load(files->loose) < 0)

View File

@ -280,7 +280,6 @@ static int init_object_disambiguation(struct repository *r,

ds->len = len;
ds->repo = r;
odb_prepare_alternates(r->objects);
return 0;
}


124
odb.c
View File

@ -2,11 +2,10 @@
#include "abspath.h"
#include "commit-graph.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "hashmap.h"
#include "hex.h"
#include "khash.h"
#include "lockfile.h"
#include "loose.h"
#include "midx.h"
@ -29,8 +28,47 @@
#include "trace2.h"
#include "write-or-die.h"

KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
struct odb_source *, 1, fspathhash, fspatheq)
/*
* NEEDSWORK: we're using "core.ignoreCase" to deduplicate alternates that
* _may_ be the same. This requires quite a bit of boilerplate for dubious
* benefit:
*
* - Duplicating alternates should really only lead to regressed performance.
*
* - We don't properly resolve symlinks or mointpoints, so we may still end
* up duplicating alternates.
*
* - The value may be lying, in which case we might deduplicate alternates
* that are in fact not mapping to the same directory.
*
* We should investigate whether we can remove this whole mechanism outright.
*/
static int odb_source_paths_cmp(struct object_database *o,
const char *a, const char *b)
{
if (o->source_paths_icase < 0) {
int icase = 0;
repo_config_get_bool(o->repo, "core.ignorecase", &icase);
o->source_paths_icase = icase;
}

return o->source_paths_icase ? strcasecmp(a, b) : strcmp(a, b);
}

static int odb_source_by_path_cmp(const void *cb_data,
const struct hashmap_entry *entry,
const struct hashmap_entry *entry_or_key,
const void *keydata)
{
struct object_database *o = (struct object_database *)cb_data;
const struct odb_source *source = container_of(entry, const struct odb_source, by_path_entry);
const char *path = keydata;

if (!path)
path = container_of(entry_or_key, const struct odb_source, by_path_entry)->path;

return odb_source_paths_cmp(o, source->path, path);
}

int odb_mkstemp(struct object_database *odb,
struct strbuf *temp_filename, const char *pattern)
@ -58,8 +96,8 @@ int odb_mkstemp(struct object_database *odb,
*/
static bool odb_is_source_usable(struct object_database *o, const char *path)
{
int r;
struct strbuf normalized_objdir = STRBUF_INIT;
struct hashmap_entry key;
bool usable = false;

strbuf_realpath(&normalized_objdir, o->sources->path, 1);
@ -76,20 +114,18 @@ static bool odb_is_source_usable(struct object_database *o, const char *path)
* Prevent the common mistake of listing the same
* thing twice, or object directory itself.
*/
if (!o->source_by_path) {
khiter_t p;

o->source_by_path = kh_init_odb_path_map();
if (!hashmap_get_size(&o->source_by_path)) {
assert(!o->sources->next);
p = kh_put_odb_path_map(o->source_by_path, o->sources->path, &r);
assert(r == 1); /* never used */
kh_value(o->source_by_path, p) = o->sources;
hashmap_entry_init(&o->sources->by_path_entry,
strihash(o->sources->path));
hashmap_add(&o->source_by_path, &o->sources->by_path_entry);
}

if (fspatheq(path, normalized_objdir.buf))
if (!odb_source_paths_cmp(o, path, normalized_objdir.buf))
goto out;

if (kh_get_odb_path_map(o->source_by_path, path) < kh_end(o->source_by_path))
hashmap_entry_init(&key, strihash(path));
if (hashmap_get(&o->source_by_path, &key, path))
goto out;

usable = true;
@ -172,8 +208,6 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database *
{
struct odb_source *alternate = NULL;
struct strvec sources = STRVEC_INIT;
khiter_t pos;
int ret;

if (!odb_is_source_usable(odb, source))
goto error;
@ -184,10 +218,11 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database *
*odb->sources_tail = alternate;
odb->sources_tail = &(alternate->next);

pos = kh_put_odb_path_map(odb->source_by_path, alternate->path, &ret);
if (!ret)
hashmap_entry_init(&alternate->by_path_entry, strihash(alternate->path));
if (hashmap_get(&odb->source_by_path, &alternate->by_path_entry,
alternate->path))
BUG("source must not yet exist");
kh_value(odb->source_by_path, pos) = alternate;
hashmap_add(&odb->source_by_path, &alternate->by_path_entry);

/* recursively add alternates */
odb_source_read_alternates(alternate, &sources);
@ -210,18 +245,12 @@ void odb_add_to_alternates_file(struct object_database *odb,
int ret = odb_source_write_alternate(odb->sources, dir);
if (ret < 0)
die(NULL);
if (odb->loaded_alternates)
odb_add_alternate_recursively(odb, dir, 0);
odb_add_alternate_recursively(odb, dir, 0);
}

struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,
const char *dir)
{
/*
* Make sure alternates are initialized, or else our entry may be
* overwritten when they are.
*/
odb_prepare_alternates(odb);
return odb_add_alternate_recursively(odb, dir, 0);
}

@ -230,12 +259,6 @@ struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,
{
struct odb_source *source;

/*
* Make sure alternates are initialized, or else our entry may be
* overwritten when they are.
*/
odb_prepare_alternates(odb);

/*
* Make a new primary odb and link the old primary ODB in as an
* alternate
@ -341,7 +364,6 @@ struct odb_source *odb_find_source(struct object_database *odb, const char *obj_
char *obj_dir_real = real_pathdup(obj_dir, 1);
struct strbuf odb_path_real = STRBUF_INIT;

odb_prepare_alternates(odb);
for (source = odb->sources; source; source = source->next) {
strbuf_realpath(&odb_path_real, source->path, 1);
if (!strcmp(obj_dir_real, odb_path_real.buf))
@ -475,7 +497,6 @@ int odb_for_each_alternate(struct object_database *odb,
struct odb_source *alternate;
int r = 0;

odb_prepare_alternates(odb);
for (alternate = odb->sources->next; alternate; alternate = alternate->next) {
r = cb(alternate, payload);
if (r)
@ -484,26 +505,22 @@ int odb_for_each_alternate(struct object_database *odb,
return r;
}

void odb_prepare_alternates(struct object_database *odb)
static void odb_prepare_alternates(struct object_database *odb,
const char *alternate_db)
{
struct strvec sources = STRVEC_INIT;

if (odb->loaded_alternates)
return;

parse_alternates(odb->alternate_db, PATH_SEP, NULL, &sources);
parse_alternates(alternate_db, PATH_SEP, NULL, &sources);
odb_source_read_alternates(odb->sources, &sources);

for (size_t i = 0; i < sources.nr; i++)
odb_add_alternate_recursively(odb, sources.v[i], 0);

odb->loaded_alternates = 1;

strvec_clear(&sources);
}

int odb_has_alternates(struct object_database *odb)
{
odb_prepare_alternates(odb);
return !!odb->sources->next;
}

@ -563,8 +580,6 @@ static int do_oid_object_info_extended(struct object_database *odb,
if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags))
return 0;

odb_prepare_alternates(odb);

while (1) {
struct odb_source *source;

@ -827,7 +842,6 @@ int odb_freshen_object(struct object_database *odb,
const struct object_id *oid)
{
struct odb_source *source;
odb_prepare_alternates(odb);
for (source = odb->sources; source; source = source->next)
if (odb_source_freshen_object(source, oid, NULL))
return 1;
@ -842,7 +856,6 @@ int odb_for_each_object_ext(struct object_database *odb,
{
int ret;

odb_prepare_alternates(odb);
for (struct odb_source *source = odb->sources; source; source = source->next) {
if (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local)
continue;
@ -880,7 +893,6 @@ int odb_count_objects(struct object_database *odb,
return 0;
}

odb_prepare_alternates(odb);
for (source = odb->sources; source; source = source->next) {
unsigned long c;

@ -960,7 +972,6 @@ int odb_find_abbrev_len(struct object_database *odb,
goto out;
}

odb_prepare_alternates(odb);
for (struct odb_source *source = odb->sources; source; source = source->next) {
ret = odb_source_find_abbrev_len(source, oid, len, &len);
if (ret)
@ -1056,6 +1067,8 @@ struct object_database *odb_new(struct repository *repo,
o->repo = repo;
pthread_mutex_init(&o->replace_mutex, NULL);
string_list_init_dup(&o->submodule_source_paths);
hashmap_init(&o->source_by_path, odb_source_by_path_cmp, o, 0);
o->source_paths_icase = -1;

if (flags & ODB_NEW_HONOR_ENV) {
primary_source = xstrdup_or_null(getenv(DB_ENVIRONMENT));
@ -1066,9 +1079,11 @@ struct object_database *odb_new(struct repository *repo,

o->sources = odb_source_new(o, primary_source, true);
o->sources_tail = &o->sources->next;
o->alternate_db = secondary_sources;
o->inmemory_objects = &odb_source_inmemory_new(o)->base;

odb_prepare_alternates(o, secondary_sources);

free(secondary_sources);
free(primary_source);
return o;
}
@ -1094,8 +1109,7 @@ static void odb_free_sources(struct object_database *o)
odb_source_free(o->inmemory_objects);
o->inmemory_objects = NULL;

kh_destroy_odb_path_map(o->source_by_path);
o->source_by_path = NULL;
hashmap_clear(&o->source_by_path);
}

void odb_free(struct object_database *o)
@ -1103,8 +1117,6 @@ void odb_free(struct object_database *o)
if (!o)
return;

free(o->alternate_db);

oidmap_clear(&o->replace_map, 1);
pthread_mutex_destroy(&o->replace_mutex);

@ -1126,14 +1138,14 @@ void odb_prepare(struct object_database *o, enum odb_prepare_flags flags)
* Reprepare alt odbs, in case the alternates file was modified
* during the course of this process. This only _adds_ odbs to
* the linked list, so existing odbs will continue to exist for
* the lifetime of the process.
* the lifetime of the process. Consequently, we don't have to
* reprocess GIT_ALTERNATE_OBJECT_DIRECTORIES here.
*/
if (flags & ODB_PREPARE_FLUSH_CACHES) {
o->loaded_alternates = 0;
odb_prepare_alternates(o, NULL);
o->object_count_valid = 0;
}

odb_prepare_alternates(o);
for (source = o->sources; source; source = source->next)
odb_source_prepare(source, flags);


24
odb.h
View File

@ -1,6 +1,7 @@
#ifndef ODB_H
#define ODB_H

#include "hashmap.h"
#include "object.h"
#include "oidset.h"
#include "oidmap.h"
@ -54,16 +55,19 @@ struct object_database {
*/
struct odb_source *sources;
struct odb_source **sources_tail;
struct kh_odb_path_map *source_by_path;

int loaded_alternates;

/*
* A list of alternate object directories loaded from the environment;
* this should not generally need to be accessed directly, but will
* populate the "sources" list when odb_prepare_alternates() is run.
* Map of object database sources, keyed by their respective paths.
* This map is used to detect the case where the same source is
* registered multiple times.
*/
char *alternate_db;
struct hashmap source_by_path;

/*
* Whether source paths shall be compared case-insensitively, as
* determined by "core.ignoreCase".
*/
int source_paths_icase;

/*
* Objects that should be substituted by other objects
@ -260,12 +264,6 @@ void odb_for_each_alternate_ref(struct object_database *odb,
int odb_mkstemp(struct object_database *odb,
struct strbuf *temp_filename, const char *pattern);

/*
* Prepare alternate object sources for the given database by reading
* "objects/info/alternates" and opening the respective sources.
*/
void odb_prepare_alternates(struct object_database *odb);

/*
* Check whether the object database has any alternates. The primary object
* source does not count as alternate.

View File

@ -1,6 +1,7 @@
#ifndef ODB_SOURCE_H
#define ODB_SOURCE_H

#include "hashmap.h"
#include "object.h"
#include "odb.h"
#include "odb/transaction.h"
@ -50,6 +51,12 @@ struct strvec;
struct odb_source {
struct odb_source *next;

/*
* Entry in the object database's map of sources, keyed by this
* source's path.
*/
struct hashmap_entry by_path_entry;

/* Object database that owns this object source. */
struct object_database *odb;


View File

@ -184,7 +184,6 @@ static int istream_source(struct odb_stream **out,
{
struct odb_source *source;

odb_prepare_alternates(odb);
for (source = odb->sources; source; source = source->next)
if (!odb_source_read_object_stream(out, source, oid))
return 0;

View File

@ -717,7 +717,6 @@ static int open_bitmap(struct repository *r,

assert(!bitmap_git->map);

odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);

@ -3417,7 +3416,6 @@ int verify_bitmap_files(struct repository *r)
struct packed_git *p;
int res = 0;

odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
struct multi_pack_index *m = get_multi_pack_index(files->packed);

View File

@ -1936,7 +1936,6 @@ int has_object_pack(struct repository *r, const struct object_id *oid)
{
struct odb_source *source;

odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
if (!odb_source_read_object_info(&files->packed->base, oid, NULL, 0))

View File

@ -77,8 +77,6 @@ static inline struct repo_for_each_pack_data repo_for_eack_pack_data_init(struct
{
struct repo_for_each_pack_data data = { 0 };

odb_prepare_alternates(repo->objects);

for (struct odb_source *source = repo->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
struct packfile_list_entry *entry = packfile_store_get_packs(files->packed);

12
setup.c
View File

@ -2880,12 +2880,6 @@ int init_db(struct repository *repo,
reinit = create_default_files(repo, template_dir, original_git_dir,
&repo_fmt, init_shared_repository);

if (!(flags & INIT_DB_SKIP_REFDB))
create_reference_database(repo, initial_branch, flags & INIT_DB_QUIET);
create_object_database(repo);

startup_info->have_repository = 1;

if (repo_settings_get_shared_repository(repo)) {
char buf[10];
/* We do not spell "group" and such, so that
@ -2907,6 +2901,12 @@ int init_db(struct repository *repo,
repo_config_set(repo, "receive.denyNonFastforwards", "true");
}

if (!(flags & INIT_DB_SKIP_REFDB))
create_reference_database(repo, initial_branch, flags & INIT_DB_QUIET);
create_object_database(repo);

startup_info->have_repository = 1;

if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);