From 11c689b8730e0927ef9fc73eea59270318b3177d Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jul 2026 14:01:59 +0200 Subject: [PATCH 1/6] packfile: thread odb_source_packed through packed_object_info() Add an optional `struct odb_source_packed *source` parameter to `packed_object_info()` and `packed_object_info_with_index_pos()`. This parameter is unused at this point in time, but it will be used in a follow-up commit so that we can record the source of a specific object. Note that callers in "odb/source-packed.c" pass the already-available source, but all other callers pass `NULL` instead. This is fine though, as we only care about populating this info when called via the packed store. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 2 +- builtin/pack-objects.c | 4 ++-- commit-graph.c | 2 +- odb/source-packed.c | 4 ++-- pack-bitmap.c | 2 +- packfile.c | 8 +++++--- packfile.h | 6 ++++-- t/helper/test-bitmap.c | 2 +- 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 0f3dbd9850..8726485f1f 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -497,7 +497,7 @@ static void batch_object_write(const char *obj_name, data->info.sizep = &data->size; if (pack) - ret = packed_object_info(pack, offset, &data->info); + ret = packed_object_info(NULL, pack, offset, &data->info); else ret = odb_read_object_info_extended(the_repository->objects, &data->oid, &data->info, diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index bc5f9ef321..620d9ce085 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2463,7 +2463,7 @@ static void drop_reused_delta(struct object_entry *entry) oi.sizep = &size; oi.typep = &type; - if (packed_object_info(IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { + if (packed_object_info(NULL, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { /* * We failed to get the info from this pack for some reason; * fall back to odb_read_object_info, which may find another copy. @@ -3804,7 +3804,7 @@ static int add_object_entry_from_pack(const struct object_id *oid, ofs = nth_packed_object_offset(p, pos); oi.typep = &type; - if (packed_object_info(p, ofs, &oi) < 0) { + if (packed_object_info(NULL, p, ofs, &oi) < 0) { die(_("could not get type of object %s in pack %s"), oid_to_hex(oid), p->pack_name); } else if (type == OBJ_COMMIT) { diff --git a/commit-graph.c b/commit-graph.c index c6d9c5c740..9dc8bd5eee 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -1538,7 +1538,7 @@ static int add_packed_commits(const struct object_id *oid, struct object_info oi = OBJECT_INFO_INIT; oi.typep = &type; - if (packed_object_info(pack, offset, &oi) < 0) + if (packed_object_info(NULL, pack, offset, &oi) < 0) die(_("unable to get type of object %s"), oid_to_hex(oid)); return add_packed_commits_oi(oid, &oi, data); diff --git a/odb/source-packed.c b/odb/source-packed.c index 42c28fba0e..43fb53b72d 100644 --- a/odb/source-packed.c +++ b/odb/source-packed.c @@ -59,7 +59,7 @@ static int odb_source_packed_read_object_info(struct odb_source *source, if (!oi) return 0; - ret = packed_object_info(e.p, e.offset, oi); + ret = packed_object_info(packed, e.p, e.offset, oi); if (ret < 0) { mark_bad_packed_object(e.p, oid); return -1; @@ -99,7 +99,7 @@ static int odb_source_packed_for_each_object_wrapper(const struct object_id *oid off_t offset = nth_packed_object_offset(pack, index_pos); struct object_info oi = *data->request; - if (packed_object_info_with_index_pos(pack, offset, + if (packed_object_info_with_index_pos(data->store, pack, offset, &index_pos, &oi) < 0) { mark_bad_packed_object(pack, oid); return -1; diff --git a/pack-bitmap.c b/pack-bitmap.c index 83eb47a28b..35774b6f0c 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -1877,7 +1877,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git, ofs = pack_pos_to_offset(pack, pos); } - if (packed_object_info(pack, ofs, &oi) < 0) { + if (packed_object_info(NULL, pack, ofs, &oi) < 0) { struct object_id oid; nth_bitmap_object_oid(bitmap_git, &oid, pack_pos_to_index(pack, pos)); diff --git a/packfile.c b/packfile.c index 1d1b23b6cc..2b741d7a76 100644 --- a/packfile.c +++ b/packfile.c @@ -1324,7 +1324,8 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset, hashmap_add(&delta_base_cache, &ent->ent); } -int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset, +int packed_object_info_with_index_pos(struct odb_source_packed *source UNUSED, + struct packed_git *p, off_t obj_offset, uint32_t *maybe_index_pos, struct object_info *oi) { struct pack_window *w_curs = NULL; @@ -1446,10 +1447,11 @@ out: return ret; } -int packed_object_info(struct packed_git *p, off_t obj_offset, +int packed_object_info(struct odb_source_packed *source, + struct packed_git *p, off_t obj_offset, struct object_info *oi) { - return packed_object_info_with_index_pos(p, obj_offset, NULL, oi); + return packed_object_info_with_index_pos(source, p, obj_offset, NULL, oi); } static void *unpack_compressed_entry(struct packed_git *p, diff --git a/packfile.h b/packfile.h index 2329a69701..e1f77152b5 100644 --- a/packfile.h +++ b/packfile.h @@ -320,9 +320,11 @@ extern int do_check_packed_object_crc; * Look up the object info for a specific offset in the packfile. * Returns zero on success, a negative error code otherwise. */ -int packed_object_info(struct packed_git *pack, +int packed_object_info(struct odb_source_packed *source, + struct packed_git *pack, off_t offset, struct object_info *); -int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset, +int packed_object_info_with_index_pos(struct odb_source_packed *source, + struct packed_git *p, off_t obj_offset, uint32_t *maybe_index_pos, struct object_info *oi); void mark_bad_packed_object(struct packed_git *, const struct object_id *); diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c index b130832b81..8547ef67e2 100644 --- a/t/helper/test-bitmap.c +++ b/t/helper/test-bitmap.c @@ -52,7 +52,7 @@ static int add_packed_object(const struct object_id *oid, entry = packlist_alloc(packed, oid); entry->idx.offset = nth_packed_object_offset(pack, pos); - if (packed_object_info(pack, entry->idx.offset, &oi) < 0) + if (packed_object_info(NULL, pack, entry->idx.offset, &oi) < 0) die("could not get type of object %s", oid_to_hex(oid)); oe_set_type(entry, type); From 126076b62f5164f9da6bbe454fc71c164ea5cb42 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jul 2026 14:02:00 +0200 Subject: [PATCH 2/6] odb: make backend-specific fields optional The `struct object_info` carries two pieces of information about how an object was looked up: - The `whence` enum identifying the backend. - The backend-tagged union `u` exposing backend-specific details (currently only the packed-source case, which records the owning pack, offset and packed object type). The union is populated unconditionally, even though most callers don't care about provenance at all. Split the backend-specific union out into a new public type, `struct object_info_source`, and make the object info structure carry it via just another opt-in request pointer. As with all the other requestable information, callers that need source info allocate a `struct object_info_source` on the stack and point `sourcep` at it; callers that don't care about it simply leave the field as a `NULL` pointer. Adapt callers accordingly. Note that the `whence` enum is strictly-speaking also backend-specific information, so it would be another good candidate to be moved into the `struct object_info_source`. For now though it is left alone, as it will be replaced by a `struct odb_source` pointer in a subsequent commit. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 8 ++++-- builtin/index-pack.c | 8 ++++-- builtin/pack-objects.c | 15 ++++++++--- odb.c | 3 ++- odb.h | 60 ++++++++++++++++++++++++++++-------------- packfile.c | 33 ++++++++++++----------- reachable.c | 5 +++- 7 files changed, 87 insertions(+), 45 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 8726485f1f..adc626ce30 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -835,7 +835,8 @@ static int batch_one_object_oi(const struct object_id *oid, { struct for_each_object_payload *payload = _payload; if (oi && oi->whence == OI_PACKED) - return payload->callback(oid, oi->u.packed.pack, oi->u.packed.offset, + return payload->callback(oid, oi->sourcep->u.packed.pack, + oi->sourcep->u.packed.offset, payload->payload); return payload->callback(oid, NULL, 0, payload->payload); } @@ -906,7 +907,10 @@ static void batch_each_object(struct batch_options *opt, &payload, flags); } } else { - struct object_info oi = { 0 }; + struct object_info_source oi_source; + struct object_info oi = { + .sourcep = &oi_source, + }; for (source = the_repository->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index f396658468..77af26db8f 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1825,11 +1825,15 @@ static void repack_local_links(void) oidset_iter_init(&outgoing_links, &iter); while ((oid = oidset_iter_next(&iter))) { - struct object_info info = OBJECT_INFO_INIT; + struct object_info_source info_source; + struct object_info info = { + .sourcep = &info_source, + }; + if (odb_read_object_info_extended(the_repository->objects, oid, &info, 0)) /* Missing; assume it is a promisor object */ continue; - if (info.whence == OI_PACKED && info.u.packed.pack->pack_promisor) + if (info.whence == OI_PACKED && info_source.u.packed.pack->pack_promisor) continue; if (!cmd.args.nr) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 620d9ce085..9deb37e9e8 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -4491,8 +4491,9 @@ static int add_object_in_unpacked_pack(const struct object_id *oid, void *data UNUSED) { if (cruft) { - add_cruft_object_entry(oid, OBJ_NONE, oi->u.packed.pack, - oi->u.packed.offset, NULL, *oi->mtimep); + add_cruft_object_entry(oid, OBJ_NONE, oi->sourcep->u.packed.pack, + oi->sourcep->u.packed.offset, NULL, + *oi->mtimep); } else { add_object_entry(oid, OBJ_NONE, "", 0); } @@ -4509,8 +4510,10 @@ static void add_objects_in_unpacked_packs(void) ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS | ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS, }; + struct object_info_source oi_source; struct object_info oi = { .mtimep = &mtime, + .sourcep = &oi_source, }; odb_prepare_alternates(to_pack.repo->objects); @@ -5000,10 +5003,14 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED, static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED) { - struct object_info info = OBJECT_INFO_INIT; + struct object_info_source info_source; + struct object_info info = { + .sourcep = &info_source, + }; + if (odb_read_object_info_extended(the_repository->objects, &obj->oid, &info, 0)) BUG("should_include_obj should only be called on existing objects"); - return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor; + return info.whence != OI_PACKED || !info_source.u.packed.pack->pack_promisor; } static int is_not_in_promisor_pack(struct commit *commit, void *data) { diff --git a/odb.c b/odb.c index 7d555be09f..99f4e7551c 100644 --- a/odb.c +++ b/odb.c @@ -692,7 +692,8 @@ static int oid_object_info_convert(struct repository *r, } } input_oi->whence = new_oi.whence; - input_oi->u = new_oi.u; + if (input_oi->sourcep) + *input_oi->sourcep = *new_oi.sourcep; return ret; } diff --git a/odb.h b/odb.h index 3834a0dcbf..770900289a 100644 --- a/odb.h +++ b/odb.h @@ -248,6 +248,38 @@ int odb_pretend_object(struct object_database *odb, void *buf, size_t len, enum object_type type, struct object_id *oid); +/* + * Object information that can be used to uniquely identify an object and learn + * more about how exactly it is stored. + */ +struct object_info_source { + /* + * Backend-specific information about the specific object. This can be + * used for example to uniquely identify a given object in case it + * exists multiple times. + */ + union { + /* + * struct { + * ... Nothing to expose in this case + * } cached; + * struct { + * ... Nothing to expose in this case + * } loose; + */ + struct { + struct packed_git *pack; + off_t offset; + enum packed_object_type { + PACKED_OBJECT_TYPE_UNKNOWN, + PACKED_OBJECT_TYPE_FULL, + PACKED_OBJECT_TYPE_OFS_DELTA, + PACKED_OBJECT_TYPE_REF_DELTA, + } type; + } packed; + } u; +}; + struct object_info { /* Request */ enum object_type *typep; @@ -269,32 +301,20 @@ struct object_info { */ time_t *mtimep; + /* + * Backend-specific information that tells the caller where exactly an + * object was looked up from. This information should help disambiguate + * object lookups in case the same object exists in multiple sources, + * or multiple times in the same source. + */ + struct object_info_source *sourcep; + /* Response */ enum { OI_CACHED, OI_LOOSE, OI_PACKED, } whence; - union { - /* - * struct { - * ... Nothing to expose in this case - * } cached; - * struct { - * ... Nothing to expose in this case - * } loose; - */ - struct { - struct packed_git *pack; - off_t offset; - enum packed_object_type { - PACKED_OBJECT_TYPE_UNKNOWN, - PACKED_OBJECT_TYPE_FULL, - PACKED_OBJECT_TYPE_OFS_DELTA, - PACKED_OBJECT_TYPE_REF_DELTA, - } type; - } packed; - } u; }; /* diff --git a/packfile.c b/packfile.c index 2b741d7a76..688c410b35 100644 --- a/packfile.c +++ b/packfile.c @@ -1422,22 +1422,25 @@ int packed_object_info_with_index_pos(struct odb_source_packed *source UNUSED, } oi->whence = OI_PACKED; - oi->u.packed.offset = obj_offset; - oi->u.packed.pack = p; - switch (type) { - case OBJ_NONE: - oi->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN; - break; - case OBJ_REF_DELTA: - oi->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA; - break; - case OBJ_OFS_DELTA: - oi->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA; - break; - default: - oi->u.packed.type = PACKED_OBJECT_TYPE_FULL; - break; + if (oi->sourcep) { + oi->sourcep->u.packed.offset = obj_offset; + oi->sourcep->u.packed.pack = p; + + switch (type) { + case OBJ_NONE: + oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN; + break; + case OBJ_REF_DELTA: + oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA; + break; + case OBJ_OFS_DELTA: + oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA; + break; + default: + oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_FULL; + break; + } } ret = 0; diff --git a/reachable.c b/reachable.c index 101cfc2727..2fc5b82d62 100644 --- a/reachable.c +++ b/reachable.c @@ -235,7 +235,8 @@ static int add_recent_object(const struct object_id *oid, add_pending_object(data->revs, obj, ""); if (data->cb) { if (oi->whence == OI_PACKED) - data->cb(obj, oi->u.packed.pack, oi->u.packed.offset, *oi->mtimep); + data->cb(obj, oi->sourcep->u.packed.pack, + oi->sourcep->u.packed.offset, *oi->mtimep); else data->cb(obj, NULL, 0, *oi->mtimep); } @@ -252,9 +253,11 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs, unsigned flags; enum object_type type; time_t mtime; + struct object_info_source oi_source; struct object_info oi = { .mtimep = &mtime, .typep = &type, + .sourcep = &oi_source, }; int r; From 32a95be604e710d38e05d0013fbb2a64257c4014 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jul 2026 14:02:01 +0200 Subject: [PATCH 3/6] odb: add `source` field to struct object_info_source The previous commit introduced `struct object_info_source` as an opt-in container for backend-specific information, but for now we only moved preexisting data into this structure. Most importantly, the caller has no way yet to learn about which source an object was actually looked up from. Instead, callers have to rely on the `whence` enum to distinguish the object type, but cannot use that enum to tell the object source. Add a `struct odb_source *source` field to the structure and populate it from each backend's lookup path. The `whence` enum is still set and used by callers; it will be removed in a subsequent commit now that `sourcep->source` can identify the backend on its own. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 8 ++++---- builtin/index-pack.c | 6 +++--- builtin/pack-objects.c | 14 +++++++------- odb.c | 4 ++-- odb.h | 11 +++++++---- odb/source-inmemory.c | 3 +++ odb/source-loose.c | 2 ++ packfile.c | 20 ++++++++++++-------- reachable.c | 8 ++++---- 9 files changed, 44 insertions(+), 32 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index adc626ce30..0aca6acb75 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -835,8 +835,8 @@ static int batch_one_object_oi(const struct object_id *oid, { struct for_each_object_payload *payload = _payload; if (oi && oi->whence == OI_PACKED) - return payload->callback(oid, oi->sourcep->u.packed.pack, - oi->sourcep->u.packed.offset, + return payload->callback(oid, oi->source_infop->u.packed.pack, + oi->source_infop->u.packed.offset, payload->payload); return payload->callback(oid, NULL, 0, payload->payload); } @@ -907,9 +907,9 @@ static void batch_each_object(struct batch_options *opt, &payload, flags); } } else { - struct object_info_source oi_source; + struct odb_source_info source_info; struct object_info oi = { - .sourcep = &oi_source, + .source_infop = &source_info, }; for (source = the_repository->objects->sources; source; source = source->next) { diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 77af26db8f..fe6e70522d 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1825,15 +1825,15 @@ static void repack_local_links(void) oidset_iter_init(&outgoing_links, &iter); while ((oid = oidset_iter_next(&iter))) { - struct object_info_source info_source; + struct odb_source_info source_info; struct object_info info = { - .sourcep = &info_source, + .source_infop = &source_info, }; if (odb_read_object_info_extended(the_repository->objects, oid, &info, 0)) /* Missing; assume it is a promisor object */ continue; - if (info.whence == OI_PACKED && info_source.u.packed.pack->pack_promisor) + if (info.whence == OI_PACKED && source_info.u.packed.pack->pack_promisor) continue; if (!cmd.args.nr) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 9deb37e9e8..b7ef90f67c 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -4491,8 +4491,8 @@ static int add_object_in_unpacked_pack(const struct object_id *oid, void *data UNUSED) { if (cruft) { - add_cruft_object_entry(oid, OBJ_NONE, oi->sourcep->u.packed.pack, - oi->sourcep->u.packed.offset, NULL, + add_cruft_object_entry(oid, OBJ_NONE, oi->source_infop->u.packed.pack, + oi->source_infop->u.packed.offset, NULL, *oi->mtimep); } else { add_object_entry(oid, OBJ_NONE, "", 0); @@ -4510,10 +4510,10 @@ static void add_objects_in_unpacked_packs(void) ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS | ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS, }; - struct object_info_source oi_source; + struct odb_source_info source_info; struct object_info oi = { .mtimep = &mtime, - .sourcep = &oi_source, + .source_infop = &source_info, }; odb_prepare_alternates(to_pack.repo->objects); @@ -5003,14 +5003,14 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED, static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED) { - struct object_info_source info_source; + struct odb_source_info source_info; struct object_info info = { - .sourcep = &info_source, + .source_infop = &source_info, }; if (odb_read_object_info_extended(the_repository->objects, &obj->oid, &info, 0)) BUG("should_include_obj should only be called on existing objects"); - return info.whence != OI_PACKED || !info_source.u.packed.pack->pack_promisor; + return info.whence != OI_PACKED || !source_info.u.packed.pack->pack_promisor; } static int is_not_in_promisor_pack(struct commit *commit, void *data) { diff --git a/odb.c b/odb.c index 99f4e7551c..34c35c47a5 100644 --- a/odb.c +++ b/odb.c @@ -692,8 +692,8 @@ static int oid_object_info_convert(struct repository *r, } } input_oi->whence = new_oi.whence; - if (input_oi->sourcep) - *input_oi->sourcep = *new_oi.sourcep; + if (input_oi->source_infop) + *input_oi->source_infop = *new_oi.source_infop; return ret; } diff --git a/odb.h b/odb.h index 770900289a..659bf8afe1 100644 --- a/odb.h +++ b/odb.h @@ -249,10 +249,13 @@ int odb_pretend_object(struct object_database *odb, struct object_id *oid); /* - * Object information that can be used to uniquely identify an object and learn - * more about how exactly it is stored. + * Object database source information that can be used to uniquely identify an + * object and learn more about how exactly it is stored. */ -struct object_info_source { +struct odb_source_info { + /* The source that this object has been looked up from. */ + struct odb_source *source; + /* * Backend-specific information about the specific object. This can be * used for example to uniquely identify a given object in case it @@ -307,7 +310,7 @@ struct object_info { * object lookups in case the same object exists in multiple sources, * or multiple times in the same source. */ - struct object_info_source *sourcep; + struct odb_source_info *source_infop; /* Response */ enum { diff --git a/odb/source-inmemory.c b/odb/source-inmemory.c index e004566d76..1d173bfa46 100644 --- a/odb/source-inmemory.c +++ b/odb/source-inmemory.c @@ -52,6 +52,9 @@ static void populate_object_info(struct odb_source_inmemory *source, *oi->contentp = xmemdupz(object->buf, object->size); if (oi->mtimep) *oi->mtimep = 0; + if (oi->source_infop) + oi->source_infop->source = &source->base; + oi->whence = OI_CACHED; } diff --git a/odb/source-loose.c b/odb/source-loose.c index 66e6bb8d3f..c254957602 100644 --- a/odb/source-loose.c +++ b/odb/source-loose.c @@ -196,6 +196,8 @@ out: oi->typep = NULL; if (oi->delta_base_oid) oidclr(oi->delta_base_oid, loose->base.odb->repo->hash_algo); + if (oi->source_infop && !ret) + oi->source_infop->source = &loose->base; if (!ret) oi->whence = OI_LOOSE; } diff --git a/packfile.c b/packfile.c index 688c410b35..ce51d1e5a3 100644 --- a/packfile.c +++ b/packfile.c @@ -1324,7 +1324,7 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset, hashmap_add(&delta_base_cache, &ent->ent); } -int packed_object_info_with_index_pos(struct odb_source_packed *source UNUSED, +int packed_object_info_with_index_pos(struct odb_source_packed *source, struct packed_git *p, off_t obj_offset, uint32_t *maybe_index_pos, struct object_info *oi) { @@ -1423,22 +1423,26 @@ int packed_object_info_with_index_pos(struct odb_source_packed *source UNUSED, oi->whence = OI_PACKED; - if (oi->sourcep) { - oi->sourcep->u.packed.offset = obj_offset; - oi->sourcep->u.packed.pack = p; + if (oi->source_infop) { + if (!source) + BUG("cannot request source without an owning source"); + oi->source_infop->source = &source->base; + + oi->source_infop->u.packed.offset = obj_offset; + oi->source_infop->u.packed.pack = p; switch (type) { case OBJ_NONE: - oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN; + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN; break; case OBJ_REF_DELTA: - oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA; + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA; break; case OBJ_OFS_DELTA: - oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA; + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA; break; default: - oi->sourcep->u.packed.type = PACKED_OBJECT_TYPE_FULL; + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_FULL; break; } } diff --git a/reachable.c b/reachable.c index 2fc5b82d62..bf76b48fc5 100644 --- a/reachable.c +++ b/reachable.c @@ -235,8 +235,8 @@ static int add_recent_object(const struct object_id *oid, add_pending_object(data->revs, obj, ""); if (data->cb) { if (oi->whence == OI_PACKED) - data->cb(obj, oi->sourcep->u.packed.pack, - oi->sourcep->u.packed.offset, *oi->mtimep); + data->cb(obj, oi->source_infop->u.packed.pack, + oi->source_infop->u.packed.offset, *oi->mtimep); else data->cb(obj, NULL, 0, *oi->mtimep); } @@ -253,11 +253,11 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs, unsigned flags; enum object_type type; time_t mtime; - struct object_info_source oi_source; + struct odb_source_info source_info; struct object_info oi = { .mtimep = &mtime, .typep = &type, - .sourcep = &oi_source, + .source_infop = &source_info, }; int r; From 2242f7ee36e9ec1a70314b685b1680e641e56f88 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jul 2026 14:02:02 +0200 Subject: [PATCH 4/6] treewide: convert users of `whence` to the new source field The `whence` field has become redundant now that callers can learn about the exact source an object has been looked up from via the `struct object_info_source::source` field. Adapt callers to use the new field. Note that all callsites already set up the `info.sourcep` request pointer, so the conversion is rather straight-forward. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 2 +- builtin/index-pack.c | 3 ++- builtin/pack-objects.c | 2 +- reachable.c | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 0aca6acb75..758f8fc736 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -834,7 +834,7 @@ static int batch_one_object_oi(const struct object_id *oid, void *_payload) { struct for_each_object_payload *payload = _payload; - if (oi && oi->whence == OI_PACKED) + if (oi && oi->source_infop->source->type == ODB_SOURCE_PACKED) return payload->callback(oid, oi->source_infop->u.packed.pack, oi->source_infop->u.packed.offset, payload->payload); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index fe6e70522d..7af1aea6f9 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1833,7 +1833,8 @@ static void repack_local_links(void) if (odb_read_object_info_extended(the_repository->objects, oid, &info, 0)) /* Missing; assume it is a promisor object */ continue; - if (info.whence == OI_PACKED && source_info.u.packed.pack->pack_promisor) + if (source_info.source->type == ODB_SOURCE_PACKED && + source_info.u.packed.pack->pack_promisor) continue; if (!cmd.args.nr) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index b7ef90f67c..4fdb6dbf6f 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -5010,7 +5010,7 @@ static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED) if (odb_read_object_info_extended(the_repository->objects, &obj->oid, &info, 0)) BUG("should_include_obj should only be called on existing objects"); - return info.whence != OI_PACKED || !source_info.u.packed.pack->pack_promisor; + return source_info.source->type != ODB_SOURCE_PACKED || !source_info.u.packed.pack->pack_promisor; } static int is_not_in_promisor_pack(struct commit *commit, void *data) { diff --git a/reachable.c b/reachable.c index bf76b48fc5..caadacc02a 100644 --- a/reachable.c +++ b/reachable.c @@ -234,7 +234,7 @@ static int add_recent_object(const struct object_id *oid, add_pending_object(data->revs, obj, ""); if (data->cb) { - if (oi->whence == OI_PACKED) + if (oi->source_infop->source->type == ODB_SOURCE_PACKED) data->cb(obj, oi->source_infop->u.packed.pack, oi->source_infop->u.packed.offset, *oi->mtimep); else From aea037e53444fd2ffebbd22b49726d9730ced389 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jul 2026 14:02:03 +0200 Subject: [PATCH 5/6] odb: drop `whence` field from object info In the preceding commits we have migrated all callers to derive their information of how a specific object is stored to use the new object info source instead, and hence the field is now unused. Drop it. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- odb.c | 1 - odb.h | 7 ------- odb/source-inmemory.c | 2 -- odb/source-loose.c | 2 -- packfile.c | 2 -- 5 files changed, 14 deletions(-) diff --git a/odb.c b/odb.c index 34c35c47a5..175a1ee42c 100644 --- a/odb.c +++ b/odb.c @@ -691,7 +691,6 @@ static int oid_object_info_convert(struct repository *r, return -1; } } - input_oi->whence = new_oi.whence; if (input_oi->source_infop) *input_oi->source_infop = *new_oi.source_infop; return ret; diff --git a/odb.h b/odb.h index 659bf8afe1..c251788d50 100644 --- a/odb.h +++ b/odb.h @@ -311,13 +311,6 @@ struct object_info { * or multiple times in the same source. */ struct odb_source_info *source_infop; - - /* Response */ - enum { - OI_CACHED, - OI_LOOSE, - OI_PACKED, - } whence; }; /* diff --git a/odb/source-inmemory.c b/odb/source-inmemory.c index 1d173bfa46..460aec821c 100644 --- a/odb/source-inmemory.c +++ b/odb/source-inmemory.c @@ -54,8 +54,6 @@ static void populate_object_info(struct odb_source_inmemory *source, *oi->mtimep = 0; if (oi->source_infop) oi->source_infop->source = &source->base; - - oi->whence = OI_CACHED; } static int odb_source_inmemory_read_object_info(struct odb_source *source, diff --git a/odb/source-loose.c b/odb/source-loose.c index c254957602..54df2e57d3 100644 --- a/odb/source-loose.c +++ b/odb/source-loose.c @@ -198,8 +198,6 @@ out: oidclr(oi->delta_base_oid, loose->base.odb->repo->hash_algo); if (oi->source_infop && !ret) oi->source_infop->source = &loose->base; - if (!ret) - oi->whence = OI_LOOSE; } return ret; diff --git a/packfile.c b/packfile.c index ce51d1e5a3..8fa6309a09 100644 --- a/packfile.c +++ b/packfile.c @@ -1421,8 +1421,6 @@ int packed_object_info_with_index_pos(struct odb_source_packed *source, oidclr(oi->delta_base_oid, p->repo->hash_algo); } - oi->whence = OI_PACKED; - if (oi->source_infop) { if (!source) BUG("cannot request source without an owning source"); From 8a7ad23e11de9a1de0d16a3aaddb840be768ab30 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jul 2026 14:02:04 +0200 Subject: [PATCH 6/6] odb: document object info fields Some of the fields in `struct object_info` are undocumented. Add these missing comments. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- odb.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/odb.h b/odb.h index c251788d50..a1e222f605 100644 --- a/odb.h +++ b/odb.h @@ -283,12 +283,28 @@ struct odb_source_info { } u; }; +/* + * The object info contains the query and response that is to be used for + * functions that end up reading object information. Callers are expected to + * populate pointers whose information they want to request. + */ struct object_info { - /* Request */ + /* The object type. */ enum object_type *typep; + + /* The inflated object size in bytes. */ size_t *sizep; + + /* The object size as stored on disk. */ off_t *disk_sizep; + + /* + * The base the object is deltified against, in case it is stored as a + * delta. + */ struct object_id *delta_base_oid; + + /* The object contents. Ownership of memory goes over to the caller. */ void **contentp; /*