From ca571025d86b55933d493e38d6e72824bcf5a80a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Aug 2026 05:34:25 +0200 Subject: [PATCH 1/6] loose: load loose object map for the correct source When loading the loose object map via `load_one_loose_object_map()` we pass in both a repository and the corresponding source. We ultimately don't really respect the passed-in source though as we instead always load the map via the common directory. This doesn't make any sense though, as the function is called in a loop through all sources, and as such the expectation is that we'll load the map that belongs to the given source. The consequence is that we'll ignore loose object maps of any configured alternates. Fix this bug by instead loading the map via the loose source's path. Helped-by: Toon Claes Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- loose.c | 18 ++++++++++-------- t/t1016-compatObjectFormat.sh | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/loose.c b/loose.c index bf01d3e42d..9dad75373b 100644 --- a/loose.c +++ b/loose.c @@ -61,9 +61,11 @@ static int insert_loose_map(struct odb_source_loose *loose, return inserted; } -static int load_one_loose_object_map(struct repository *repo, struct odb_source_loose *loose) +static int load_one_loose_object_map(struct odb_source_loose *loose) { - struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT; + struct repository *repo = loose->base.odb->repo; + struct strbuf buf = STRBUF_INIT; + char *path; FILE *fp; int ret = -1; @@ -78,10 +80,10 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_ insert_loose_map(loose, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob); insert_loose_map(loose, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid); - repo_common_path_replace(repo, &path, "objects/loose-object-idx"); - fp = fopen(path.buf, "rb"); + path = xstrfmt("%s/loose-object-idx", loose->base.path); + fp = fopen(path, "rb"); if (!fp) { - strbuf_release(&path); + free(path); return 0; } @@ -102,7 +104,7 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_ err: fclose(fp); strbuf_release(&buf); - strbuf_release(&path); + free(path); return ret; } @@ -117,10 +119,10 @@ int repo_read_loose_object_map(struct repository *repo) for (source = repo->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); - if (load_one_loose_object_map(repo, files->loose) < 0) { + if (load_one_loose_object_map(files->loose) < 0) return -1; - } } + return 0; } diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh index 92d48b96a1..9cafcee509 100755 --- a/t/t1016-compatObjectFormat.sh +++ b/t/t1016-compatObjectFormat.sh @@ -187,6 +187,24 @@ do eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) && eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4) ' + + test_expect_success 'rev-parse maps oid of object borrowed from alternate' ' + for repo in alt borrow + do + test_when_finished "rm -rf $repo" && + git init --object-format=$hash $repo && + git -C $repo config set core.repositoryformatversion 1 && + git -C $repo config set extensions.compatObjectFormat $(compat_hash $hash) || exit 1 + done && + + git -C alt commit --allow-empty --message A && + echo "$(pwd)/alt/.git/objects" >borrow/.git/objects/info/alternates && + + oid=$(git -C alt rev-parse HEAD) && + git -C alt rev-parse --output-object-format=$(compat_hash $hash) "$oid" >expect && + git -C borrow rev-parse --output-object-format=$(compat_hash $hash) "$oid" >actual && + test_cmp expect actual + ' done cd "$base" From 8a1ba94eb5863cd7491899bb23a290081e760453 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Aug 2026 05:34:26 +0200 Subject: [PATCH 2/6] setup: detangle loading of loose object maps When a repository is configured to use a compatibility hash function then we load the loose object map when we initialize the repository. This object map provides the mappings between the canonical object hash and the compatibility object hash. Loading the object map happens in `repo_set_compat_hash_algo()`, which calls `repo_read_loose_object_map()` in case the compatibility object hash is non-zero. This setup sequence has two major downsides: - We assume that the primary object database is the "files" object database and unconditionally downcast it. This will cause us to BUG in case a different object database type was used together with a compat hash algorithm. - We require the object database to already have been initialized when configuring the object database. This means that we must intermix configuration of the repository and initialization of its sub-structures in a weird way. Refactor the logic so that we instead load the loose object map via the "loose" backend, which fixes both of the above issues. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- loose.c | 11 +++++------ loose.h | 1 + odb/source-loose.c | 2 ++ repository.c | 2 -- setup.c | 5 +++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/loose.c b/loose.c index 9dad75373b..a3b2dcedc2 100644 --- a/loose.c +++ b/loose.c @@ -61,7 +61,7 @@ static int insert_loose_map(struct odb_source_loose *loose, return inserted; } -static int load_one_loose_object_map(struct odb_source_loose *loose) +int loose_object_map_load(struct odb_source_loose *loose) { struct repository *repo = loose->base.odb->repo; struct strbuf buf = STRBUF_INIT; @@ -69,6 +69,9 @@ static int load_one_loose_object_map(struct odb_source_loose *loose) FILE *fp; int ret = -1; + if (!should_use_loose_object_map(repo)) + return 0; + if (!loose->map) loose_object_map_init(&loose->map); if (!loose->cache) { @@ -112,14 +115,10 @@ int repo_read_loose_object_map(struct repository *repo) { struct odb_source *source; - if (!should_use_loose_object_map(repo)) - return 0; - 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 (load_one_loose_object_map(files->loose) < 0) + if (loose_object_map_load(files->loose) < 0) return -1; } diff --git a/loose.h b/loose.h index 6c9b3f4571..ed663ac550 100644 --- a/loose.h +++ b/loose.h @@ -13,6 +13,7 @@ struct loose_object_map { void loose_object_map_init(struct loose_object_map **map); void loose_object_map_clear(struct loose_object_map **map); +int loose_object_map_load(struct odb_source_loose *loose); int repo_loose_object_map_oid(struct repository *repo, const struct object_id *src, const struct git_hash_algo *dest_algo, diff --git a/odb/source-loose.c b/odb/source-loose.c index 3f7d04a56e..812ca1c138 100644 --- a/odb/source-loose.c +++ b/odb/source-loose.c @@ -727,5 +727,7 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb, if (!is_absolute_path(loose->base.path)) chdir_notify_register(NULL, odb_source_loose_reparent, loose); + loose_object_map_load(loose); + return loose; } diff --git a/repository.c b/repository.c index 2ef0778846..6d633002b4 100644 --- a/repository.c +++ b/repository.c @@ -201,8 +201,6 @@ void repo_set_compat_hash_algo(struct repository *repo MAYBE_UNUSED, uint32_t al if (hash_algo_by_ptr(repo->hash_algo) == algo) BUG("hash_algo and compat_hash_algo match"); repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL; - if (repo->compat_hash_algo) - repo_read_loose_object_map(repo); #else if (algo) die(_("compatibility hash algorithm support requires Rust")); diff --git a/setup.c b/setup.c index d31808130b..825572f5f1 100644 --- a/setup.c +++ b/setup.c @@ -1788,8 +1788,6 @@ int apply_repository_format(struct repository *repo, repo->bare_cfg = format->is_bare; 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, @@ -1805,6 +1803,9 @@ int apply_repository_format(struct repository *repo, repo->repository_format_precious_objects = format->precious_objects; + repo->objects = odb_new(repo, object_directory, + alternate_object_directories); + free(alternate_object_directories); free(object_directory); return 0; From 30bc6f0e8c2aef5f9280468fa2ca7c170209603f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Aug 2026 05:34:27 +0200 Subject: [PATCH 3/6] setup: handle ODB-related environment variables in `odb_new()` When initializing a repository's object database we have to respect the GIT_OBJECT_DIRECTORY and GIT_ALTERNATE_OBJECT_DIRECTORIES environment variables, which can be set by the user to override the default location of where we write objects to and read objects from. This is handled in `apply_repository_format()`, which is fine. But in a subsequent commit we'll have to defer constructing the object database to a later point in some cases, and that will require a second site where we call `odb_new()`. And of course, that second site would have to handle those environment variables, as well. It would be somewhat awkward to duplicate the logic though. But there's a better alternative: instead of handling this logic in "setup.c", we can easily handle environment variables in `odb_new()` itself. This ensures that object database creation is neatly self-contained, and we don't have to duplicate any of the logic. Another benefit is that in a future patch series we plan to move handling of alternates into the backends themselves [1], and that will require us to also handle those environment variables in the "files" backend itself. So moving the logic into the ODB level already gets us one step closer to that goal. Refactor the logic accordingly. [1]: https://lore.kernel.org/git/amLgMqkqxR8mKIbT@pks.im/ Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- odb.c | 21 ++++++++++++--------- odb.h | 17 +++++++++++++++-- setup.c | 11 ++++------- t/unit-tests/u-odb-inmemory.c | 2 +- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/odb.c b/odb.c index cf6e7938c0..ed1d63f4bd 100644 --- a/odb.c +++ b/odb.c @@ -1004,26 +1004,29 @@ int odb_write_object_stream(struct object_database *odb, } struct object_database *odb_new(struct repository *repo, - const char *primary_source, - const char *secondary_sources) + enum odb_new_flags flags) { - struct object_database *o = xmalloc(sizeof(*o)); - char *to_free = NULL; + char *primary_source = NULL, *secondary_sources = NULL; + struct object_database *o; - memset(o, 0, sizeof(*o)); + CALLOC_ARRAY(o, 1); o->repo = repo; pthread_mutex_init(&o->replace_mutex, NULL); string_list_init_dup(&o->submodule_source_paths); + if (flags & ODB_NEW_HONOR_ENV) { + primary_source = xstrdup_or_null(getenv(DB_ENVIRONMENT)); + secondary_sources = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT)); + } if (!primary_source) - primary_source = to_free = xstrfmt("%s/objects", repo->commondir); + primary_source = xstrfmt("%s/objects", repo->commondir); + o->sources = odb_source_new(o, primary_source, true); o->sources_tail = &o->sources->next; - o->alternate_db = xstrdup_or_null(secondary_sources); + o->alternate_db = secondary_sources; o->inmemory_objects = &odb_source_inmemory_new(o)->base; - free(to_free); - + free(primary_source); return o; } diff --git a/odb.h b/odb.h index 7995bed97b..8ec335c7f7 100644 --- a/odb.h +++ b/odb.h @@ -100,6 +100,20 @@ struct object_database { struct string_list submodule_source_paths; }; +enum odb_new_flags { + /* + * Honor environment variables when constructing the object database + * sources. This makes us respect the following environment variables: + * + * - GIT_OBJECT_DIRECTORY to override the primary object directory. + * + * - GIT_ALTERNATE_OBJECT_DIRECTORIES to override alternates. + * + * Environment variables may be backend-specific. + */ + ODB_NEW_HONOR_ENV = (1 << 0), +}; + /* * Create a new object database for the given repository. * @@ -112,8 +126,7 @@ struct object_database { * Returns the newly created object database. */ struct object_database *odb_new(struct repository *repo, - const char *primary_source, - const char *alternate_sources); + enum odb_new_flags flags); /* Free the object database and release all resources. */ void odb_free(struct object_database *o); diff --git a/setup.c b/setup.c index 825572f5f1..5dfab3e79e 100644 --- a/setup.c +++ b/setup.c @@ -1765,7 +1765,7 @@ int apply_repository_format(struct repository *repo, enum apply_repository_format_flags flags, struct strbuf *err) { - char *object_directory = NULL, *alternate_object_directories = NULL; + enum odb_new_flags odb_new_flags = 0; if (verify_repository_format(format, err) < 0) return -1; @@ -1779,8 +1779,6 @@ int apply_repository_format(struct repository *repo, if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) { const char *shallow_file; - object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT)); - alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT)); shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); if (shallow_file) set_alternate_shallow_file(repo, shallow_file); @@ -1803,11 +1801,10 @@ int apply_repository_format(struct repository *repo, repo->repository_format_precious_objects = format->precious_objects; - repo->objects = odb_new(repo, object_directory, - alternate_object_directories); + if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) + odb_new_flags |= ODB_NEW_HONOR_ENV; + repo->objects = odb_new(repo, odb_new_flags); - free(alternate_object_directories); - free(object_directory); return 0; } diff --git a/t/unit-tests/u-odb-inmemory.c b/t/unit-tests/u-odb-inmemory.c index 6844bfc37c..db323e10fd 100644 --- a/t/unit-tests/u-odb-inmemory.c +++ b/t/unit-tests/u-odb-inmemory.c @@ -38,7 +38,7 @@ static void cl_assert_object_info(struct odb_source_inmemory *source, void test_odb_inmemory__initialize(void) { - odb = odb_new(&repo, "", ""); + odb = odb_new(&repo, 0); } void test_odb_inmemory__cleanup(void) From c1d233bd3001530042ff097f6aef0a658b7f79cb Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Aug 2026 05:34:28 +0200 Subject: [PATCH 4/6] setup: defer object database creation In a subsequent commit we'll make the creation of the on-disk data structures of an object database pluggable. This will lead to an in-between state where we have already configured the repository's object database, but it's not usable yet until we eventually call `create_object_directory()`. Lift the call to `odb_new()` out of `apply_repository_format()` so that callers have more wiggle room with when exactly they call it, and adapt them accordingly. The only exception is `init_db()`, where we now defer creating the object database until we call `create_object_database()`. With this change, initializing and creating the object database on disk is now neatly encapsulated in a single function, which will make it easier for a subsequent commit to move creation of the on-disk data structures into the `struct odb_source` backends. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- repository.c | 1 + setup.c | 17 ++++++++--------- setup.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/repository.c b/repository.c index 6d633002b4..5ec264e607 100644 --- a/repository.c +++ b/repository.c @@ -294,6 +294,7 @@ int repo_init(struct repository *repo, warning("%s", err.buf); goto error; } + repo->objects = odb_new(repo, 0); if (worktree) repo_set_worktree(repo, worktree); diff --git a/setup.c b/setup.c index 5dfab3e79e..97338cbc51 100644 --- a/setup.c +++ b/setup.c @@ -1765,8 +1765,6 @@ int apply_repository_format(struct repository *repo, enum apply_repository_format_flags flags, struct strbuf *err) { - enum odb_new_flags odb_new_flags = 0; - if (verify_repository_format(format, err) < 0) return -1; @@ -1801,10 +1799,6 @@ int apply_repository_format(struct repository *repo, repo->repository_format_precious_objects = format->precious_objects; - if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) - odb_new_flags |= ODB_NEW_HONOR_ENV; - repo->objects = odb_new(repo, odb_new_flags); - return 0; } @@ -1888,6 +1882,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags read_and_verify_repository_format(&fmt, ".", NULL); if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) die("%s", err.buf); + repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV); startup_info->have_repository = 1; clear_repository_format(&fmt); @@ -2090,6 +2085,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) if (apply_repository_format(repo, &discovery.format, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) die("%s", err.buf); + repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV); clear_repository_format(&discovery.format); strbuf_release(&err); @@ -2651,11 +2647,13 @@ static int create_default_files(struct repository *repo, return reinit; } -static void create_object_directory(struct repository *repo) +static void create_object_database(struct repository *repo) { struct strbuf path = STRBUF_INIT; size_t baselen; + repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV); + strbuf_addstr(&path, repo_get_object_directory(repo)); baselen = path.len; @@ -2866,7 +2864,6 @@ int init_db(struct repository *repo, repository_format_configure(&repo_fmt, hash, ref_storage_format); if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) die("%s", err.buf); - startup_info->have_repository = 1; /* * Ensure `core.hidedotfiles` is processed. This must happen after we @@ -2882,7 +2879,9 @@ int init_db(struct repository *repo, if (!(flags & INIT_DB_SKIP_REFDB)) create_reference_database(repo, initial_branch, flags & INIT_DB_QUIET); - create_object_directory(repo); + create_object_database(repo); + + startup_info->have_repository = 1; if (repo_settings_get_shared_repository(repo)) { char buf[10]; diff --git a/setup.h b/setup.h index 654f10e059..763fd384e8 100644 --- a/setup.h +++ b/setup.h @@ -245,8 +245,8 @@ enum apply_repository_format_flags { /* * 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 + * 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, From 335fe2545e4d64b79fc28acc945bc3278739d078 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Aug 2026 05:34:29 +0200 Subject: [PATCH 5/6] odb/source: introduce function to map source type to name Introduce a new function that maps an object source's type to a human-readable name. Use the function to provide better human-readable error messages for the downcasting functions. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- odb/source-files.h | 4 +++- odb/source-inmemory.h | 4 +++- odb/source-loose.h | 4 +++- odb/source-packed.h | 4 +++- odb/source.c | 19 +++++++++++++++++++ odb/source.h | 6 ++++++ 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/odb/source-files.h b/odb/source-files.h index d7ac3c1c81..6a803afdda 100644 --- a/odb/source-files.h +++ b/odb/source-files.h @@ -28,7 +28,9 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb, static inline struct odb_source_files *odb_source_files_downcast(struct odb_source *source) { if (source->type != ODB_SOURCE_FILES) - BUG("trying to downcast source of type '%d' to files", source->type); + BUG("trying to downcast source of type '%s' to '%s'", + odb_source_type_to_name(source->type), + odb_source_type_to_name(ODB_SOURCE_FILES)); return container_of(source, struct odb_source_files, base); } diff --git a/odb/source-inmemory.h b/odb/source-inmemory.h index a88fc2e320..adbad23e8b 100644 --- a/odb/source-inmemory.h +++ b/odb/source-inmemory.h @@ -26,7 +26,9 @@ struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb) static inline struct odb_source_inmemory *odb_source_inmemory_downcast(struct odb_source *source) { if (source->type != ODB_SOURCE_INMEMORY) - BUG("trying to downcast source of type '%d' to in-memory", source->type); + BUG("trying to downcast source of type '%s' to '%s'", + odb_source_type_to_name(source->type), + odb_source_type_to_name(ODB_SOURCE_INMEMORY)); return container_of(source, struct odb_source_inmemory, base); } diff --git a/odb/source-loose.h b/odb/source-loose.h index 6070aaf3ce..3cf2e1f8f1 100644 --- a/odb/source-loose.h +++ b/odb/source-loose.h @@ -41,7 +41,9 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb, static inline struct odb_source_loose *odb_source_loose_downcast(struct odb_source *source) { if (source->type != ODB_SOURCE_LOOSE) - BUG("trying to downcast source of type '%d' to loose", source->type); + BUG("trying to downcast source of type '%s' to '%s'", + odb_source_type_to_name(source->type), + odb_source_type_to_name(ODB_SOURCE_LOOSE)); return container_of(source, struct odb_source_loose, base); } diff --git a/odb/source-packed.h b/odb/source-packed.h index 77309ddd09..a0f6b5096d 100644 --- a/odb/source-packed.h +++ b/odb/source-packed.h @@ -78,7 +78,9 @@ struct odb_source_packed *odb_source_packed_new(struct object_database *odb, static inline struct odb_source_packed *odb_source_packed_downcast(struct odb_source *source) { if (source->type != ODB_SOURCE_PACKED) - BUG("trying to downcast source of type '%d' to packed", source->type); + BUG("trying to downcast source of type '%s' to '%s'", + odb_source_type_to_name(source->type), + odb_source_type_to_name(ODB_SOURCE_PACKED)); return container_of(source, struct odb_source_packed, base); } diff --git a/odb/source.c b/odb/source.c index 7993dcbd65..30188b806d 100644 --- a/odb/source.c +++ b/odb/source.c @@ -4,6 +4,25 @@ #include "odb/source.h" #include "packfile.h" +static const char * const odb_source_names_by_type[] = { + [ODB_SOURCE_UNKNOWN] = "unknown", + [ODB_SOURCE_FILES] = "files", + [ODB_SOURCE_LOOSE] = "loose", + [ODB_SOURCE_PACKED] = "packed", + [ODB_SOURCE_INMEMORY] = "in-memory", +}; + +const char *odb_source_type_to_name(enum odb_source_type type) +{ + const char *name; + if (type < 0 || type >= ARRAY_SIZE(odb_source_names_by_type)) + type = ODB_SOURCE_UNKNOWN; + name = odb_source_names_by_type[type]; + if (!name) + BUG("name missing in `odb_source_names_by_type` for '%d'", type); + return name; +} + struct odb_source *odb_source_new(struct object_database *odb, const char *path, bool local) diff --git a/odb/source.h b/odb/source.h index cd63dba91f..ab16d152f4 100644 --- a/odb/source.h +++ b/odb/source.h @@ -25,6 +25,12 @@ enum odb_source_type { ODB_SOURCE_INMEMORY, }; +/* + * Convert between the enum and its name. Returns the equivalent of "unknown" + * for unknown types. + */ +const char *odb_source_type_to_name(enum odb_source_type type); + struct object_id; struct odb_read_stream; struct strvec; From e927cfeb21d6a217b708216862deb36f144f064b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Aug 2026 05:34:30 +0200 Subject: [PATCH 6/6] odb: make creation of on-disk structures pluggable When creating a new "files" object database source we have to create a couple of directories. These directories are of course specific to this particular backend, and a different backend may require a setup that is completely different. Make the creation of on-disk structures pluggable to accommodate for this. Note that there is one exception though: the "objects" directory must exist in a repository regardless of which backend is in use. If it doesn't exist then the repository is not treated as a Git repository at all. Consequently, we create this directory regardless of the backend. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- odb/source-files.c | 19 +++++++++++++++++++ odb/source.h | 23 +++++++++++++++++++++++ setup.c | 34 ++++++++++++++++++---------------- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/odb/source-files.c b/odb/source-files.c index 4138758511..0db6e681fe 100644 --- a/odb/source-files.c +++ b/odb/source-files.c @@ -9,6 +9,7 @@ #include "odb/source-files.h" #include "odb/source-loose.h" #include "packfile.h" +#include "path.h" #include "strbuf.h" #include "write-or-die.h" @@ -41,6 +42,23 @@ static void odb_source_files_close(struct odb_source *source) odb_source_close(&files->packed->base); } +static int odb_source_files_create_on_disk(struct odb_source *source) +{ + struct strbuf path = STRBUF_INIT; + + safe_create_dir(source->odb->repo, source->path, 1); + + strbuf_addf(&path, "%s/pack", source->path); + safe_create_dir(source->odb->repo, path.buf, 1); + + strbuf_reset(&path); + strbuf_addf(&path, "%s/info", source->path); + safe_create_dir(source->odb->repo, path.buf, 1); + + strbuf_release(&path); + return 0; +} + static void odb_source_files_prepare(struct odb_source *source, enum odb_prepare_flags flags) { @@ -271,6 +289,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb, files->base.free = odb_source_files_free; files->base.close = odb_source_files_close; + files->base.create_on_disk = odb_source_files_create_on_disk; files->base.prepare = odb_source_files_prepare; files->base.read_object_info = odb_source_files_read_object_info; files->base.read_object_stream = odb_source_files_read_object_stream; diff --git a/odb/source.h b/odb/source.h index ab16d152f4..4abc418bdd 100644 --- a/odb/source.h +++ b/odb/source.h @@ -89,6 +89,18 @@ struct odb_source { */ void (*close)(struct odb_source *source); + /* + * This callback is expected to create on-disk data structures that are + * required for this source to operate. + * + * The callback is expected to return 0 on success, a negative error + * code otherwise. + * + * This callback may be NULL in case the source does not need any + * on-disk setup. + */ + int (*create_on_disk)(struct odb_source *source); + /* * This callback is expected to prepare the source so that it becomes * ready for use. It optionally clears underlying caches of the object @@ -316,6 +328,17 @@ static inline void odb_source_close(struct odb_source *source) source->close(source); } +/* + * Create on-disk data structures that are required for this source to operate + * correctly. Returns 0 on success, a negative error code otherwise. + */ +static inline int odb_source_create_on_disk(struct odb_source *source) +{ + if (!source->create_on_disk) + return 0; + return source->create_on_disk(source); +} + /* * Prepare the object database source and clear any caches. Depending on the * backend used this may have the effect that concurrently-written objects diff --git a/setup.c b/setup.c index 97338cbc51..ace3c59d18 100644 --- a/setup.c +++ b/setup.c @@ -2649,25 +2649,27 @@ static int create_default_files(struct repository *repo, static void create_object_database(struct repository *repo) { - struct strbuf path = STRBUF_INIT; - size_t baselen; + /* + * Create the "objects" directory in the common directory. This is done + * so that the repository can be discovered regardless of the backend + * used. + * + * Note that we only do this in case the object directory wasn't + * overwritten via an environment variable. If it _is_ being overridden + * then we skip this step, as the repository won't be discoverable + * anyway without the environment variable. + */ + if (!getenv(DB_ENVIRONMENT)) { + struct strbuf objects_dir = STRBUF_INIT; + repo_common_path_append(repo, &objects_dir, "objects"); + safe_create_dir(repo, objects_dir.buf, 1); + strbuf_release(&objects_dir); + } repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV); - strbuf_addstr(&path, repo_get_object_directory(repo)); - baselen = path.len; - - safe_create_dir(repo, path.buf, 1); - - strbuf_setlen(&path, baselen); - strbuf_addstr(&path, "/pack"); - safe_create_dir(repo, path.buf, 1); - - strbuf_setlen(&path, baselen); - strbuf_addstr(&path, "/info"); - safe_create_dir(repo, path.buf, 1); - - strbuf_release(&path); + if (odb_source_create_on_disk(repo->objects->sources) < 0) + die(_("failed creating object database")); } static void separate_git_dir(const char *git_dir, const char *git_link)