{"thread":{"id":"66255","subject":"[PATCH v2 01/13] cache-tree: drop `the_repository` in `cache_tree_fully_valid()`","startedAt":"2026-09-02T13:35:05Z","lastAt":"2026-09-09T05:52:29Z","messageCount":23,"participants":["Patrick Steinhardt","Karthik Nayak","Justin Tobler"],"isPatch":true,"patchVersion":2,"patchTotal":13},"messages":[{"id":"551757","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260901-pks-odb-registering-in-memory-sources-v1-0-97a312d5fa25@pks.im","subject":"[PATCH v2 00/13] odb: stop registering in-memory sources","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:48Z","receivedAt":"2026-09-02T13:35:04Z","isPatch":true,"body":"Hi,\n\nthe object database has a list of sources that is used for two\ndifferent purposes:\n\n  - We use it to track the list of alternates.\n\n  - We use it to track temporary in-memory sources that we create for\n    various purposes. Most importantly, this is used to link object\n    database sources from submodules into the main store.\n\nThis dual-use is quite awkward, as it mixes two different levels of\nconcerns and thus as a consequence makes both harder to reason about.\nIt's also a source of bugs: we make assumptions about the ordering of\nsources all over the place, and we furthermore assume in other places\nthat the sources only contain alternates in the first place. I don't\nthink this surfaces in the form of real bugs, but I've long disliked\nthis dual-use.\n\nFurthermore, we want to migrate handling of alternates into the \"files\"\nbackend itself in a subsequent patch series. This is most importantly to\nfix a performance regression by making the backend own all of its\nalternates, but it also fixes a couple of longer-standing design issues\nthat I've been struggling with [1].\n\nMost importantly though: this whole machinery is not even needed at all.\nA couple years ago we have already refactored our codebase so that\nsubmodule sources don't even have to be linked into the main object\ndatabase anymore. And all the other use cases where we link sources into\nthe main object database can be trivially converted, too.\n\nSo this patch series does exactly that: it removes the mechanism to link\nad-hoc sources into the object database entirely. This ensures that the\nlist of sources is exactly the list of alternates, and that makes it\neasier to move them into the \"files\" backend in a subsequent patch\nseries.\n\nThere is one exception though: creating transactions still creates a\ntemporary quarantine directory. This mechanism is left as-is for now,\nbut as it's an implementation detail of the \"files\" backend anyway\nthat's not conflicting with our above stated goals.\n\nThis series is built on top of 1630431f32 (The 21st batch, 2026-08-31)\nwith ty/repository-fetch-if-missing at 508ec9837c (repository: move\nfetch_if_missing into struct repository, 2026-08-15) merged into it.\nThere's still two merge conflicts, but these are trivial to resolve: in\n\"odb.c\" and \"odb.h\" you simply remove both ours and theirs, and in\n\"builtin/multi-pack-index.c\" you only need to munge the parameters a\nbit.\n\nChanges in v2:\n  - Adapt `cache_tree_fully_valid()` to take a `struct index_state` as\n    input instead of taking both a repository and a cache tree, as\n    suggested by Junio.\n  - Link to v1: https://patch.msgid.link/20260901-pks-odb-registering-in-memory-sources-v1-0-97a312d5fa25@pks.im\n\nThanks!\n\nPatrick\n\n[1]: <amLgMqkqxR8mKIbT@pks.im>\n\n---\nPatrick Steinhardt (13):\n      cache-tree: drop `the_repository` in `cache_tree_fully_valid()`\n      cache-tree: remove dependency on `the_repository`\n      submodule-config: remove uses of `the_repository`\n      submodule-config: stop using `the_hash_algo`\n      submodule-config: stop registering submodule sources\n      builtin/grep: stop registering submodule ODB as source\n      odb: remove infrastructure to register submodule sources\n      tmp-objdir: drop unused function to register alternate\n      odb/packed: fix memory leaks when freeing source\n      builtin/multi-pack-index: refuse unknown sources with \"--object-dir=\"\n      t/helper: adapt read-midx to not link ad-hoc source anymore\n      t/helper: stop registering alternates in \"ref-store\" command\n      odb: remove the ability to link sources ad-hoc\n\n builtin/checkout.c                     |  2 +-\n builtin/commit.c                       |  2 +-\n builtin/fetch.c                        |  2 +-\n builtin/grep.c                         | 28 +++-------\n builtin/multi-pack-index.c             |  3 +-\n builtin/submodule--helper.c            |  8 +--\n cache-tree.c                           | 95 +++++++++++++++++++---------------\n cache-tree.h                           |  7 +--\n odb.c                                  | 42 ---------------\n odb.h                                  | 22 --------\n odb/source-packed.c                    |  1 +\n read-cache-ll.h                        |  5 +-\n read-cache.c                           |  9 ++--\n sequencer.c                            |  2 +-\n sparse-index.c                         |  2 +-\n submodule-config.c                     | 59 +++++++++++----------\n submodule-config.h                     | 12 +++--\n submodule.c                            |  2 +-\n t/README                               |  7 ---\n t/helper/test-read-midx.c              | 43 ++++++++++-----\n t/helper/test-ref-store.c              |  8 ---\n t/helper/test-submodule.c              |  4 +-\n t/t5319-multi-pack-index.sh            |  9 ++--\n t/t5526-fetch-submodules.sh            |  3 --\n t/t5531-deep-submodule-push.sh         |  3 --\n t/t5545-push-options.sh                |  3 --\n t/t5572-pull-submodule.sh              |  3 --\n t/t6437-submodule-merge.sh             |  3 --\n t/t7418-submodule-sparse-gitmodules.sh |  3 --\n t/t7814-grep-recurse-submodules.sh     |  3 --\n tmp-objdir.c                           |  5 --\n tmp-objdir.h                           |  6 ---\n unpack-trees.c                         |  9 ++--\n 33 files changed, 168 insertions(+), 247 deletions(-)\n\nRange-diff versus v1:\n\n -:  ---------- >  1:  4870d0661d cache-tree: drop `the_repository` in `cache_tree_fully_valid()`\n 1:  a4e2648526 !  2:  755652bfe1 cache-tree: remove dependency on `the_repository`\n    @@ Commit message\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n    - ## builtin/checkout.c ##\n    -@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *opts,\n    - \t\t}\n    - \t}\n    - \n    --\tif (!cache_tree_fully_valid(the_repository->index->cache_tree))\n    -+\tif (!cache_tree_fully_valid(the_repository, the_repository->index->cache_tree))\n    - \t\tcache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);\n    - \n    - \tif (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))\n    -\n    - ## builtin/commit.c ##\n    -@@ builtin/commit.c: static const char *prepare_index(const char **argv, const char *prefix,\n    - \t\t\t\t       LOCK_DIE_ON_ERROR);\n    - \t\trefresh_cache_or_die(refresh_flags);\n    - \t\tif (the_repository->index->cache_changed\n    --\t\t    || !cache_tree_fully_valid(the_repository->index->cache_tree))\n    -+\t\t    || !cache_tree_fully_valid(the_repository, the_repository->index->cache_tree))\n    - \t\t\tcache_tree_update(the_repository->index, WRITE_TREE_SILENT);\n    - \t\tif (write_locked_index(the_repository->index, &index_lock,\n    - \t\t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n    -\n      ## cache-tree.c ##\n     @@\n     -#define USE_THE_REPOSITORY_VARIABLE\n      #define DISABLE_SIGN_COMPARE_WARNINGS\n      \n      #include \"git-compat-util.h\"\n    -@@ cache-tree.c: static void discard_unused_subtrees(struct cache_tree *it)\n    - \t}\n    - }\n    - \n    --int cache_tree_fully_valid(struct cache_tree *it)\n    -+int cache_tree_fully_valid(struct repository *repo, struct cache_tree *it)\n    - {\n    - \tint i;\n    - \tif (!it)\n    - \t\treturn 0;\n    - \tif (it->entry_count < 0 ||\n    --\t    !odb_has_object(the_repository->objects, &it->oid,\n    -+\t    !odb_has_object(repo->objects, &it->oid,\n    - \t\t\t    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))\n    - \t\treturn 0;\n    - \tfor (i = 0; i < it->subtree_nr; i++) {\n    --\t\tif (!cache_tree_fully_valid(it->down[i]->cache_tree))\n    -+\t\tif (!cache_tree_fully_valid(repo, it->down[i]->cache_tree))\n    - \t\t\treturn 0;\n    - \t}\n    - \treturn 1;\n    +@@ cache-tree.c: int cache_tree_fully_valid(struct index_state *istate)\n    + \t\t\t\t\t\tistate->cache_tree);\n      }\n      \n     -static int must_check_existence(const struct cache_entry *ce)\n    @@ cache-tree.c: static struct cache_tree *read_one(const char **buffer, unsigned l\n      \n      \treturn result;\n      }\n    -@@ cache-tree.c: struct tree *write_in_core_index_as_tree(struct repository *repo,\n    - \tint was_valid, ret;\n    - \n    - \twas_valid = index_state->cache_tree &&\n    --\t\t    cache_tree_fully_valid(index_state->cache_tree);\n    -+\t\t    cache_tree_fully_valid(repo, index_state->cache_tree);\n    - \n    - \tret = write_index_as_tree_internal(&o, index_state, was_valid, 0, NULL);\n    - \tif (ret == WRITE_TREE_UNMERGED_INDEX) {\n     @@ cache-tree.c: int write_index_as_tree(struct object_id *oid, struct index_state *index_state,\n      \thold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);\n      \n    @@ cache-tree.c: int write_index_as_tree(struct object_id *oid, struct index_state\n      \tif (entries < 0) {\n      \t\tret = WRITE_TREE_UNREADABLE_INDEX;\n      \t\tgoto out;\n    -@@ cache-tree.c: int write_index_as_tree(struct object_id *oid, struct index_state *index_state,\n    - \n    - \twas_valid = !(flags & WRITE_TREE_IGNORE_CACHE_TREE) &&\n    - \t\t    index_state->cache_tree &&\n    --\t\t    cache_tree_fully_valid(index_state->cache_tree);\n    -+\t\t    cache_tree_fully_valid(index_state->repo, index_state->cache_tree);\n    - \n    - \tret = write_index_as_tree_internal(oid, index_state, was_valid, flags,\n    - \t\t\t\t\t   prefix);\n     @@ cache-tree.c: static void prime_cache_tree_rec(struct repository *r,\n      \t\t\tstruct cache_tree_sub *sub;\n      \t\t\tstruct tree *subtree = lookup_tree(r, &entry.oid);\n    @@ cache-tree.h: struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const c\n     +struct cache_tree *cache_tree_read(struct repository *repo,\n     +\t\t\t\t   const char *buffer, unsigned long size);\n      \n    --int cache_tree_fully_valid(struct cache_tree *);\n    -+int cache_tree_fully_valid(struct repository *, struct cache_tree *);\n    + int cache_tree_fully_valid(struct index_state *);\n      int cache_tree_update(struct index_state *, int);\n    - int cache_tree_verify(struct repository *, struct index_state *);\n    - \n     \n      ## read-cache-ll.h ##\n     @@ read-cache-ll.h: void validate_cache_entries(const struct index_state *istate);\n    @@ read-cache.c: void prefetch_cache_entries(const struct index_state *istate,\n      \t\tif (!odb_read_object_info_extended(the_repository->objects,\n      \t\t\t\t\t\t   &ce->oid, NULL,\n     \n    - ## sequencer.c ##\n    -@@ sequencer.c: static int do_recursive_merge(struct repository *r,\n    - \n    - static struct object_id *get_cache_tree_oid(struct index_state *istate)\n    - {\n    --\tif (!cache_tree_fully_valid(istate->cache_tree))\n    -+\tif (!cache_tree_fully_valid(istate->repo, istate->cache_tree))\n    - \t\tif (cache_tree_update(istate, 0)) {\n    - \t\t\terror(_(\"unable to update cache tree\"));\n    - \t\t\treturn NULL;\n    -\n    - ## sparse-index.c ##\n    -@@ sparse-index.c: int convert_to_sparse(struct index_state *istate, int flags)\n    - \tif (index_has_unmerged_entries(istate))\n    - \t\treturn 0;\n    - \n    --\tif (!cache_tree_fully_valid(istate->cache_tree)) {\n    -+\tif (!cache_tree_fully_valid(istate->repo, istate->cache_tree)) {\n    - \t\t/* Clear and recompute the cache-tree */\n    - \t\tcache_tree_free(&istate->cache_tree);\n    - \n    -\n      ## unpack-trees.c ##\n     @@ unpack-trees.c: static void report_collided_checkout(struct index_state *index)\n      \tstring_list_clear(&list, 0);\n    @@ unpack-trees.c: static int check_updates(struct unpack_trees_options *o,\n      \t\t\tsize_t last_pc_queue_size = pc_queue_size();\n      \n      \t\t\tif (ce->ce_flags & CE_WT_REMOVE)\n    -@@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n    - \t\t\t}\n    - \n    - \t\t\tif (!o->skip_cache_tree_update &&\n    --\t\t\t    !cache_tree_fully_valid(o->internal.result.cache_tree))\n    -+\t\t\t    !cache_tree_fully_valid(the_repository, o->internal.result.cache_tree))\n    - \t\t\t\tcache_tree_update(&o->internal.result,\n    - \t\t\t\t\t\t  WRITE_TREE_SILENT |\n    - \t\t\t\t\t\t  WRITE_TREE_REPAIR);\n 2:  bedb614b5c =  3:  46bbe548a6 submodule-config: remove uses of `the_repository`\n 3:  8fa02cdd5c =  4:  65f8634bda submodule-config: stop using `the_hash_algo`\n 4:  ebf1dfb548 =  5:  4faf4fdd57 submodule-config: stop registering submodule sources\n 5:  dfe1a212f7 =  6:  dee5aca311 builtin/grep: stop registering submodule ODB as source\n 6:  1644e49419 =  7:  2ce128799a odb: remove infrastructure to register submodule sources\n 7:  3f3ccfba74 =  8:  0a174f6304 tmp-objdir: drop unused function to register alternate\n 8:  f1341b797b =  9:  d22c376cd2 odb/packed: fix memory leaks when freeing source\n 9:  c1f6d9ebdd = 10:  09405ade4c builtin/multi-pack-index: refuse unknown sources with \"--object-dir=\"\n10:  c5b9c1c96e = 11:  557b3ab923 t/helper: adapt read-midx to not link ad-hoc source anymore\n11:  3f43f73cd8 = 12:  78b26c172a t/helper: stop registering alternates in \"ref-store\" command\n12:  4c670d726f = 13:  1ca49caf1c odb: remove the ability to link sources ad-hoc\n\n---\nbase-commit: e5d60560f61f520e9ea350645a6cc9770b0f1607\nchange-id: 20260811-pks-odb-registering-in-memory-sources-88648cd95735\n\n"},{"id":"551756","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-1-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 01/13] cache-tree: drop `the_repository` in `cache_tree_fully_valid()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:49Z","receivedAt":"2026-09-02T13:35:05Z","isPatch":true,"body":"The function `cache_tree_fully_valid()` verifies whether the cache tree\nowned by the index is valid or not. As part of that, the function checks\nwhether the objects referenced by the cache all exist. But because the\nfunction has no repository available, it is using the object database of\n`the_repository` instead.\n\nWe could of course adapt callers to pass in a repository as parameter\nexplicitly to get rid of this implicit dependency on global state. But\nall of them pass the cache tree owned by a `struct index_state`, and\nthat structure already has a reference to its owning repository.\n\nSo instead, adapt the function to accept a `struct index_state`, which\nensures that callers will implicitly always pass the correct repository.\nAdapt callers accordingly.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/checkout.c |  2 +-\n builtin/commit.c   |  2 +-\n cache-tree.c       | 17 ++++++++++++-----\n cache-tree.h       |  2 +-\n sequencer.c        |  2 +-\n sparse-index.c     |  2 +-\n unpack-trees.c     |  2 +-\n 7 files changed, 18 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..505d3f7bf3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -921,7 +921,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t}\n \t}\n \n-\tif (!cache_tree_fully_valid(the_repository->index->cache_tree))\n+\tif (!cache_tree_fully_valid(the_repository->index))\n \t\tcache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);\n \n \tif (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 28f6174503..840b6b4083 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -484,7 +484,7 @@ static const char *prepare_index(const char **argv, const char *prefix,\n \t\t\t\t       LOCK_DIE_ON_ERROR);\n \t\trefresh_cache_or_die(refresh_flags);\n \t\tif (the_repository->index->cache_changed\n-\t\t    || !cache_tree_fully_valid(the_repository->index->cache_tree))\n+\t\t    || !cache_tree_fully_valid(the_repository->index))\n \t\t\tcache_tree_update(the_repository->index, WRITE_TREE_SILENT);\n \t\tif (write_locked_index(the_repository->index, &index_lock,\n \t\t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\ndiff --git a/cache-tree.c b/cache-tree.c\nindex a220372a42..6103b3fcb3 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -275,22 +275,29 @@ static void discard_unused_subtrees(struct cache_tree *it)\n \t}\n }\n \n-int cache_tree_fully_valid(struct cache_tree *it)\n+static int cache_tree_fully_valid_recursive(struct object_database *odb,\n+\t\t\t\t\t    struct cache_tree *it)\n {\n \tint i;\n \tif (!it)\n \t\treturn 0;\n \tif (it->entry_count < 0 ||\n-\t    !odb_has_object(the_repository->objects, &it->oid,\n+\t    !odb_has_object(odb, &it->oid,\n \t\t\t    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))\n \t\treturn 0;\n \tfor (i = 0; i < it->subtree_nr; i++) {\n-\t\tif (!cache_tree_fully_valid(it->down[i]->cache_tree))\n+\t\tif (!cache_tree_fully_valid_recursive(odb, it->down[i]->cache_tree))\n \t\t\treturn 0;\n \t}\n \treturn 1;\n }\n \n+int cache_tree_fully_valid(struct index_state *istate)\n+{\n+\treturn cache_tree_fully_valid_recursive(istate->repo->objects,\n+\t\t\t\t\t\tistate->cache_tree);\n+}\n+\n static int must_check_existence(const struct cache_entry *ce)\n {\n \treturn !(repo_has_promisor_remote(the_repository) && ce_skip_worktree(ce));\n@@ -775,7 +782,7 @@ struct tree *write_in_core_index_as_tree(struct repository *repo,\n \tint was_valid, ret;\n \n \twas_valid = index_state->cache_tree &&\n-\t\t    cache_tree_fully_valid(index_state->cache_tree);\n+\t\t    cache_tree_fully_valid(index_state);\n \n \tret = write_index_as_tree_internal(&o, index_state, was_valid, 0, NULL);\n \tif (ret == WRITE_TREE_UNMERGED_INDEX) {\n@@ -811,7 +818,7 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,\n \n \twas_valid = !(flags & WRITE_TREE_IGNORE_CACHE_TREE) &&\n \t\t    index_state->cache_tree &&\n-\t\t    cache_tree_fully_valid(index_state->cache_tree);\n+\t\t    cache_tree_fully_valid(index_state);\n \n \tret = write_index_as_tree_internal(oid, index_state, was_valid, flags,\n \t\t\t\t\t   prefix);\ndiff --git a/cache-tree.h b/cache-tree.h\nindex f8bddae523..4b3f60d6db 100644\n--- a/cache-tree.h\n+++ b/cache-tree.h\n@@ -31,7 +31,7 @@ int cache_tree_subtree_pos(struct cache_tree *it, const char *path, int pathlen)\n void cache_tree_write(struct strbuf *, struct cache_tree *root);\n struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);\n \n-int cache_tree_fully_valid(struct cache_tree *);\n+int cache_tree_fully_valid(struct index_state *);\n int cache_tree_update(struct index_state *, int);\n int cache_tree_verify(struct repository *, struct index_state *);\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 65afd100d9..11a95c031b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -814,7 +814,7 @@ static int do_recursive_merge(struct repository *r,\n \n static struct object_id *get_cache_tree_oid(struct index_state *istate)\n {\n-\tif (!cache_tree_fully_valid(istate->cache_tree))\n+\tif (!cache_tree_fully_valid(istate))\n \t\tif (cache_tree_update(istate, 0)) {\n \t\t\terror(_(\"unable to update cache tree\"));\n \t\t\treturn NULL;\ndiff --git a/sparse-index.c b/sparse-index.c\nindex c1fa231a89..3d77dadae5 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -228,7 +228,7 @@ int convert_to_sparse(struct index_state *istate, int flags)\n \tif (index_has_unmerged_entries(istate))\n \t\treturn 0;\n \n-\tif (!cache_tree_fully_valid(istate->cache_tree)) {\n+\tif (!cache_tree_fully_valid(istate)) {\n \t\t/* Clear and recompute the cache-tree */\n \t\tcache_tree_free(&istate->cache_tree);\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 154d6d40a1..f6bb1e6d2b 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2086,7 +2086,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\t\t}\n \n \t\t\tif (!o->skip_cache_tree_update &&\n-\t\t\t    !cache_tree_fully_valid(o->internal.result.cache_tree))\n+\t\t\t    !cache_tree_fully_valid(&o->internal.result))\n \t\t\t\tcache_tree_update(&o->internal.result,\n \t\t\t\t\t\t  WRITE_TREE_SILENT |\n \t\t\t\t\t\t  WRITE_TREE_REPAIR);\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551758","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-2-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 02/13] cache-tree: remove dependency on `the_repository`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:50Z","receivedAt":"2026-09-02T13:35:06Z","isPatch":true,"body":"The \"cache-tree\" subsystem still depends on `the_repository`. Adapt it\nto instead use repositories provided via the context, either as a new\nparameter or the one passed in via `struct index_state`.\n\nBesides getting rid of `the_repository`, this also removes the last\ndependency on registering submodule sources with the main object\ndatabase. When reading gitmodules from a submodule's index we implicitly\nread that object via `the_repository`'s object database, which is of\ncourse wrong. This works though because we would then register the\nsubmodule's object database with the main object database, but a later\npatch is going to get rid of that mechanism.\n\nYou can verify that we indeed no longer depend on this mechanism by\nrunning tests with `GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=true`. Without\nthis patch we fail in t1092, with this patch we never register submodule\nobject databases anymore.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n cache-tree.c    | 78 +++++++++++++++++++++++++++++++--------------------------\n cache-tree.h    |  5 ++--\n read-cache-ll.h |  5 ++--\n read-cache.c    |  9 ++++---\n unpack-trees.c  |  7 +++---\n 5 files changed, 57 insertions(+), 47 deletions(-)\n\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 6103b3fcb3..b8cbb5da22 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -1,4 +1,3 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n@@ -298,12 +297,14 @@ int cache_tree_fully_valid(struct index_state *istate)\n \t\t\t\t\t\tistate->cache_tree);\n }\n \n-static int must_check_existence(const struct cache_entry *ce)\n+static int must_check_existence(const struct cache_entry *ce, void *cb_data)\n {\n-\treturn !(repo_has_promisor_remote(the_repository) && ce_skip_worktree(ce));\n+\tstruct repository *repo = cb_data;\n+\treturn !(repo_has_promisor_remote(repo) && ce_skip_worktree(ce));\n }\n \n-static int update_one(struct cache_tree *it,\n+static int update_one(struct repository *repo,\n+\t\t      struct cache_tree *it,\n \t\t      struct cache_entry **cache,\n \t\t      int entries,\n \t\t      const char *base,\n@@ -341,7 +342,7 @@ static int update_one(struct cache_tree *it,\n \t}\n \n \tif (0 <= it->entry_count &&\n-\t    odb_has_object(the_repository->objects, &it->oid,\n+\t    odb_has_object(repo->objects, &it->oid,\n \t\t\t   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))\n \t\treturn it->entry_count;\n \n@@ -382,7 +383,8 @@ static int update_one(struct cache_tree *it,\n \t\tsub = find_subtree(it, path + baselen, sublen, 1);\n \t\tif (!sub->cache_tree)\n \t\t\tsub->cache_tree = cache_tree();\n-\t\tsubcnt = update_one(sub->cache_tree,\n+\t\tsubcnt = update_one(repo,\n+\t\t\t\t    sub->cache_tree,\n \t\t\t\t    cache + i, entries - i,\n \t\t\t\t    path,\n \t\t\t\t    baselen + sublen + 1,\n@@ -446,10 +448,10 @@ static int update_one(struct cache_tree *it,\n \t\t}\n \n \t\tce_missing_ok = mode == S_IFGITLINK || missing_ok ||\n-\t\t\t!must_check_existence(ce);\n+\t\t\t!must_check_existence(ce, repo);\n \t\tif (is_null_oid(oid) ||\n \t\t    (!ce_missing_ok &&\n-\t\t     !odb_has_object(the_repository->objects, oid,\n+\t\t     !odb_has_object(repo->objects, oid,\n \t\t\t\t     ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))) {\n \t\t\tstrbuf_release(&buffer);\n \t\t\tif (expected_missing)\n@@ -481,12 +483,12 @@ static int update_one(struct cache_tree *it,\n \t\t/*\n \t\t * \"sub\" can be an empty tree if all subentries are i-t-a.\n \t\t */\n-\t\tif (contains_ita && is_empty_tree_oid(oid, the_repository->hash_algo))\n+\t\tif (contains_ita && is_empty_tree_oid(oid, repo->hash_algo))\n \t\t\tcontinue;\n \n \t\tstrbuf_grow(&buffer, entlen + 100);\n \t\tstrbuf_addf(&buffer, \"%o %.*s%c\", mode, entlen, path + baselen, '\\0');\n-\t\tstrbuf_add(&buffer, oid->hash, the_hash_algo->rawsz);\n+\t\tstrbuf_add(&buffer, oid->hash, repo->hash_algo->rawsz);\n \n #if DEBUG_CACHE_TREE\n \t\tfprintf(stderr, \"cache-tree update-one %o %.*s\\n\",\n@@ -496,16 +498,16 @@ static int update_one(struct cache_tree *it,\n \n \tif (repair) {\n \t\tstruct object_id oid;\n-\t\thash_object_file(the_hash_algo, buffer.buf, buffer.len,\n+\t\thash_object_file(repo->hash_algo, buffer.buf, buffer.len,\n \t\t\t\t OBJ_TREE, &oid);\n-\t\tif (odb_has_object(the_repository->objects, &oid, ODB_HAS_OBJECT_RECHECK_PACKED))\n+\t\tif (odb_has_object(repo->objects, &oid, ODB_HAS_OBJECT_RECHECK_PACKED))\n \t\t\toidcpy(&it->oid, &oid);\n \t\telse\n \t\t\tto_invalidate = 1;\n \t} else if (dryrun) {\n-\t\thash_object_file(the_hash_algo, buffer.buf, buffer.len,\n+\t\thash_object_file(repo->hash_algo, buffer.buf, buffer.len,\n \t\t\t\t OBJ_TREE, &it->oid);\n-\t} else if (odb_write_object_ext(the_repository->objects, buffer.buf, buffer.len, OBJ_TREE,\n+\t} else if (odb_write_object_ext(repo->objects, buffer.buf, buffer.len, OBJ_TREE,\n \t\t\t\t\t&it->oid, NULL, flags & WRITE_TREE_SILENT ? ODB_WRITE_OBJECT_SILENT : 0)) {\n \t\tstrbuf_release(&buffer);\n \t\treturn -1;\n@@ -523,7 +525,7 @@ static int update_one(struct cache_tree *it,\n \n int cache_tree_update(struct index_state *istate, int flags)\n {\n-\tint inflight = !!the_repository->objects->transaction;\n+\tint inflight = !!istate->repo->objects->transaction;\n \tstruct odb_transaction *transaction;\n \tint skip, i;\n \n@@ -535,14 +537,14 @@ int cache_tree_update(struct index_state *istate, int flags)\n \tif (!istate->cache_tree)\n \t\tistate->cache_tree = cache_tree();\n \n-\tif (!(flags & WRITE_TREE_MISSING_OK) && repo_has_promisor_remote(the_repository))\n-\t\tprefetch_cache_entries(istate, must_check_existence);\n+\tif (!(flags & WRITE_TREE_MISSING_OK) && repo_has_promisor_remote(istate->repo))\n+\t\tprefetch_cache_entries(istate, must_check_existence, istate->repo);\n \n \ttrace_performance_enter();\n \ttrace2_region_enter(\"cache_tree\", \"update\", istate->repo);\n \tif (!inflight)\n-\t\todb_transaction_begin_or_die(the_repository->objects, &transaction, 0);\n-\ti = update_one(istate->cache_tree, istate->cache, istate->cache_nr,\n+\t\todb_transaction_begin_or_die(istate->repo->objects, &transaction, 0);\n+\ti = update_one(istate->repo, istate->cache_tree, istate->cache, istate->cache_nr,\n \t\t       \"\", 0, &skip, flags);\n \tif (!inflight)\n \t\todb_transaction_commit_and_finalize_or_die(transaction);\n@@ -554,7 +556,8 @@ int cache_tree_update(struct index_state *istate, int flags)\n \treturn 0;\n }\n \n-static void write_one(struct strbuf *buffer, struct cache_tree *it,\n+static void write_one(struct repository *repo,\n+\t\t      struct strbuf *buffer, struct cache_tree *it,\n \t\t      const char *path, int pathlen)\n {\n \tint i;\n@@ -580,7 +583,7 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it,\n #endif\n \n \tif (0 <= it->entry_count) {\n-\t\tstrbuf_add(buffer, it->oid.hash, the_hash_algo->rawsz);\n+\t\tstrbuf_add(buffer, it->oid.hash, repo->hash_algo->rawsz);\n \t}\n \tfor (i = 0; i < it->subtree_nr; i++) {\n \t\tstruct cache_tree_sub *down = it->down[i];\n@@ -590,15 +593,16 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it,\n \t\t\t\t\t     prev->name, prev->namelen) <= 0)\n \t\t\t\tdie(\"fatal - unsorted cache subtree\");\n \t\t}\n-\t\twrite_one(buffer, down->cache_tree, down->name, down->namelen);\n+\t\twrite_one(repo, buffer, down->cache_tree, down->name, down->namelen);\n \t}\n }\n \n-void cache_tree_write(struct strbuf *sb, struct cache_tree *root)\n+void cache_tree_write(struct repository *repo,\n+\t\t      struct strbuf *sb, struct cache_tree *root)\n {\n-\ttrace2_region_enter(\"cache_tree\", \"write\", the_repository);\n-\twrite_one(sb, root, \"\", 0);\n-\ttrace2_region_leave(\"cache_tree\", \"write\", the_repository);\n+\ttrace2_region_enter(\"cache_tree\", \"write\", repo);\n+\twrite_one(repo, sb, root, \"\", 0);\n+\ttrace2_region_leave(\"cache_tree\", \"write\", repo);\n }\n \n static int parse_int(const char **ptr, unsigned long *len_p, int *out)\n@@ -632,13 +636,14 @@ static int parse_int(const char **ptr, unsigned long *len_p, int *out)\n \treturn 0;\n }\n \n-static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)\n+static struct cache_tree *read_one(struct repository *repo,\n+\t\t\t\t   const char **buffer, unsigned long *size_p)\n {\n \tconst char *buf = *buffer;\n \tunsigned long size = *size_p;\n \tstruct cache_tree *it;\n \tint i, subtree_nr;\n-\tconst unsigned rawsz = the_hash_algo->rawsz;\n+\tconst unsigned rawsz = repo->hash_algo->rawsz;\n \n \tit = NULL;\n \t/* skip name, but make sure name exists */\n@@ -665,7 +670,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)\n \t\tif (size < rawsz)\n \t\t\tgoto free_return;\n \t\toidread(&it->oid, (const unsigned char *)buf,\n-\t\t\tthe_repository->hash_algo);\n+\t\t\trepo->hash_algo);\n \t\tbuf += rawsz;\n \t\tsize -= rawsz;\n \t}\n@@ -693,7 +698,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)\n \t\tstruct cache_tree_sub *subtree;\n \t\tconst char *name = buf;\n \n-\t\tsub = read_one(&buf, &size);\n+\t\tsub = read_one(repo, &buf, &size);\n \t\tif (!sub)\n \t\t\tgoto free_return;\n \t\tsubtree = cache_tree_sub(it, name);\n@@ -710,16 +715,17 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)\n \treturn NULL;\n }\n \n-struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)\n+struct cache_tree *cache_tree_read(struct repository *repo,\n+\t\t\t\t   const char *buffer, unsigned long size)\n {\n \tstruct cache_tree *result;\n \n \tif (buffer[0])\n \t\treturn NULL; /* not the whole tree */\n \n-\ttrace2_region_enter(\"cache_tree\", \"read\", the_repository);\n-\tresult = read_one(&buffer, &size);\n-\ttrace2_region_leave(\"cache_tree\", \"read\", the_repository);\n+\ttrace2_region_enter(\"cache_tree\", \"read\", repo);\n+\tresult = read_one(repo, &buffer, &size);\n+\ttrace2_region_leave(\"cache_tree\", \"read\", repo);\n \n \treturn result;\n }\n@@ -810,7 +816,7 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,\n \thold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);\n \n \tentries = read_index_from(index_state, index_path,\n-\t\t\t\t  repo_get_git_dir(the_repository));\n+\t\t\t\t  repo_get_git_dir(index_state->repo));\n \tif (entries < 0) {\n \t\tret = WRITE_TREE_UNREADABLE_INDEX;\n \t\tgoto out;\n@@ -866,7 +872,7 @@ static void prime_cache_tree_rec(struct repository *r,\n \t\t\tstruct cache_tree_sub *sub;\n \t\t\tstruct tree *subtree = lookup_tree(r, &entry.oid);\n \n-\t\t\tif (repo_parse_tree(the_repository, subtree) < 0)\n+\t\t\tif (repo_parse_tree(r, subtree) < 0)\n \t\t\t\texit(128);\n \t\t\tsub = cache_tree_sub(it, entry.path);\n \t\t\tsub->cache_tree = cache_tree();\ndiff --git a/cache-tree.h b/cache-tree.h\nindex 4b3f60d6db..7a2177de83 100644\n--- a/cache-tree.h\n+++ b/cache-tree.h\n@@ -28,8 +28,9 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);\n \n int cache_tree_subtree_pos(struct cache_tree *it, const char *path, int pathlen);\n \n-void cache_tree_write(struct strbuf *, struct cache_tree *root);\n-struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);\n+void cache_tree_write(struct repository *repo, struct strbuf *, struct cache_tree *root);\n+struct cache_tree *cache_tree_read(struct repository *repo,\n+\t\t\t\t   const char *buffer, unsigned long size);\n \n int cache_tree_fully_valid(struct index_state *);\n int cache_tree_update(struct index_state *, int);\ndiff --git a/read-cache-ll.h b/read-cache-ll.h\nindex 8eb266cfd1..066dd8bc3b 100644\n--- a/read-cache-ll.h\n+++ b/read-cache-ll.h\n@@ -269,9 +269,10 @@ void validate_cache_entries(const struct index_state *istate);\n  * the given predicate. This function should only be called if\n  * repo_has_promisor_remote() returns true.\n  */\n-typedef int (*must_prefetch_predicate)(const struct cache_entry *);\n+typedef int (*must_prefetch_predicate)(const struct cache_entry *, void *cb_data);\n void prefetch_cache_entries(const struct index_state *istate,\n-\t\t\t    must_prefetch_predicate must_prefetch);\n+\t\t\t    must_prefetch_predicate must_prefetch,\n+\t\t\t    void *cb_data);\n \n /* Initialize and use the cache information */\n struct lock_file;\ndiff --git a/read-cache.c b/read-cache.c\nindex 8044ff820b..e40f290bb3 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1748,7 +1748,7 @@ static int read_index_extension(struct index_state *istate,\n {\n \tswitch (CACHE_EXT(ext)) {\n \tcase CACHE_EXT_TREE:\n-\t\tistate->cache_tree = cache_tree_read(data, sz);\n+\t\tistate->cache_tree = cache_tree_read(istate->repo, data, sz);\n \t\tbreak;\n \tcase CACHE_EXT_RESOLVE_UNDO:\n \t\tistate->resolve_undo = resolve_undo_read(data, sz, the_hash_algo);\n@@ -3012,7 +3012,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,\n \t    !drop_cache_tree && istate->cache_tree) {\n \t\tstrbuf_reset(&sb);\n \n-\t\tcache_tree_write(&sb, istate->cache_tree);\n+\t\tcache_tree_write(istate->repo, &sb, istate->cache_tree);\n \t\terr = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;\n \t\thashwrite(f, sb.buf, sb.len);\n \t\tif (err) {\n@@ -3733,7 +3733,8 @@ static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_ta\n }\n \n void prefetch_cache_entries(const struct index_state *istate,\n-\t\t\t    must_prefetch_predicate must_prefetch)\n+\t\t\t    must_prefetch_predicate must_prefetch,\n+\t\t\t    void *cb_data)\n {\n \tint i;\n \tstruct oid_array to_fetch = OID_ARRAY_INIT;\n@@ -3741,7 +3742,7 @@ void prefetch_cache_entries(const struct index_state *istate,\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tstruct cache_entry *ce = istate->cache[i];\n \n-\t\tif (S_ISGITLINK(ce->ce_mode) || !must_prefetch(ce))\n+\t\tif (S_ISGITLINK(ce->ce_mode) || !must_prefetch(ce, cb_data))\n \t\t\tcontinue;\n \t\tif (!odb_read_object_info_extended(the_repository->objects,\n \t\t\t\t\t\t   &ce->oid, NULL,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f6bb1e6d2b..1802809ad3 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -416,7 +416,8 @@ static void report_collided_checkout(struct index_state *index)\n \tstring_list_clear(&list, 0);\n }\n \n-static int must_checkout(const struct cache_entry *ce)\n+static int must_checkout(const struct cache_entry *ce,\n+\t\t\t void *cb_data UNUSED)\n {\n \treturn ce->ce_flags & CE_UPDATE;\n }\n@@ -477,7 +478,7 @@ static int check_updates(struct unpack_trees_options *o,\n \t\t * Prefetch the objects that are to be checked out in the loop\n \t\t * below.\n \t\t */\n-\t\tprefetch_cache_entries(index, must_checkout);\n+\t\tprefetch_cache_entries(index, must_checkout, NULL);\n \n \tget_parallel_checkout_configs(&pc_workers, &pc_threshold);\n \n@@ -487,7 +488,7 @@ static int check_updates(struct unpack_trees_options *o,\n \tfor (i = 0; i < index->cache_nr; i++) {\n \t\tstruct cache_entry *ce = index->cache[i];\n \n-\t\tif (must_checkout(ce)) {\n+\t\tif (must_checkout(ce, NULL)) {\n \t\t\tsize_t last_pc_queue_size = pc_queue_size();\n \n \t\t\tif (ce->ce_flags & CE_WT_REMOVE)\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551759","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-3-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 03/13] submodule-config: remove uses of `the_repository`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:51Z","receivedAt":"2026-09-02T13:35:08Z","isPatch":true,"body":"Several functions in the submodule-config subsystem implicitly depend\non `the_repository`. Refactor these to take a `struct repository` as\nparameter and adapt callers accordingly.\n\nNote that as usual with these refactorings, callers simply pass\n`the_repository` even if they already have a different repository\navailable in the calling context. This simplifies the migration and\nensures that we don't have a change in behaviour.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fetch.c             |  2 +-\n builtin/grep.c              |  2 +-\n builtin/submodule--helper.c |  8 ++++----\n submodule-config.c          | 49 ++++++++++++++++++++++++++-------------------\n submodule-config.h          | 12 +++++++----\n submodule.c                 |  2 +-\n t/helper/test-submodule.c   |  4 ++--\n 7 files changed, 45 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex ab7db2be06..533fdfe7d8 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -2681,7 +2681,7 @@ int cmd_fetch(int argc,\n \t\tint *rs = config.recurse_submodules == RECURSE_SUBMODULES_DEFAULT\n \t\t\t  ? &config.recurse_submodules : NULL;\n \n-\t\tfetch_config_from_gitmodules(sfjc, rs);\n+\t\tfetch_config_from_gitmodules(the_repository, sfjc, rs);\n \t}\n \n \ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex d3d86abe01..073dfaaf45 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -897,7 +897,7 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\tif (recurse_submodules) {\n \t\t\tsubmodule_free(opt->repo);\n \t\t\tobj_read_lock();\n-\t\t\tgitmodules_config_oid(&real_obj->oid);\n+\t\t\tgitmodules_config_oid(the_repository, &real_obj->oid);\n \t\t\tobj_read_unlock();\n \t\t}\n \t\tif (grep_object(opt, pathspec, real_obj, list->objects[i].name,\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e7cd3225fa..aaaa963fd8 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3041,7 +3041,7 @@ static int module_update(int argc, const char **argv, const char *prefix,\n \t\tNULL\n \t};\n \n-\tupdate_clone_config_from_gitmodules(&opt.max_jobs);\n+\tupdate_clone_config_from_gitmodules(the_repository, &opt.max_jobs);\n \trepo_config(the_repository, git_update_clone_config, &opt.max_jobs);\n \n \targc = parse_options(argc, argv, prefix, module_update_options,\n@@ -3255,7 +3255,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix,\n \t\t    path);\n \n \tconfig_name = xstrfmt(\"submodule.%s.url\", sub->name);\n-\tret = config_set_in_gitmodules_file_gently(config_name, newurl);\n+\tret = config_set_in_gitmodules_file_gently(the_repository, config_name, newurl);\n \n \tif (!ret) {\n \t\trepo_read_gitmodules(the_repository, 0);\n@@ -3311,7 +3311,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix,\n \t\t    path);\n \n \tconfig_name = xstrfmt(\"submodule.%s.branch\", sub->name);\n-\tret = config_set_in_gitmodules_file_gently(config_name, opt_branch);\n+\tret = config_set_in_gitmodules_file_gently(the_repository, config_name, opt_branch);\n \n \tfree(config_name);\n \treturn !!ret;\n@@ -3510,7 +3510,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n \t\tdie(_(\"please make sure that the .gitmodules file is in the working tree\"));\n \n \tkey = xstrfmt(\"submodule.%s.%s\", name, var);\n-\tret = config_set_in_gitmodules_file_gently(key, value);\n+\tret = config_set_in_gitmodules_file_gently(the_repository, key, value);\n \tfree(key);\n \n \treturn ret;\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f75997402a..f8c2cf7a93 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -667,19 +667,20 @@ static int parse_config(const char *var, const char *value,\n \treturn ret;\n }\n \n-static int gitmodule_oid_from_commit(const struct object_id *treeish_name,\n+static int gitmodule_oid_from_commit(struct repository *repo,\n+\t\t\t\t     const struct object_id *treeish_name,\n \t\t\t\t     struct object_id *gitmodules_oid,\n \t\t\t\t     struct strbuf *rev)\n {\n \tint ret = 0;\n \n \tif (is_null_oid(treeish_name)) {\n-\t\toidclr(gitmodules_oid, the_repository->hash_algo);\n+\t\toidclr(gitmodules_oid, repo->hash_algo);\n \t\treturn 1;\n \t}\n \n \tstrbuf_addf(rev, \"%s:.gitmodules\", oid_to_hex(treeish_name));\n-\tif (repo_get_oid(the_repository, rev->buf, gitmodules_oid) >= 0)\n+\tif (repo_get_oid(repo, rev->buf, gitmodules_oid) >= 0)\n \t\tret = 1;\n \n \treturn ret;\n@@ -689,9 +690,11 @@ static int gitmodule_oid_from_commit(const struct object_id *treeish_name,\n  * (key) with on-demand reading of the appropriate .gitmodules from\n  * revisions.\n  */\n-static const struct submodule *config_from(struct submodule_cache *cache,\n-\t\tconst struct object_id *treeish_name, const char *key,\n-\t\tenum lookup_type lookup_type)\n+static const struct submodule *config_from(struct repository *repo,\n+\t\t\t\t\t   struct submodule_cache *cache,\n+\t\t\t\t\t   const struct object_id *treeish_name,\n+\t\t\t\t\t   const char *key,\n+\t\t\t\t\t   enum lookup_type lookup_type)\n {\n \tstruct strbuf rev = STRBUF_INIT;\n \tsize_t config_size;\n@@ -718,7 +721,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,\n \t\treturn entry->config;\n \t}\n \n-\tif (!gitmodule_oid_from_commit(treeish_name, &oid, &rev))\n+\tif (!gitmodule_oid_from_commit(repo, treeish_name, &oid, &rev))\n \t\tgoto out;\n \n \tswitch (lookup_type) {\n@@ -732,7 +735,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,\n \tif (submodule)\n \t\tgoto out;\n \n-\tconfig = odb_read_object(the_repository->objects, &oid,\n+\tconfig = odb_read_object(repo->objects, &oid,\n \t\t\t\t &type, &config_size);\n \tif (!config || type != OBJ_BLOB)\n \t\tgoto out;\n@@ -843,21 +846,22 @@ void repo_read_gitmodules(struct repository *repo, int skip_if_read)\n \trepo->submodule_cache->gitmodules_read = 1;\n }\n \n-void gitmodules_config_oid(const struct object_id *commit_oid)\n+void gitmodules_config_oid(struct repository *repo,\n+\t\t\t   const struct object_id *commit_oid)\n {\n \tstruct strbuf rev = STRBUF_INIT;\n \tstruct object_id oid;\n \n-\tsubmodule_cache_check_init(the_repository);\n+\tsubmodule_cache_check_init(repo);\n \n-\tif (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {\n+\tif (gitmodule_oid_from_commit(repo, commit_oid, &oid, &rev)) {\n \t\tgit_config_from_blob_oid(gitmodules_cb, rev.buf,\n-\t\t\t\t\t the_repository, &oid, the_repository,\n+\t\t\t\t\t repo, &oid, repo,\n \t\t\t\t\t CONFIG_SCOPE_UNKNOWN);\n \t}\n \tstrbuf_release(&rev);\n \n-\tthe_repository->submodule_cache->gitmodules_read = 1;\n+\trepo->submodule_cache->gitmodules_read = 1;\n }\n \n const struct submodule *submodule_from_name(struct repository *r,\n@@ -865,7 +869,7 @@ const struct submodule *submodule_from_name(struct repository *r,\n \t\tconst char *name)\n {\n \trepo_read_gitmodules(r, 1);\n-\treturn config_from(r->submodule_cache, treeish_name, name, lookup_name);\n+\treturn config_from(r, r->submodule_cache, treeish_name, name, lookup_name);\n }\n \n const struct submodule *submodule_from_path(struct repository *r,\n@@ -873,7 +877,7 @@ const struct submodule *submodule_from_path(struct repository *r,\n \t\tconst char *path)\n {\n \trepo_read_gitmodules(r, 1);\n-\treturn config_from(r->submodule_cache, treeish_name, path, lookup_path);\n+\treturn config_from(r, r->submodule_cache, treeish_name, path, lookup_path);\n }\n \n /**\n@@ -980,11 +984,12 @@ int print_config_from_gitmodules(struct repository *repo, const char *key)\n \treturn 0;\n }\n \n-int config_set_in_gitmodules_file_gently(const char *key, const char *value)\n+int config_set_in_gitmodules_file_gently(struct repository *repo,\n+\t\t\t\t\t const char *key, const char *value)\n {\n \tint ret;\n \n-\tret = repo_config_set_in_file_gently(the_repository, GITMODULES_FILE, key, NULL, value);\n+\tret = repo_config_set_in_file_gently(repo, GITMODULES_FILE, key, NULL, value);\n \tif (ret < 0)\n \t\t/* Maybe the user already did that, don't error out here */\n \t\twarning(_(\"Could not update .gitmodules entry %s\"), key);\n@@ -1017,13 +1022,15 @@ static int gitmodules_fetch_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)\n+void fetch_config_from_gitmodules(struct repository *repo,\n+\t\t\t\t  int *max_children,\n+\t\t\t\t  int *recurse_submodules)\n {\n \tstruct fetch_config config = {\n \t\t.max_children = max_children,\n \t\t.recurse_submodules = recurse_submodules\n \t};\n-\tconfig_from_gitmodules(gitmodules_fetch_config, the_repository, &config);\n+\tconfig_from_gitmodules(gitmodules_fetch_config, repo, &config);\n }\n \n static int gitmodules_update_clone_config(const char *var, const char *value,\n@@ -1036,7 +1043,7 @@ static int gitmodules_update_clone_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-void update_clone_config_from_gitmodules(int *max_jobs)\n+void update_clone_config_from_gitmodules(struct repository *repo, int *max_jobs)\n {\n-\tconfig_from_gitmodules(gitmodules_update_clone_config, the_repository, max_jobs);\n+\tconfig_from_gitmodules(gitmodules_update_clone_config, repo, max_jobs);\n }\ndiff --git a/submodule-config.h b/submodule-config.h\nindex f55d4e3b61..755570d5d1 100644\n--- a/submodule-config.h\n+++ b/submodule-config.h\n@@ -57,7 +57,8 @@ int option_fetch_parse_recurse_submodules(const struct option *opt,\n int parse_update_recurse_submodules_arg(const char *opt, const char *arg);\n int parse_push_recurse_submodules_arg(const char *opt, const char *arg);\n void repo_read_gitmodules(struct repository *repo, int skip_if_read);\n-void gitmodules_config_oid(const struct object_id *commit_oid);\n+void gitmodules_config_oid(struct repository *repo,\n+\t\t\t   const struct object_id *commit_oid);\n \n /**\n  * Same as submodule_from_path but lookup by name.\n@@ -80,7 +81,8 @@ const struct submodule *submodule_from_path(struct repository *r,\n void submodule_free(struct repository *r);\n \n int print_config_from_gitmodules(struct repository *repo, const char *key);\n-int config_set_in_gitmodules_file_gently(const char *key, const char *value);\n+int config_set_in_gitmodules_file_gently(struct repository *repo,\n+\t\t\t\t\t const char *key, const char *value);\n \n /*\n  * Returns 0 if the name is syntactically acceptable as a submodule \"name\"\n@@ -100,8 +102,10 @@ int check_submodule_url(const char *url);\n  * New helpers to retrieve arbitrary configuration from the '.gitmodules' file\n  * should NOT be added.\n  */\n-void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);\n-void update_clone_config_from_gitmodules(int *max_jobs);\n+void fetch_config_from_gitmodules(struct repository *repo,\n+\t\t\t\t  int *max_children,\n+\t\t\t\t  int *recurse_submodules);\n+void update_clone_config_from_gitmodules(struct repository *repo, int *max_jobs);\n \n /*\n  * Submodule entry that contains relevant information about a\ndiff --git a/submodule.c b/submodule.c\nindex 5c92575888..6fcb606f7e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -133,7 +133,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)\n \tstrbuf_addstr(&entry, \"submodule.\");\n \tstrbuf_addstr(&entry, submodule->name);\n \tstrbuf_addstr(&entry, \".path\");\n-\tret = config_set_in_gitmodules_file_gently(entry.buf, newpath);\n+\tret = config_set_in_gitmodules_file_gently(the_repository, entry.buf, newpath);\n \tstrbuf_release(&entry);\n \treturn ret;\n }\ndiff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c\nindex 3c5c4c4a09..ea9bef0904 100644\n--- a/t/helper/test-submodule.c\n+++ b/t/helper/test-submodule.c\n@@ -168,7 +168,7 @@ static int cmd__submodule_config_set(int argc, const char **argv)\n \t\tif (!is_writing_gitmodules_ok())\n \t\t\tdie(\"please make sure that the .gitmodules file is in the working tree\");\n \n-\t\treturn config_set_in_gitmodules_file_gently(argv[1], argv[2]);\n+\t\treturn config_set_in_gitmodules_file_gently(the_repository, argv[1], argv[2]);\n \t}\n \tusage_with_options(usage, options);\n }\n@@ -188,7 +188,7 @@ static int cmd__submodule_config_unset(int argc, const char **argv)\n \tif (argc == 2) {\n \t\tif (!is_writing_gitmodules_ok())\n \t\t\tdie(\"please make sure that the .gitmodules file is in the working tree\");\n-\t\treturn config_set_in_gitmodules_file_gently(argv[1], NULL);\n+\t\treturn config_set_in_gitmodules_file_gently(the_repository, argv[1], NULL);\n \t}\n \tusage_with_options(usage, options);\n }\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551760","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-4-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 04/13] submodule-config: stop using `the_hash_algo`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:52Z","receivedAt":"2026-09-02T13:35:11Z","isPatch":true,"body":"We have two uses of `the_hash_algo` in \"submodule-config.c\":\n\n  - One trivial use in `gitmodules_cb`, which we can convert to use the\n    hash algorithm of the repository that's already available in the\n    caller's context.\n\n  - One use where we compute the hashmap key of an object ID. We should\n    only ever get valid, populated object IDs here, and consequently we\n    can easily adapt that function to use the hash algorithm of the\n    passed-in object ID.\n\nAdapt both sites accordingly. Safeguard us against the case where the\npassed-in object ID is _not_ properly initialized. While this case\nshouldn't ever happen, it doesn't hurt to be defensive.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n submodule-config.c | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f8c2cf7a93..7c73fa108b 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -133,7 +133,9 @@ void submodule_cache_free(struct submodule_cache *cache)\n static unsigned int hash_oid_string(const struct object_id *oid,\n \t\t\t\t    const char *string)\n {\n-\treturn memhash(oid->hash, the_hash_algo->rawsz) + strhash(string);\n+\tif (oid->algo == GIT_HASH_UNKNOWN)\n+\t\tBUG(\"hashing an object ID with unknown algorithm\");\n+\treturn memhash(oid->hash, hash_algos[oid->algo].rawsz) + strhash(string);\n }\n \n static void cache_put_path(struct submodule_cache *cache,\n@@ -824,7 +826,7 @@ static int gitmodules_cb(const char *var, const char *value,\n \n \tparameter.cache = repo->submodule_cache;\n \tparameter.treeish_name = NULL;\n-\tparameter.gitmodules_oid = null_oid(the_hash_algo);\n+\tparameter.gitmodules_oid = null_oid(repo->hash_algo);\n \tparameter.overwrite = 1;\n \n \treturn parse_config(var, value, ctx, &parameter);\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551761","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-5-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 05/13] submodule-config: stop registering submodule sources","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:53Z","receivedAt":"2026-09-02T13:35:13Z","isPatch":true,"body":"When reading the \".gitmodules\" file from a blob in a repository other\nthan `the_repository`, we register the repository's object database as\nan in-memory source of `the_repository`'s object database. This call has\nits origins in d9b8b8f896 (submodule-config.c: use repo_get_oid for\nreading .gitmodules, 2019-04-16): back then, `config_with_options()` was\nnot able to read a blob from an arbitrary repository, but would always\nread it via `the_repository`. So even though the blob could be resolved\nin the submodule repository via `repo_get_oid()`, the submodule's object\ndatabase had to be registered as an in-memory source of `the_repository`\nso that the subsequent object read was able to find the blob at all.\n\nThat need went away with e3e8bf046e (submodule-config: pass repo\nupon blob config read, 2021-08-16), which taught the config machinery\nto read the blob from the repository we pass to it. The same series\nconverted the eager submodule source registration into a lazy mechanism\nthat only registers submodule sources with the object database when an\nobject lookup failed. The intent though was that we don't ever have to\nfall back to this mechanism in the first place, and to verify that this\nis the case we introduced GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB. If set,\nthen any such lazy registration would cause us to BUG.\n\nAt the beginning of this series, we still triggered this bug in t1092.\nBut now that we have converted the \"cache-tree\" subsystem to not depend\non `the_repository` anymore it also knows to properly access objects via\nthe submodule. With that change, GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\ndoes not cause any failures anymore.\n\nRemove the call to `odb_add_submodule_source_by_path()`. This removes\nthe last user of `the_repository`, so at the same time we can also get\nrid of `USE_THE_REPOSITORY_VARIABLE`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n submodule-config.c | 4 ----\n 1 file changed, 4 deletions(-)\n\ndiff --git a/submodule-config.c b/submodule-config.c\nindex 7c73fa108b..37c3be377b 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -1,4 +1,3 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n@@ -803,9 +802,6 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void\n \t\t} else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||\n \t\t\t   repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {\n \t\t\tconfig_source.blob = oidstr = xstrdup(oid_to_hex(&oid));\n-\t\t\tif (repo != the_repository)\n-\t\t\t\todb_add_submodule_source_by_path(the_repository->objects,\n-\t\t\t\t\t\t\t\t repo->objects->sources->path);\n \t\t} else {\n \t\t\tgoto out;\n \t\t}\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551762","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-6-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 06/13] builtin/grep: stop registering submodule ODB as source","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:54Z","receivedAt":"2026-09-02T13:35:15Z","isPatch":true,"body":"Same as with the preceding commit, git-grep(1) registers each\nsubmodule's object database as an in-memory source of the main object\ndatabase before grepping it. This was introduced as an eager alternate\nregistration and converted into the lazy mechanism via 8d33c3af0b (grep:\nuse submodule-ODB-as-alternate lazy-addition, 2021-08-16).\n\nStarting with 0693806bf8 (grep: add repository to OID grep sources,\n2021-08-16), the command instead knows to pass submodule repositories to\nour workers, which means that those now use that repository to look up\nobjects, too. As a consequence, registering submodule sources as\nalternates is not required anymore.\n\nRemove the logic to register submodule sources. Unfortunately, this does\nnot allow us to get rid of the object read lock as initializing the\nsubrepository is still racy.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/grep.c | 26 ++++++--------------------\n 1 file changed, 6 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 073dfaaf45..b045f8a488 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -463,16 +463,6 @@ static int grep_submodule(struct grep_opt *opt,\n \tALLOC_GROW(repos_to_free, repos_to_free_nr + 1, repos_to_free_alloc);\n \trepos_to_free[repos_to_free_nr++] = subrepo;\n \n-\t/*\n-\t * NEEDSWORK: repo_read_gitmodules() might call\n-\t * odb_add_to_alternates_memory() via config_from_gitmodules(). This\n-\t * operation causes a race condition with concurrent object readings\n-\t * performed by the worker threads. That's why we need obj_read_lock()\n-\t * here. It should be removed once it's no longer necessary to add the\n-\t * subrepo's odbs to the in-memory alternates list.\n-\t */\n-\tobj_read_lock();\n-\n \t/*\n \t * NEEDSWORK: when reading a submodule, the sparsity settings in the\n \t * superproject are incorrectly forgotten or misused. For example:\n@@ -498,18 +488,14 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\tditto.\n \t *\n \t * Note that this list is not exhaustive.\n+\t *\n+\t * NEEDSWORK: initializing the subrepository is not thread-safe,\n+\t * either, as it may cause us to race around `get_main_ref_store()`. We\n+\t * thus need to hold the object-read lock to serialize all readers with\n+\t * one another.\n \t */\n+\tobj_read_lock();\n \trepo_read_gitmodules(subrepo, 0);\n-\n-\t/*\n-\t * All code paths tested by test code no longer need submodule ODBs to\n-\t * be added as alternates, but add it to the list just in case.\n-\t * Submodule ODBs added through add_submodule_odb_by_path() will be\n-\t * lazily registered as alternates when needed (and except in an\n-\t * unexpected code interaction, it won't be needed).\n-\t */\n-\todb_add_submodule_source_by_path(the_repository->objects,\n-\t\t\t\t\t subrepo->objects->sources->path);\n \tobj_read_unlock();\n \n \tmemcpy(&subopt, opt, sizeof(subopt));\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551763","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-7-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 07/13] odb: remove infrastructure to register submodule sources","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:55Z","receivedAt":"2026-09-02T13:35:17Z","isPatch":true,"body":"The preceding commits have removed the last two users of\n`odb_add_submodule_source_by_path()`. The mechanism was only ever\nmeant as a transitional crutch while migrating submodule object\naccess away from \"add the submodule ODB as an alternate of\nthe_repository\" towards explicitly passing the submodule repository,\nsee a35e03dee0 (submodule: lazily add submodule ODBs as alternates,\n2021-08-16). Remove it.\n\nAs GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB is now a no-op, remove its\ndocumentation and the exports from the test suite, as well.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n odb.c                                  | 36 ----------------------------------\n odb.h                                  | 14 -------------\n t/README                               |  7 -------\n t/t5526-fetch-submodules.sh            |  3 ---\n t/t5531-deep-submodule-push.sh         |  3 ---\n t/t5545-push-options.sh                |  3 ---\n t/t5572-pull-submodule.sh              |  3 ---\n t/t6437-submodule-merge.sh             |  3 ---\n t/t7418-submodule-sparse-gitmodules.sh |  3 ---\n t/t7814-grep-recurse-submodules.sh     |  3 ---\n 10 files changed, 78 deletions(-)\n\ndiff --git a/odb.c b/odb.c\nindex 6d5943e5ea..2f8a70a90c 100644\n--- a/odb.c\n+++ b/odb.c\n@@ -388,12 +388,6 @@ struct odb_source *odb_find_source_or_die(struct object_database *odb, const cha\n \treturn source;\n }\n \n-void odb_add_submodule_source_by_path(struct object_database *odb,\n-\t\t\t\t      const char *path)\n-{\n-\tstring_list_insert(&odb->submodule_source_paths, path);\n-}\n-\n static void fill_alternate_refs_command(struct repository *repo,\n \t\t\t\t\tstruct child_process *cmd,\n \t\t\t\t\tconst char *repo_path)\n@@ -549,23 +543,6 @@ void disable_obj_read_lock(void)\n \tpthread_mutex_destroy(&obj_read_mutex);\n }\n \n-static int register_all_submodule_sources(struct object_database *odb)\n-{\n-\tint ret = odb->submodule_source_paths.nr;\n-\n-\tfor (size_t i = 0; i < odb->submodule_source_paths.nr; i++)\n-\t\todb_add_to_alternates_memory(odb,\n-\t\t\t\t\t     odb->submodule_source_paths.items[i].string);\n-\tif (ret) {\n-\t\tstring_list_clear(&odb->submodule_source_paths, 0);\n-\t\ttrace2_data_intmax(\"submodule\", odb->repo,\n-\t\t\t\t   \"register_all_submodule_sources/registered\", ret);\n-\t\tif (git_env_bool(\"GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\", 0))\n-\t\t\tBUG(\"register_all_submodule_sources() called\");\n-\t}\n-\treturn ret;\n-}\n-\n static enum odb_read_status do_oid_object_info_extended(struct object_database *odb,\n \t\t\t\t\t\t\tconst struct object_id *oid,\n \t\t\t\t\t\t\tstruct object_info *oi, unsigned flags)\n@@ -614,16 +591,6 @@ static enum odb_read_status do_oid_object_info_extended(struct object_database *\n \t\t\t}\n \t\t}\n \n-\t\t/*\n-\t\t * This might be an attempt at accessing a submodule object as\n-\t\t * if it were in main object store (having called\n-\t\t * `odb_add_submodule_source_by_path()` on that submodule's\n-\t\t * ODB). If any such ODBs exist, register them and try again.\n-\t\t */\n-\t\tif (register_all_submodule_sources(odb))\n-\t\t\t/* We added some alternates; retry */\n-\t\t\tcontinue;\n-\n \t\t/* Check if it is a missing object */\n \t\tif (odb->repo->fetch_if_missing && repo_has_promisor_remote(odb->repo) &&\n \t\t    !already_retried &&\n@@ -1109,7 +1076,6 @@ struct object_database *odb_new(struct repository *repo,\n \tCALLOC_ARRAY(o, 1);\n \to->repo = repo;\n \tpthread_mutex_init(&o->replace_mutex, NULL);\n-\tstring_list_init_dup(&o->submodule_source_paths);\n \thashmap_init(&o->source_by_path, odb_source_by_path_cmp, o, 0);\n \to->source_paths_icase = -1;\n \n@@ -1166,8 +1132,6 @@ void odb_free(struct object_database *o)\n \todb_close(o);\n \todb_free_sources(o);\n \n-\tstring_list_clear(&o->submodule_source_paths, 0);\n-\n \tfree(o);\n }\n \ndiff --git a/odb.h b/odb.h\nindex 248ee9cdfa..54548efc55 100644\n--- a/odb.h\n+++ b/odb.h\n@@ -89,12 +89,6 @@ struct object_database {\n \tunsigned long object_count;\n \tunsigned object_count_flags;\n \tunsigned object_count_valid : 1;\n-\n-\t/*\n-\t * Submodule source paths that will be added as additional sources to\n-\t * allow lookup of submodule objects via the main object database.\n-\t */\n-\tstruct string_list submodule_source_paths;\n };\n \n enum odb_new_flags {\n@@ -224,14 +218,6 @@ void odb_restore_primary_source(struct object_database *odb,\n \t\t\t\tstruct odb_source *restore_source,\n \t\t\t\tconst char *old_path);\n \n-/*\n- * Call odb_add_submodule_source_by_path() to add the submodule at the given\n- * path to a list. The object stores of all submodules in that list will be\n- * added as additional sources in the object store when looking up objects.\n- */\n-void odb_add_submodule_source_by_path(struct object_database *odb,\n-\t\t\t\t      const char *path);\n-\n /*\n  * Iterate through all alternates of the database and execute the provided\n  * callback function for each of them. Stop iterating once the callback\ndiff --git a/t/README b/t/README\nindex 9a9daaf2af..f831c5355b 100644\n--- a/t/README\n+++ b/t/README\n@@ -462,13 +462,6 @@ GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting\n to <n> and 'checkout.thresholdForParallelism' to 0, forcing the\n execution of the parallel-checkout code.\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes\n-registering submodule ODBs as alternates a fatal action. Support for\n-this environment variable can be removed once the migration to\n-explicitly providing repositories when accessing submodule objects is\n-complete or needs to be abandoned for whatever reason (in which case the\n-migrated codepaths still retain their performance benefits).\n-\n GIT_TEST_REQUIRE_PREREQ=<list> allows specifying a space separated list of\n prereqs that are required to succeed. If a prereq in this list is triggered by\n a test and then fails then the whole test run will abort. This can help to make\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 7b3b7359da..37d7373b36 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -3,9 +3,6 @@\n \n test_description='Recursive \"git fetch\" for submodules'\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n . ./test-lib.sh\n \n pwd=$(pwd)\ndiff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh\nindex 7d239dd31f..73429ec6e3 100755\n--- a/t/t5531-deep-submodule-push.sh\n+++ b/t/t5531-deep-submodule-push.sh\n@@ -5,9 +5,6 @@ test_description='test push with submodules'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh\nindex fb13549da7..239edd7d62 100755\n--- a/t/t5545-push-options.sh\n+++ b/t/t5545-push-options.sh\n@@ -5,9 +5,6 @@ test_description='pushing to a repository using push options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n . ./test-lib.sh\n \n mk_repo_pair () {\ndiff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh\nindex 42d14328b6..9969a3294e 100755\n--- a/t/t5572-pull-submodule.sh\n+++ b/t/t5572-pull-submodule.sh\n@@ -2,9 +2,6 @@\n \n test_description='pull can handle submodules'\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-submodule-update.sh\n \ndiff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh\nindex 107e13afbc..1546d5f773 100755\n--- a/t/t6437-submodule-merge.sh\n+++ b/t/t6437-submodule-merge.sh\n@@ -5,9 +5,6 @@ test_description='merging with submodules'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n . ./test-lib.sh\n \n #\ndiff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh\nindex dde11ecce8..cf94e30e78 100755\n--- a/t/t7418-submodule-sparse-gitmodules.sh\n+++ b/t/t7418-submodule-sparse-gitmodules.sh\n@@ -12,9 +12,6 @@ The test setup uses a sparse checkout, however the same scenario can be set up\n also by committing .gitmodules and then just removing it from the filesystem.\n '\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh\nindex e1cf53dc9e..3d149d34c1 100755\n--- a/t/t7814-grep-recurse-submodules.sh\n+++ b/t/t7814-grep-recurse-submodules.sh\n@@ -9,9 +9,6 @@ submodules.\n TEST_CREATE_REPO_NO_TEMPLATE=1\n . ./test-lib.sh\n \n-GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1\n-export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n-\n test_expect_success 'setup directory structure and submodule' '\n \techo \"(1|2)d(3|4)\" >a &&\n \tmkdir b &&\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551764","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-8-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 08/13] tmp-objdir: drop unused function to register alternate","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:56Z","receivedAt":"2026-09-02T13:35:20Z","isPatch":true,"body":"The last caller of `tmp_objdir_add_as_alternate()` went away in\nbdee7b3013 (builtin/receive-pack: stage incoming objects via ODB\ntransactions, 2026-07-10) and is unused now. Remove the function.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n tmp-objdir.c | 5 -----\n tmp-objdir.h | 6 ------\n 2 files changed, 11 deletions(-)\n\ndiff --git a/tmp-objdir.c b/tmp-objdir.c\nindex 0eaa79ffd7..deaaf6ba2e 100644\n--- a/tmp-objdir.c\n+++ b/tmp-objdir.c\n@@ -321,11 +321,6 @@ const char **tmp_objdir_env(const struct tmp_objdir *t)\n \treturn t->env.v;\n }\n \n-void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)\n-{\n-\todb_add_to_alternates_memory(t->repo->objects, t->path.buf);\n-}\n-\n struct odb_source *tmp_objdir_replace_primary_odb(struct tmp_objdir *t,\n \t\t\t\t\t\t  int will_destroy)\n {\ndiff --git a/tmp-objdir.h b/tmp-objdir.h\nindex 81eb927413..05f0d08d10 100644\n--- a/tmp-objdir.h\n+++ b/tmp-objdir.h\n@@ -55,12 +55,6 @@ int tmp_objdir_destroy(struct tmp_objdir *);\n  */\n void tmp_objdir_discard_objects(struct tmp_objdir *);\n \n-/*\n- * Add the temporary object directory as an alternate object store in the\n- * current process.\n- */\n-void tmp_objdir_add_as_alternate(const struct tmp_objdir *);\n-\n /*\n  * Replaces the writable object store in the current process with the temporary\n  * object directory and makes the former main object store an alternate.\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551765","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-9-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 09/13] odb/packed: fix memory leaks when freeing source","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:57Z","receivedAt":"2026-09-02T13:35:22Z","isPatch":true,"body":"When freeing a \"packed\" source we don't close either its packs nor its\nmulti-pack indices. This can cause memory leaks in case we create an\nad-hoc packed source. As we used to always link packed sources to the\nmain object database we never noticed this issue until now, but it's\ngoing to surface in subsequent commits where we stop linking them.\n\nPlug the memory leaks by closing the source first.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n odb/source-packed.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/odb/source-packed.c b/odb/source-packed.c\nindex 1d90e714e6..166e76e2d6 100644\n--- a/odb/source-packed.c\n+++ b/odb/source-packed.c\n@@ -844,6 +844,7 @@ static void odb_source_packed_free(struct odb_source *source)\n \n \tchdir_notify_unregister(odb_source_packed_reparent, packed);\n \n+\todb_source_close(source);\n \tfor (struct packfile_list_entry *e = packed->packs.head; e; e = e->next)\n \t\tfree(e->pack);\n \tpackfile_list_clear(&packed->packs);\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551766","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-10-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 10/13] builtin/multi-pack-index: refuse unknown sources with \"--object-dir=\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:58Z","receivedAt":"2026-09-02T13:35:25Z","isPatch":true,"body":"Users can tell git-multi-pack-index(1) to access multi-pack indices that\nare stored in a different object directory via the \"--object-dir=\"\noption. This allows them to for example write or verify a multi-pack\nindex other than the one located in the main object directory in case a\nrepository has alternates with multiple multi-pack indices.\n\nBut while the documentation explicitly points out that the specified\nobject directory must be an alternate of the current repository, we\nnever verify that property. Instead, starting with 017db7bb14 (midx:\nload multi-pack indices via their source, 2025-08-11), we now construct\nan ad-hoc source and link it to the main object directory.\n\nBesides contradicting the documentation, it's dubious that this really\nought to work in the first place: creating a multi-pack index (and\npotentially a bitmap) for a completely foreign object directory is of\nquestionable value, as bitmap commit selection operates on the invoking\nrepository's refs. Furthermore, this is the only remaining caller\noutside of our test helpers that constructs an ad-hoc source and links\nit to the database, and we want to get rid of this mechanism as part of\nthis series.\n\nStop constructing the ad-hoc source and instead refuse the operation.\nWhile this results in a change in behaviour, this restriction has been\ndocumented as such ever since f57a739691 (midx: avoid opening multiple\nMIDXs when writing, 2021-09-01).\n\nNote that this change requires us to adapt one test chain in t5319, as\nit creates an object directory that is not connected to any repository\nand then uses it via \"--object-dir=\". The setup itself already documents\nthis and does the necessary gymnastics to link the object directory to a\ntemporary repository, but subsequent tests don't. Adapt those tests to\nretain and reuse the temporary repository.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/multi-pack-index.c  | 3 ++-\n t/t5319-multi-pack-index.sh | 9 ++++-----\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c\nindex 6e73c85cde..753bd53a70 100644\n--- a/builtin/multi-pack-index.c\n+++ b/builtin/multi-pack-index.c\n@@ -90,7 +90,8 @@ static struct odb_source_files *handle_object_dir_option(struct repository *repo\n {\n \tstruct odb_source *source = odb_find_source(repo->objects, opts.object_dir);\n \tif (!source)\n-\t\tsource = odb_add_to_alternates_memory(repo->objects, opts.object_dir);\n+\t\tdie(_(\"object directory is not an alternate of the current repository: '%s'\"),\n+\t\t    opts.object_dir);\n \treturn odb_source_files_downcast(source);\n }\n \ndiff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh\nindex 68143cb5b7..00e90f163f 100755\n--- a/t/t5319-multi-pack-index.sh\n+++ b/t/t5319-multi-pack-index.sh\n@@ -698,10 +698,9 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '\n \tcorrupt_data $idx64 $(test_oid idxoff) \"\\02\" &&\n \t# objects64 is not a real repository, but can serve as an alternate\n \t# anyway so we can write a MIDX into it\n-\tgit init repo &&\n-\ttest_when_finished \"rm -fr repo\" &&\n+\tgit init repo64 &&\n \t(\n-\t\tcd repo &&\n+\t\tcd repo64 &&\n \t\t( cd ../objects64 && pwd ) >.git/objects/info/alternates &&\n \t\tmidx64=$(git multi-pack-index --object-dir=../objects64 write)\n \t) &&\n@@ -709,7 +708,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '\n '\n \n test_expect_success 'verify multi-pack-index with 64-bit offsets' '\n-\tgit multi-pack-index verify --object-dir=objects64\n+\tgit -C repo64 multi-pack-index verify --object-dir=../objects64\n '\n \n NUM_OBJECTS=63\n@@ -721,7 +720,7 @@ MIDX_BYTE_LARGE_OFFSET=$(($MIDX_OFFSET_LARGE_OFFSETS + 3))\n \n test_expect_success 'verify incorrect 64-bit offset' '\n \tcorrupt_midx_and_verify $MIDX_BYTE_LARGE_OFFSET \"\\07\" objects64 \\\n-\t\t\"incorrect object offset\"\n+\t\t\"incorrect object offset\" \"git -C repo64 multi-pack-index verify --object-dir=../objects64\"\n '\n \n test_expect_success 'setup expire tests' '\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551767","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-11-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 11/13] t/helper: adapt read-midx to not link ad-hoc source anymore","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:34:59Z","receivedAt":"2026-09-02T13:35:27Z","isPatch":true,"body":"Same as in the preceding commit, refactor the setup of ad-hoc object\ndatabase sources when accessing a multi-pack index in an arbitrary\nlocation to not link the newly created source into the main object\ndatabase anymore.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/helper/test-read-midx.c | 43 +++++++++++++++++++++++++++++++------------\n 1 file changed, 31 insertions(+), 12 deletions(-)\n\ndiff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c\nindex 27a05da957..1f7a1927e4 100644\n--- a/t/helper/test-read-midx.c\n+++ b/t/helper/test-read-midx.c\n@@ -5,34 +5,42 @@\n #include \"midx.h\"\n #include \"repository.h\"\n #include \"odb.h\"\n+#include \"odb/source-packed.h\"\n #include \"pack-bitmap.h\"\n #include \"packfile.h\"\n #include \"setup.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n \n-static struct multi_pack_index *setup_midx(const char *object_dir)\n+static struct multi_pack_index *setup_midx(const char *object_dir,\n+\t\t\t\t\t   struct odb_source_packed **out)\n {\n-\tstruct odb_source_files *files;\n+\tstruct odb_source_packed *packed;\n \tstruct odb_source *source;\n+\n \tsetup_git_directory(the_repository);\n+\n \tsource = odb_find_source(the_repository->objects, object_dir);\n-\tif (!source)\n-\t\tsource = odb_add_to_alternates_memory(the_repository->objects,\n-\t\t\t\t\t\t      object_dir);\n-\tfiles = odb_source_files_downcast(source);\n+\tif (source) {\n+\t\tpacked = odb_source_files_downcast(source)->packed;\n+\t} else {\n+\t\tpacked = odb_source_packed_new(the_repository->objects,\n+\t\t\t\t\t       object_dir, false);\n+\t\t*out = packed;\n+\t}\n \n-\treturn load_multi_pack_index(files->packed);\n+\treturn load_multi_pack_index(packed);\n }\n \n static int read_midx_file(const char *object_dir, const char *checksum,\n \t\t\t  int show_objects)\n {\n+\tstruct odb_source_packed *packed = NULL;\n \tuint32_t i;\n \tstruct multi_pack_index *m, *tip;\n \tint ret = 0;\n \n-\tm = tip = setup_midx(object_dir);\n+\tm = tip = setup_midx(object_dir, &packed);\n \n \tif (!m)\n \t\treturn 1;\n@@ -91,29 +99,35 @@ static int read_midx_file(const char *object_dir, const char *checksum,\n \n out:\n \tclose_midx(tip);\n+\tif (packed)\n+\t\todb_source_free(&packed->base);\n \n \treturn ret;\n }\n \n static int read_midx_checksum(const char *object_dir)\n {\n+\tstruct odb_source_packed *packed = NULL;\n \tstruct multi_pack_index *m;\n \n-\tm = setup_midx(object_dir);\n+\tm = setup_midx(object_dir, &packed);\n \tif (!m)\n \t\treturn 1;\n \tprintf(\"%s\\n\", midx_get_checksum_hex(m));\n \n \tclose_midx(m);\n+\tif (packed)\n+\t\todb_source_free(&packed->base);\n \treturn 0;\n }\n \n static int read_midx_preferred_pack(const char *object_dir)\n {\n+\tstruct odb_source_packed *packed = NULL;\n \tstruct multi_pack_index *midx = NULL;\n \tuint32_t preferred_pack;\n \n-\tmidx = setup_midx(object_dir);\n+\tmidx = setup_midx(object_dir, &packed);\n \tif (!midx)\n \t\treturn 1;\n \n@@ -124,17 +138,21 @@ static int read_midx_preferred_pack(const char *object_dir)\n \t}\n \n \tprintf(\"%s\\n\", midx->pack_names[preferred_pack]);\n+\n \tclose_midx(midx);\n+\tif (packed)\n+\t\todb_source_free(&packed->base);\n \treturn 0;\n }\n \n static int read_midx_bitmapped_packs(const char *object_dir)\n {\n+\tstruct odb_source_packed *packed = NULL;\n \tstruct multi_pack_index *midx = NULL;\n \tstruct bitmapped_pack pack;\n \tuint32_t i;\n \n-\tmidx = setup_midx(object_dir);\n+\tmidx = setup_midx(object_dir, &packed);\n \tif (!midx)\n \t\treturn 1;\n \n@@ -150,7 +168,8 @@ static int read_midx_bitmapped_packs(const char *object_dir)\n \t}\n \n \tclose_midx(midx);\n-\n+\tif (packed)\n+\t\todb_source_free(&packed->base);\n \treturn 0;\n }\n \n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551768","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-12-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 12/13] t/helper: stop registering alternates in \"ref-store\" command","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:35:00Z","receivedAt":"2026-09-02T13:35:29Z","isPatch":true,"body":"When using the \"ref-store\" command we support access to multiple\ndifferent reference stores. As part of that we allow the caller to\nexplicitly exercise stores of a submodule. This allows us to verify\nlow-level behaviour of submodule stores, which is exercised in t1406.\n\nWhen doing so we also link the submodule's object database into the main\nobject database. The intent of this is that it allows us to access\nobjects of the submodule, too. But that functionality is not even\nneeded anymore: when creating a submodule reference store, we will first\ninitialize the submodule repository and then initialize the store with\nthat repository. And as the reference subsystem doesn't depend on\n`the_repository` anymore all subsequent object lookups performed by the\nreference store will be routed to the submodule repository.\n\nIt is thus not needed anymore to register the submodule object store\nwith the main object database. Remove the call.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/helper/test-ref-store.c | 8 --------\n 1 file changed, 8 deletions(-)\n\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 5a9a3053d9..db58f00589 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -74,14 +74,6 @@ static const char **get_store(const char **argv, struct ref_store **refs)\n \t} else if (!strcmp(argv[0], \"main\")) {\n \t\t*refs = get_main_ref_store(the_repository);\n \t} else if (skip_prefix(argv[0], \"submodule:\", &gitdir)) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (!repo_submodule_path_append(the_repository,\n-\t\t\t\t\t\t&sb, gitdir, \"objects/\"))\n-\t\t\tdie(\"computing submodule path failed\");\n-\t\todb_add_to_alternates_memory(the_repository->objects, sb.buf);\n-\t\tstrbuf_release(&sb);\n-\n \t\t*refs = repo_get_submodule_ref_store(the_repository, gitdir);\n \t} else if (skip_prefix(argv[0], \"worktree:\", &gitdir)) {\n \t\tstruct worktree **p, **worktrees = get_worktrees(the_repository);\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"551769","messageId":"20260902-pks-odb-registering-in-memory-sources-v2-13-c6ca12fdea4d@pks.im","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"[PATCH v2 13/13] odb: remove the ability to link sources ad-hoc","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-02T13:35:01Z","receivedAt":"2026-09-02T13:35:32Z","isPatch":true,"body":"Over the course of this patch series we have adapted all callers of\n`odb_add_to_alternates_memory()` to not do so anymore. Remove the\nfunction.\n\nThis series of refactorings doesn't only simplify our code base. More\nimportantly, with those changes in place we can now unconditionally\nassume that the list of sources linked to the object database only\nconsists of the primary source and its alternates. This serves as the\nfoundation to eventually move handling of alternates into the \"files\"\nbackend itself.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n odb.c | 6 ------\n odb.h | 8 --------\n 2 files changed, 14 deletions(-)\n\ndiff --git a/odb.c b/odb.c\nindex 2f8a70a90c..5fe081496f 100644\n--- a/odb.c\n+++ b/odb.c\n@@ -247,12 +247,6 @@ void odb_add_to_alternates_file(struct object_database *odb,\n \todb_add_alternate_recursively(odb, dir, 0);\n }\n \n-struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,\n-\t\t\t\t\t\tconst char *dir)\n-{\n-\treturn odb_add_alternate_recursively(odb, dir, 0);\n-}\n-\n struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,\n \t\t\t\t\t\t    const char *dir, int will_destroy,\n \t\t\t\t\t\t    struct odb_source **prev_source)\ndiff --git a/odb.h b/odb.h\nindex 54548efc55..9025239df5 100644\n--- a/odb.h\n+++ b/odb.h\n@@ -258,14 +258,6 @@ int odb_has_alternates(struct object_database *odb);\n void odb_add_to_alternates_file(struct object_database *odb,\n \t\t\t\tconst char *dir);\n \n-/*\n- * Add the directory to the in-memory list of alternate sources (along with any\n- * recursive alternates it points to), but do not modify the on-disk alternates\n- * file.\n- */\n-struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,\n-\t\t\t\t\t\tconst char *dir);\n-\n /*\n  * Read an object from the database. Returns the object data and assigns object\n  * type and size to the `type` and `size` pointers, if these pointers are\n\n-- \n2.55.0.979.g7e5102b832.dirty\n\n"},{"id":"552011","messageId":"CAOLa=ZQ=oCDtjAQXNXe51DvKUCCk0CK1EuM+QKhJ3iH8YiS+mw@mail.gmail.com","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-2-c6ca12fdea4d@pks.im","subject":"Re: [PATCH v2 02/13] cache-tree: remove dependency on `the_repository`","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2026-09-04T22:28:07Z","receivedAt":"2026-09-04T22:28:08Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The \"cache-tree\" subsystem still depends on `the_repository`. Adapt it\n> to instead use repositories provided via the context, either as a new\n> parameter or the one passed in via `struct index_state`.\n>\n> Besides getting rid of `the_repository`, this also removes the last\n> dependency on registering submodule sources with the main object\n> database. When reading gitmodules from a submodule's index we implicitly\n> read that object via `the_repository`'s object database, which is of\n> course wrong. This works though because we would then register the\n> submodule's object database with the main object database, but a later\n> patch is going to get rid of that mechanism.\n>\n> You can verify that we indeed no longer depend on this mechanism by\n> running tests with `GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=true`. Without\n> this patch we fail in t1092, with this patch we never register submodule\n> object databases anymore.\n>\n\nInteresting, to sum up if I understand correctly, somewhere in the call\nchain of looking up the gitmodules from the submodules index, we end up\nusing 'the_repository' instead of the submodule's repo structure.\n\nThis is of course wrong, because we use the wrong repo, the consequence\nis that the lookup fails, but we have a last ditch effort of adding all\nsubmodules as alternates and retrying the object read, this succeeds.\n\nSo this patch fixes the repository being correctly passed down. Meaning\nwe no longer need to add the submodules as an alternate.\n\n[snip]\n\nThe changes look good.\n"},{"id":"552012","messageId":"CAOLa=ZSW=kZ4BQ9updQ9jvTs=g1oWe_24LpAaSW082iQkQcFrA@mail.gmail.com","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-7-c6ca12fdea4d@pks.im","subject":"Re: [PATCH v2 07/13] odb: remove infrastructure to register submodule sources","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2026-09-04T22:36:57Z","receivedAt":"2026-09-04T22:37:00Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The preceding commits have removed the last two users of\n> `odb_add_submodule_source_by_path()`. The mechanism was only ever\n> meant as a transitional crutch while migrating submodule object\n> access away from \"add the submodule ODB as an alternate of\n> the_repository\" towards explicitly passing the submodule repository,\n> see a35e03dee0 (submodule: lazily add submodule ODBs as alternates,\n> 2021-08-16). Remove it.\n>\n> As GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB is now a no-op, remove its\n> documentation and the exports from the test suite, as well.\n>\n\nOkay so the last two commits removed the last two sources which\nregistered the submodule as an in-memory odb.\n\nThat leaves us to cleanup all the code around it.\n\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  odb.c                                  | 36 ----------------------------------\n>  odb.h                                  | 14 -------------\n>  t/README                               |  7 -------\n>  t/t5526-fetch-submodules.sh            |  3 ---\n>  t/t5531-deep-submodule-push.sh         |  3 ---\n>  t/t5545-push-options.sh                |  3 ---\n>  t/t5572-pull-submodule.sh              |  3 ---\n>  t/t6437-submodule-merge.sh             |  3 ---\n>  t/t7418-submodule-sparse-gitmodules.sh |  3 ---\n>  t/t7814-grep-recurse-submodules.sh     |  3 ---\n>  10 files changed, 78 deletions(-)\n>\n> diff --git a/odb.c b/odb.c\n> index 6d5943e5ea..2f8a70a90c 100644\n> --- a/odb.c\n> +++ b/odb.c\n> @@ -388,12 +388,6 @@ struct odb_source *odb_find_source_or_die(struct object_database *odb, const cha\n>  \treturn source;\n>  }\n>\n> -void odb_add_submodule_source_by_path(struct object_database *odb,\n> -\t\t\t\t      const char *path)\n> -{\n> -\tstring_list_insert(&odb->submodule_source_paths, path);\n> -}\n> -\n>  static void fill_alternate_refs_command(struct repository *repo,\n>  \t\t\t\t\tstruct child_process *cmd,\n>  \t\t\t\t\tconst char *repo_path)\n> @@ -549,23 +543,6 @@ void disable_obj_read_lock(void)\n>  \tpthread_mutex_destroy(&obj_read_mutex);\n>  }\n>\n> -static int register_all_submodule_sources(struct object_database *odb)\n> -{\n> -\tint ret = odb->submodule_source_paths.nr;\n> -\n> -\tfor (size_t i = 0; i < odb->submodule_source_paths.nr; i++)\n> -\t\todb_add_to_alternates_memory(odb,\n> -\t\t\t\t\t     odb->submodule_source_paths.items[i].string);\n> -\tif (ret) {\n> -\t\tstring_list_clear(&odb->submodule_source_paths, 0);\n> -\t\ttrace2_data_intmax(\"submodule\", odb->repo,\n> -\t\t\t\t   \"register_all_submodule_sources/registered\", ret);\n> -\t\tif (git_env_bool(\"GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\", 0))\n> -\t\t\tBUG(\"register_all_submodule_sources() called\");\n> -\t}\n> -\treturn ret;\n> -}\n> -\n\nNice, so this remove the code to add the submodules as alternates.\n\n>  static enum odb_read_status do_oid_object_info_extended(struct object_database *odb,\n>  \t\t\t\t\t\t\tconst struct object_id *oid,\n>  \t\t\t\t\t\t\tstruct object_info *oi, unsigned flags)\n> @@ -614,16 +591,6 @@ static enum odb_read_status do_oid_object_info_extended(struct object_database *\n>  \t\t\t}\n>  \t\t}\n>\n> -\t\t/*\n> -\t\t * This might be an attempt at accessing a submodule object as\n> -\t\t * if it were in main object store (having called\n> -\t\t * `odb_add_submodule_source_by_path()` on that submodule's\n> -\t\t * ODB). If any such ODBs exist, register them and try again.\n> -\t\t */\n> -\t\tif (register_all_submodule_sources(odb))\n> -\t\t\t/* We added some alternates; retry */\n> -\t\t\tcontinue;\n> -\n\nRight so this is the retry mechanism when readin an object fails.\n\n[snip]\n\nThe rest look in order.\n"},{"id":"552013","messageId":"CAOLa=ZQVRJwRqapa8p+fdW2VL652vh5=TJ_ToNGXXM1z=aiXBg@mail.gmail.com","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-0-c6ca12fdea4d@pks.im","subject":"Re: [PATCH v2 00/13] odb: stop registering in-memory sources","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2026-09-04T22:45:56Z","receivedAt":"2026-09-04T22:45:59Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Hi,\n>\n> the object database has a list of sources that is used for two\n> different purposes:\n>\n>   - We use it to track the list of alternates.\n>\n>   - We use it to track temporary in-memory sources that we create for\n>     various purposes. Most importantly, this is used to link object\n>     database sources from submodules into the main store.\n>\n> This dual-use is quite awkward, as it mixes two different levels of\n> concerns and thus as a consequence makes both harder to reason about.\n> It's also a source of bugs: we make assumptions about the ordering of\n> sources all over the place, and we furthermore assume in other places\n> that the sources only contain alternates in the first place. I don't\n> think this surfaces in the form of real bugs, but I've long disliked\n> this dual-use.\n>\n> Furthermore, we want to migrate handling of alternates into the \"files\"\n> backend itself in a subsequent patch series. This is most importantly to\n> fix a performance regression by making the backend own all of its\n> alternates, but it also fixes a couple of longer-standing design issues\n> that I've been struggling with [1].\n>\n> Most importantly though: this whole machinery is not even needed at all.\n> A couple years ago we have already refactored our codebase so that\n> submodule sources don't even have to be linked into the main object\n> database anymore. And all the other use cases where we link sources into\n> the main object database can be trivially converted, too.\n>\n> So this patch series does exactly that: it removes the mechanism to link\n> ad-hoc sources into the object database entirely. This ensures that the\n> list of sources is exactly the list of alternates, and that makes it\n> easier to move them into the \"files\" backend in a subsequent patch\n> series.\n>\n> There is one exception though: creating transactions still creates a\n> temporary quarantine directory. This mechanism is left as-is for now,\n> but as it's an implementation detail of the \"files\" backend anyway\n> that's not conflicting with our above stated goals.\n>\n> This series is built on top of 1630431f32 (The 21st batch, 2026-08-31)\n> with ty/repository-fetch-if-missing at 508ec9837c (repository: move\n> fetch_if_missing into struct repository, 2026-08-15) merged into it.\n> There's still two merge conflicts, but these are trivial to resolve: in\n> \"odb.c\" and \"odb.h\" you simply remove both ours and theirs, and in\n> \"builtin/multi-pack-index.c\" you only need to munge the parameters a\n> bit.\n>\n\nThis was a bit of a dense read for me, mostly as I'm not too familiar\nwith the code here. So I can not so confidently say that the series\nlooks good :)\n\n> Changes in v2:\n>   - Adapt `cache_tree_fully_valid()` to take a `struct index_state` as\n>     input instead of taking both a repository and a cache tree, as\n>     suggested by Junio.\n>   - Link to v1: https://patch.msgid.link/20260901-pks-odb-registering-in-memory-sources-v1-0-97a312d5fa25@pks.im\n>\n> Thanks!\n>\n> Patrick\n\n[snip]\n"},{"id":"552084","messageId":"ap2wj0vK0-VUvaW5@denethor","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-5-c6ca12fdea4d@pks.im","subject":"Re: [PATCH v2 05/13] submodule-config: stop registering submodule sources","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-09-06T18:38:15Z","receivedAt":"2026-09-06T18:38:20Z","isPatch":true,"body":"On 26/09/02 03:34PM, Patrick Steinhardt wrote:\n> When reading the \".gitmodules\" file from a blob in a repository other\n> than `the_repository`, we register the repository's object database as\n> an in-memory source of `the_repository`'s object database. This call has\n> its origins in d9b8b8f896 (submodule-config.c: use repo_get_oid for\n> reading .gitmodules, 2019-04-16): back then, `config_with_options()` was\n> not able to read a blob from an arbitrary repository, but would always\n> read it via `the_repository`. So even though the blob could be resolved\n> in the submodule repository via `repo_get_oid()`, the submodule's object\n> database had to be registered as an in-memory source of `the_repository`\n> so that the subsequent object read was able to find the blob at all.\n\nOk, so IIUC the problem was that a gitmodule blob from a repository that\nis not \"the_repository\" would fail to be read by `config_with_options()`\nbecause it would only read objects from \"the_repository\". The workaround\nwas to add the other repositories as another source to ensure the\ngitmodule blob could be read. I had to reread the commit message a\ncouple of times to follow, but I think it makes sense now.\n\n> That need went away with e3e8bf046e (submodule-config: pass repo\n> upon blob config read, 2021-08-16), which taught the config machinery\n> to read the blob from the repository we pass to it. The same series\n> converted the eager submodule source registration into a lazy mechanism\n> that only registers submodule sources with the object database when an\n> object lookup failed. The intent though was that we don't ever have to\n> fall back to this mechanism in the first place, and to verify that this\n> is the case we introduced GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB. If set,\n> then any such lazy registration would cause us to BUG.\n> \n> At the beginning of this series, we still triggered this bug in t1092.\n> But now that we have converted the \"cache-tree\" subsystem to not depend\n> on `the_repository` anymore it also knows to properly access objects via\n> the submodule. With that change, GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB\n> does not cause any failures anymore.\n\nOk, now we can properly read objects from their respective repository\nsource and no longer neeed the workaround.\n\n> Remove the call to `odb_add_submodule_source_by_path()`. This removes\n> the last user of `the_repository`, so at the same time we can also get\n> rid of `USE_THE_REPOSITORY_VARIABLE`.\n> \n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  submodule-config.c | 4 ----\n>  1 file changed, 4 deletions(-)\n> \n> diff --git a/submodule-config.c b/submodule-config.c\n> index 7c73fa108b..37c3be377b 100644\n> --- a/submodule-config.c\n> +++ b/submodule-config.c\n> @@ -1,4 +1,3 @@\n> -#define USE_THE_REPOSITORY_VARIABLE\n>  #define DISABLE_SIGN_COMPARE_WARNINGS\n>  \n>  #include \"git-compat-util.h\"\n> @@ -803,9 +802,6 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void\n>  \t\t} else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||\n>  \t\t\t   repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {\n>  \t\t\tconfig_source.blob = oidstr = xstrdup(oid_to_hex(&oid));\n> -\t\t\tif (repo != the_repository)\n> -\t\t\t\todb_add_submodule_source_by_path(the_repository->objects,\n> -\t\t\t\t\t\t\t\t repo->objects->sources->path);\n\nNice to have one less workaround. :)\n\n-Justin\n"},{"id":"552098","messageId":"ap5spPyWgga9b315@pks.im","threadId":"66255","inReplyTo":"CAOLa=ZQ=oCDtjAQXNXe51DvKUCCk0CK1EuM+QKhJ3iH8YiS+mw@mail.gmail.com","subject":"Re: [PATCH v2 02/13] cache-tree: remove dependency on `the_repository`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-07T07:49:56Z","receivedAt":"2026-09-07T07:50:01Z","isPatch":true,"body":"On Fri, Sep 04, 2026 at 03:28:07PM -0700, Karthik Nayak wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > The \"cache-tree\" subsystem still depends on `the_repository`. Adapt it\n> > to instead use repositories provided via the context, either as a new\n> > parameter or the one passed in via `struct index_state`.\n> >\n> > Besides getting rid of `the_repository`, this also removes the last\n> > dependency on registering submodule sources with the main object\n> > database. When reading gitmodules from a submodule's index we implicitly\n> > read that object via `the_repository`'s object database, which is of\n> > course wrong. This works though because we would then register the\n> > submodule's object database with the main object database, but a later\n> > patch is going to get rid of that mechanism.\n> >\n> > You can verify that we indeed no longer depend on this mechanism by\n> > running tests with `GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=true`. Without\n> > this patch we fail in t1092, with this patch we never register submodule\n> > object databases anymore.\n> >\n> \n> Interesting, to sum up if I understand correctly, somewhere in the call\n> chain of looking up the gitmodules from the submodules index, we end up\n> using 'the_repository' instead of the submodule's repo structure.\n> \n> This is of course wrong, because we use the wrong repo, the consequence\n> is that the lookup fails, but we have a last ditch effort of adding all\n> submodules as alternates and retrying the object read, this succeeds.\n> \n> So this patch fixes the repository being correctly passed down. Meaning\n> we no longer need to add the submodules as an alternate.\n\nYup, exactly this.\n\nPatrick\n"},{"id":"552099","messageId":"ap5sqR6RBamJjEeP@pks.im","threadId":"66255","inReplyTo":"ap2wj0vK0-VUvaW5@denethor","subject":"Re: [PATCH v2 05/13] submodule-config: stop registering submodule sources","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-07T07:50:01Z","receivedAt":"2026-09-07T07:50:05Z","isPatch":true,"body":"On Sun, Sep 06, 2026 at 01:38:15PM -0500, Justin Tobler wrote:\n> On 26/09/02 03:34PM, Patrick Steinhardt wrote:\n> > When reading the \".gitmodules\" file from a blob in a repository other\n> > than `the_repository`, we register the repository's object database as\n> > an in-memory source of `the_repository`'s object database. This call has\n> > its origins in d9b8b8f896 (submodule-config.c: use repo_get_oid for\n> > reading .gitmodules, 2019-04-16): back then, `config_with_options()` was\n> > not able to read a blob from an arbitrary repository, but would always\n> > read it via `the_repository`. So even though the blob could be resolved\n> > in the submodule repository via `repo_get_oid()`, the submodule's object\n> > database had to be registered as an in-memory source of `the_repository`\n> > so that the subsequent object read was able to find the blob at all.\n> \n> Ok, so IIUC the problem was that a gitmodule blob from a repository that\n> is not \"the_repository\" would fail to be read by `config_with_options()`\n> because it would only read objects from \"the_repository\". The workaround\n> was to add the other repositories as another source to ensure the\n> gitmodule blob could be read. I had to reread the commit message a\n> couple of times to follow, but I think it makes sense now.\n\nYes. Do you think I should rewrite the commit message to make it a bit\nless dense? Otherwise I'll leave it as-is for now.\n\nPatrick\n"},{"id":"552273","messageId":"aqCTAXB3AbGW8Dt8@denethor","threadId":"66255","inReplyTo":"ap5sqR6RBamJjEeP@pks.im","subject":"Re: [PATCH v2 05/13] submodule-config: stop registering submodule sources","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-09-08T23:04:54Z","receivedAt":"2026-09-08T23:05:02Z","isPatch":true,"body":"On 26/09/07 09:50AM, Patrick Steinhardt wrote:\n> On Sun, Sep 06, 2026 at 01:38:15PM -0500, Justin Tobler wrote:\n> > On 26/09/02 03:34PM, Patrick Steinhardt wrote:\n> > > When reading the \".gitmodules\" file from a blob in a repository other\n> > > than `the_repository`, we register the repository's object database as\n> > > an in-memory source of `the_repository`'s object database. This call has\n> > > its origins in d9b8b8f896 (submodule-config.c: use repo_get_oid for\n> > > reading .gitmodules, 2019-04-16): back then, `config_with_options()` was\n> > > not able to read a blob from an arbitrary repository, but would always\n> > > read it via `the_repository`. So even though the blob could be resolved\n> > > in the submodule repository via `repo_get_oid()`, the submodule's object\n> > > database had to be registered as an in-memory source of `the_repository`\n> > > so that the subsequent object read was able to find the blob at all.\n> > \n> > Ok, so IIUC the problem was that a gitmodule blob from a repository that\n> > is not \"the_repository\" would fail to be read by `config_with_options()`\n> > because it would only read objects from \"the_repository\". The workaround\n> > was to add the other repositories as another source to ensure the\n> > gitmodule blob could be read. I had to reread the commit message a\n> > couple of times to follow, but I think it makes sense now.\n> \n> Yes. Do you think I should rewrite the commit message to make it a bit\n> less dense? Otherwise I'll leave it as-is for now.\n\nIt is a little dense. I think it is a bit confusing which \"repository\"\nwe are talking about in the first paragraph. I think:\n\n  ...we register _the_ repository's object database... \n\nis a bit ambiguous since \"the repository\" is actually a repository that\nis _not_ `the_repository`. At least for me, I was getting a bit tripped\nup. XD\n\n-Justin\n"},{"id":"552274","messageId":"aqCU7e0c84ZgZNab@denethor","threadId":"66255","inReplyTo":"20260902-pks-odb-registering-in-memory-sources-v2-10-c6ca12fdea4d@pks.im","subject":"Re: [PATCH v2 10/13] builtin/multi-pack-index: refuse unknown sources with \"--object-dir=\"","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-09-08T23:12:17Z","receivedAt":"2026-09-08T23:12:20Z","isPatch":true,"body":"On 26/09/02 03:34PM, Patrick Steinhardt wrote:\n> Users can tell git-multi-pack-index(1) to access multi-pack indices that\n> are stored in a different object directory via the \"--object-dir=\"\n> option. This allows them to for example write or verify a multi-pack\n> index other than the one located in the main object directory in case a\n> repository has alternates with multiple multi-pack indices.\n> \n> But while the documentation explicitly points out that the specified\n> object directory must be an alternate of the current repository, we\n> never verify that property. Instead, starting with 017db7bb14 (midx:\n> load multi-pack indices via their source, 2025-08-11), we now construct\n> an ad-hoc source and link it to the main object directory.\n> \n> Besides contradicting the documentation, it's dubious that this really\n> ought to work in the first place: creating a multi-pack index (and\n> potentially a bitmap) for a completely foreign object directory is of\n> questionable value, as bitmap commit selection operates on the invoking\n> repository's refs. Furthermore, this is the only remaining caller\n> outside of our test helpers that constructs an ad-hoc source and links\n> it to the database, and we want to get rid of this mechanism as part of\n> this series.\n\nI was curious if there was any intentional reason that 017db7bb14\nstarted added these as an alternate source. I assume though the reason\nwas just to address the tests when we started loading multi-pack indexes\nvia sources. So aligning with the prexisting documentation makes sense\nto me.\n\n> Stop constructing the ad-hoc source and instead refuse the operation.\n> While this results in a change in behaviour, this restriction has been\n> documented as such ever since f57a739691 (midx: avoid opening multiple\n> MIDXs when writing, 2021-09-01).\n> \n> Note that this change requires us to adapt one test chain in t5319, as\n> it creates an object directory that is not connected to any repository\n> and then uses it via \"--object-dir=\". The setup itself already documents\n> this and does the necessary gymnastics to link the object directory to a\n> temporary repository, but subsequent tests don't. Adapt those tests to\n> retain and reuse the temporary repository.\n> \n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  builtin/multi-pack-index.c  | 3 ++-\n>  t/t5319-multi-pack-index.sh | 9 ++++-----\n>  2 files changed, 6 insertions(+), 6 deletions(-)\n> \n> diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c\n> index 6e73c85cde..753bd53a70 100644\n> --- a/builtin/multi-pack-index.c\n> +++ b/builtin/multi-pack-index.c\n> @@ -90,7 +90,8 @@ static struct odb_source_files *handle_object_dir_option(struct repository *repo\n>  {\n>  \tstruct odb_source *source = odb_find_source(repo->objects, opts.object_dir);\n>  \tif (!source)\n> -\t\tsource = odb_add_to_alternates_memory(repo->objects, opts.object_dir);\n> +\t\tdie(_(\"object directory is not an alternate of the current repository: '%s'\"),\n> +\t\t    opts.object_dir);\n\nNow we no longer add these as in-memory alternates sources. Looks good.\n\n>  \treturn odb_source_files_downcast(source);\n>  }\n>  \n> diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh\n> index 68143cb5b7..00e90f163f 100755\n> --- a/t/t5319-multi-pack-index.sh\n> +++ b/t/t5319-multi-pack-index.sh\n> @@ -698,10 +698,9 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '\n>  \tcorrupt_data $idx64 $(test_oid idxoff) \"\\02\" &&\n>  \t# objects64 is not a real repository, but can serve as an alternate\n>  \t# anyway so we can write a MIDX into it\n> -\tgit init repo &&\n> -\ttest_when_finished \"rm -fr repo\" &&\n\nOk, now we just reuse the properly set up repo. Make sense.\n\n> +\tgit init repo64 &&\n>  \t(\n> -\t\tcd repo &&\n> +\t\tcd repo64 &&\n>  \t\t( cd ../objects64 && pwd ) >.git/objects/info/alternates &&\n>  \t\tmidx64=$(git multi-pack-index --object-dir=../objects64 write)\n>  \t) &&\n> @@ -709,7 +708,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '\n>  '\n>  \n>  test_expect_success 'verify multi-pack-index with 64-bit offsets' '\n> -\tgit multi-pack-index verify --object-dir=objects64\n> +\tgit -C repo64 multi-pack-index verify --object-dir=../objects64\n>  '\n>  \n>  NUM_OBJECTS=63\n> @@ -721,7 +720,7 @@ MIDX_BYTE_LARGE_OFFSET=$(($MIDX_OFFSET_LARGE_OFFSETS + 3))\n>  \n>  test_expect_success 'verify incorrect 64-bit offset' '\n>  \tcorrupt_midx_and_verify $MIDX_BYTE_LARGE_OFFSET \"\\07\" objects64 \\\n> -\t\t\"incorrect object offset\"\n> +\t\t\"incorrect object offset\" \"git -C repo64 multi-pack-index verify --object-dir=../objects64\"\n>  '\n>  \n>  test_expect_success 'setup expire tests' '\n> \n> -- \n> 2.55.0.979.g7e5102b832.dirty\n> \n> \n"},{"id":"552292","messageId":"aqD0F795VKJ6jUFd@pks.im","threadId":"66255","inReplyTo":"aqCTAXB3AbGW8Dt8@denethor","subject":"Re: [PATCH v2 05/13] submodule-config: stop registering submodule sources","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-09T05:52:23Z","receivedAt":"2026-09-09T05:52:29Z","isPatch":true,"body":"On Tue, Sep 08, 2026 at 06:04:54PM -0500, Justin Tobler wrote:\n> On 26/09/07 09:50AM, Patrick Steinhardt wrote:\n> > On Sun, Sep 06, 2026 at 01:38:15PM -0500, Justin Tobler wrote:\n> > > On 26/09/02 03:34PM, Patrick Steinhardt wrote:\n> > > > When reading the \".gitmodules\" file from a blob in a repository other\n> > > > than `the_repository`, we register the repository's object database as\n> > > > an in-memory source of `the_repository`'s object database. This call has\n> > > > its origins in d9b8b8f896 (submodule-config.c: use repo_get_oid for\n> > > > reading .gitmodules, 2019-04-16): back then, `config_with_options()` was\n> > > > not able to read a blob from an arbitrary repository, but would always\n> > > > read it via `the_repository`. So even though the blob could be resolved\n> > > > in the submodule repository via `repo_get_oid()`, the submodule's object\n> > > > database had to be registered as an in-memory source of `the_repository`\n> > > > so that the subsequent object read was able to find the blob at all.\n> > > \n> > > Ok, so IIUC the problem was that a gitmodule blob from a repository that\n> > > is not \"the_repository\" would fail to be read by `config_with_options()`\n> > > because it would only read objects from \"the_repository\". The workaround\n> > > was to add the other repositories as another source to ensure the\n> > > gitmodule blob could be read. I had to reread the commit message a\n> > > couple of times to follow, but I think it makes sense now.\n> > \n> > Yes. Do you think I should rewrite the commit message to make it a bit\n> > less dense? Otherwise I'll leave it as-is for now.\n> \n> It is a little dense. I think it is a bit confusing which \"repository\"\n> we are talking about in the first paragraph. I think:\n> \n>   ...we register _the_ repository's object database... \n> \n> is a bit ambiguous since \"the repository\" is actually a repository that\n> is _not_ `the_repository`. At least for me, I was getting a bit tripped\n> up. XD\n\nHow about we say \"we register _that_ repository's object database\"\ninstead then? I've queued that change locally, but I'll refrain from\nsending a new version only to swap out that one word.\n\nThanks!\n\nPatrick\n"}]}