odb/source: make `read_object_info()` function pluggable

Introduce a new callback function in `struct odb_source` to make the function pluggable. Note that this function is a bit less straight-forward to convert compared to the other functions. The reason here is that the logic to read an object is: 1. We try to read the object. If it exists we return it. 2. If the object does not exist we reprepare the object database source. 3. We then try reading the object info a second time in case the reprepare caused it to appear. The second read is only supposed to happen for the packfile store though, as reading loose objects is not impacted by repreparing the object database. Ideally, we'd just move this whole logic into the ODB source. But that's not easily possible because we try to avoid the reprepare unless really required, which is after we have found out that no other ODB source contains the object, either. So the logic spans across multiple ODB sources, and consequently we cannot move it into an individual source. Instead, introduce a new flag `OBJECT_INFO_SECOND_READ` that tells the backend that we already tried to look up the object once, and that this time around the ODB source should try to find any new objects that may have surfaced due to an on-disk change. With this flag, the "files" backend can trivially skip trying to re-read the object as a loose object. Furthermore, as we know that we only try the second read via the packfile store, we can skip repreparing loose objects and only reprepare the packfile store. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Mar 5, 2026 at 15:19 UTC 5946a564cddc0bf471f27ae4c3fe205441e3ef65
6 files changed +122 -37
object-file.c
+10
@@ -546,6 +546,16 @@ int odb_source_loose_read_object_info(struct odb_source *source,
546 enum object_info_flags flags)
547 {
548 static struct strbuf buf = STRBUF_INIT;
549 +
550 + /*
551 + * The second read shouldn't cause new loose objects to show up, unless
552 + * there was a race condition with a secondary process. We don't care
553 + * about this case though, so we simply skip reading loose objects a
554 + * second time.
555 + */
556 + if (flags & OBJECT_INFO_SECOND_READ)
557 + return -1;
558 +
559 odb_loose_path(source, &buf, oid);
560 return read_object_info_from_path(source, buf.buf, oid, oi, flags);
561 }
odb.c
+10 -12
@@ -688,22 +688,20 @@ static int do_oid_object_info_extended(struct object_database *odb,
688 while (1) {
689 struct odb_source *source;
690
691 - /* Most likely it's a loose object. */
692 - for (source = odb->sources; source; source = source->next) {
693 - struct odb_source_files *files = odb_source_files_downcast(source);
694 - if (!packfile_store_read_object_info(files->packed, real, oi, flags) ||
695 - !odb_source_loose_read_object_info(source, real, oi, flags))
691 + for (source = odb->sources; source; source = source->next)
692 + if (!odb_source_read_object_info(source, real, oi, flags))
693 return 0;
697 - }
694
699 - /* Not a loose object; someone else may have just packed it. */
695 + /*
696 + * When the object hasn't been found we try a second read and
697 + * tell the sources so. This may cause them to invalidate
698 + * caches or reload on-disk state.
699 + */
700 if (!(flags & OBJECT_INFO_QUICK)) {
701 - odb_reprepare(odb->repo->objects);
702 - for (source = odb->sources; source; source = source->next) {
703 - struct odb_source_files *files = odb_source_files_downcast(source);
704 - if (!packfile_store_read_object_info(files->packed, real, oi, flags))
701 + for (source = odb->sources; source; source = source->next)
702 + if (!odb_source_read_object_info(source, real, oi,
703 + flags | OBJECT_INFO_SECOND_READ))
704 return 0;
706 - }
705 }
706
707 /*
odb.h
-24
@@ -339,30 +339,6 @@ struct object_info {
339 */
340 #define OBJECT_INFO_INIT { 0 }
341
342 -/* Flags that can be passed to `odb_read_object_info_extended()`. */
343 -enum object_info_flags {
344 - /* Invoke lookup_replace_object() on the given hash. */
345 - OBJECT_INFO_LOOKUP_REPLACE = (1 << 0),
346 -
347 - /* Do not reprepare object sources when the first lookup has failed. */
348 - OBJECT_INFO_QUICK = (1 << 1),
349 -
350 - /*
351 - * Do not attempt to fetch the object if missing (even if fetch_is_missing is
352 - * nonzero).
353 - */
354 - OBJECT_INFO_SKIP_FETCH_OBJECT = (1 << 2),
355 -
356 - /* Die if object corruption (not just an object being missing) was detected. */
357 - OBJECT_INFO_DIE_IF_CORRUPT = (1 << 3),
358 -
359 - /*
360 - * This is meant for bulk prefetching of missing blobs in a partial
361 - * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK.
362 - */
363 - OBJECT_INFO_FOR_PREFETCH = (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK),
364 -};
365 -
342 /*
343 * Read object info from the object database and populate the `object_info`
344 * structure. Returns 0 on success, a negative error code otherwise.
odb/source-files.c
+15
@@ -41,6 +41,20 @@ static void odb_source_files_reprepare(struct odb_source *source)
41 packfile_store_reprepare(files->packed);
42 }
43
44 +static int odb_source_files_read_object_info(struct odb_source *source,
45 + const struct object_id *oid,
46 + struct object_info *oi,
47 + enum object_info_flags flags)
48 +{
49 + struct odb_source_files *files = odb_source_files_downcast(source);
50 +
51 + if (!packfile_store_read_object_info(files->packed, oid, oi, flags) ||
52 + !odb_source_loose_read_object_info(source, oid, oi, flags))
53 + return 0;
54 +
55 + return -1;
56 +}
57 +
58 struct odb_source_files *odb_source_files_new(struct object_database *odb,
59 const char *path,
60 bool local)
@@ -55,6 +69,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb,
69 files->base.free = odb_source_files_free;
70 files->base.close = odb_source_files_close;
71 files->base.reprepare = odb_source_files_reprepare;
72 + files->base.read_object_info = odb_source_files_read_object_info;
73
74 /*
75 * Ideally, we would only ever store absolute paths in the source. This
odb/source.h
+78
@@ -12,6 +12,45 @@ enum odb_source_type {
12 ODB_SOURCE_FILES,
13 };
14
15 +/* Flags that can be passed to `odb_read_object_info_extended()`. */
16 +enum object_info_flags {
17 + /* Invoke lookup_replace_object() on the given hash. */
18 + OBJECT_INFO_LOOKUP_REPLACE = (1 << 0),
19 +
20 + /* Do not reprepare object sources when the first lookup has failed. */
21 + OBJECT_INFO_QUICK = (1 << 1),
22 +
23 + /*
24 + * Do not attempt to fetch the object if missing (even if fetch_is_missing is
25 + * nonzero).
26 + */
27 + OBJECT_INFO_SKIP_FETCH_OBJECT = (1 << 2),
28 +
29 + /* Die if object corruption (not just an object being missing) was detected. */
30 + OBJECT_INFO_DIE_IF_CORRUPT = (1 << 3),
31 +
32 + /*
33 + * We have already tried reading the object, but it couldn't be found
34 + * via any of the attached sources, and are now doing a second read.
35 + * This second read asks the individual sources to also evaluate
36 + * whether any on-disk state may have changed that may have caused the
37 + * object to appear.
38 + *
39 + * This flag is for internal use, only. The second read only occurs
40 + * when `OBJECT_INFO_QUICK` was not passed.
41 + */
42 + OBJECT_INFO_SECOND_READ = (1 << 4),
43 +
44 + /*
45 + * This is meant for bulk prefetching of missing blobs in a partial
46 + * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK.
47 + */
48 + OBJECT_INFO_FOR_PREFETCH = (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK),
49 +};
50 +
51 +struct object_id;
52 +struct object_info;
53 +
54 /*
55 * The source is the part of the object database that stores the actual
56 * objects. It thus encapsulates the logic to read and write the specific
@@ -72,6 +111,33 @@ struct odb_source {
111 * example just been repacked so that new objects will become visible.
112 */
113 void (*reprepare)(struct odb_source *source);
114 +
115 + /*
116 + * This callback is expected to read object information from the object
117 + * database source. The object info will be partially populated with
118 + * pointers for each bit of information that was requested by the
119 + * caller.
120 + *
121 + * The flags field is a combination of `OBJECT_INFO` flags. Only the
122 + * following fields need to be handled by the backend:
123 + *
124 + * - `OBJECT_INFO_QUICK` indicates it is fine to use caches without
125 + * re-verifying the data.
126 + *
127 + * - `OBJECT_INFO_SECOND_READ` indicates that the initial object
128 + * lookup has failed and that the object sources should check
129 + * whether any of its on-disk state has changed that may have
130 + * caused the object to appear. Sources are free to ignore the
131 + * second read in case they know that the first read would have
132 + * already surfaced the object without reloading any on-disk state.
133 + *
134 + * The callback is expected to return a negative error code in case
135 + * reading the object has failed, 0 otherwise.
136 + */
137 + int (*read_object_info)(struct odb_source *source,
138 + const struct object_id *oid,
139 + struct object_info *oi,
140 + enum object_info_flags flags);
141 };
142
143 /*
@@ -131,4 +197,16 @@ static inline void odb_source_reprepare(struct odb_source *source)
197 source->reprepare(source);
198 }
199
200 +/*
201 + * Read an object from the object database source identified by its object ID.
202 + * Returns 0 on success, a negative error code otherwise.
203 + */
204 +static inline int odb_source_read_object_info(struct odb_source *source,
205 + const struct object_id *oid,
206 + struct object_info *oi,
207 + enum object_info_flags flags)
208 +{
209 + return source->read_object_info(source, oid, oi, flags);
210 +}
211 +
212 #endif
packfile.c
+9 -1
@@ -2181,11 +2181,19 @@ int packfile_store_freshen_object(struct packfile_store *store,
2181 int packfile_store_read_object_info(struct packfile_store *store,
2182 const struct object_id *oid,
2183 struct object_info *oi,
2184 - enum object_info_flags flags UNUSED)
2184 + enum object_info_flags flags)
2185 {
2186 struct pack_entry e;
2187 int ret;
2188
2189 + /*
2190 + * In case the first read didn't surface the object, we have to reload
2191 + * packfiles. This may cause us to discover new packfiles that have
2192 + * been added since the last time we have prepared the packfile store.
2193 + */
2194 + if (flags & OBJECT_INFO_SECOND_READ)
2195 + packfile_store_reprepare(store);
2196 +
2197 if (!find_pack_entry(store, oid, &e))
2198 return 1;
2199