object-file: read objects via the loose object source

When reading an object via `loose_object_info()` or `map_loose_object()` we hand in the whole repository. We then iterate through each of the object sources to figure out whether that source has the object in question. This logic is reversing responsibility though: a specific backend should only care about one specific source, where the object sources themselves are then managed by the object database. Refactor the code accordingly by passing an object source to both of these functions instead. The different sources are then handled by either `do_oid_object_info_extended()`, which sits on the object database level, and by `open_istream_loose()`. The latter function arguably is still at the wrong level, but this will be cleaned up at a later point in time. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Nov 3, 2025 at 08:42 UTC ff7ad5cb3936514ec0be32531ff6274b53dbe091
4 files changed +50 -53
object-file.c
+25 -43
@@ -167,25 +167,22 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
167 }
168
169 /*
170 - * Find "oid" as a loose object in the local repository or in an alternate.
170 + * Find "oid" as a loose object in given source.
171 * Returns 0 on success, negative on failure.
172 *
173 * The "path" out-parameter will give the path of the object we found (if any).
174 * Note that it may point to static storage and is only valid until another
175 * call to stat_loose_object().
176 */
177 -static int stat_loose_object(struct repository *r, const struct object_id *oid,
177 +static int stat_loose_object(struct odb_source_loose *loose,
178 + const struct object_id *oid,
179 struct stat *st, const char **path)
180 {
180 - struct odb_source *source;
181 static struct strbuf buf = STRBUF_INIT;
182
183 - odb_prepare_alternates(r->objects);
184 - for (source = r->objects->sources; source; source = source->next) {
185 - *path = odb_loose_path(source, &buf, oid);
186 - if (!lstat(*path, st))
187 - return 0;
188 - }
183 + *path = odb_loose_path(loose->source, &buf, oid);
184 + if (!lstat(*path, st))
185 + return 0;
186
187 return -1;
188 }
@@ -194,39 +191,24 @@ static int stat_loose_object(struct repository *r, const struct object_id *oid,
191 * Like stat_loose_object(), but actually open the object and return the
192 * descriptor. See the caveats on the "path" parameter above.
193 */
197 -static int open_loose_object(struct repository *r,
194 +static int open_loose_object(struct odb_source_loose *loose,
195 const struct object_id *oid, const char **path)
196 {
200 - int fd;
201 - struct odb_source *source;
202 - int most_interesting_errno = ENOENT;
197 static struct strbuf buf = STRBUF_INIT;
198 + int fd;
199
205 - odb_prepare_alternates(r->objects);
206 - for (source = r->objects->sources; source; source = source->next) {
207 - *path = odb_loose_path(source, &buf, oid);
208 - fd = git_open(*path);
209 - if (fd >= 0)
210 - return fd;
200 + *path = odb_loose_path(loose->source, &buf, oid);
201 + fd = git_open(*path);
202 + if (fd >= 0)
203 + return fd;
204
212 - if (most_interesting_errno == ENOENT)
213 - most_interesting_errno = errno;
214 - }
215 - errno = most_interesting_errno;
205 return -1;
206 }
207
219 -static int quick_has_loose(struct repository *r,
208 +static int quick_has_loose(struct odb_source_loose *loose,
209 const struct object_id *oid)
210 {
222 - struct odb_source *source;
223 -
224 - odb_prepare_alternates(r->objects);
225 - for (source = r->objects->sources; source; source = source->next) {
226 - if (oidtree_contains(odb_source_loose_cache(source, oid), oid))
227 - return 1;
228 - }
229 - return 0;
211 + return !!oidtree_contains(odb_source_loose_cache(loose->source, oid), oid);
212 }
213
214 /*
@@ -252,12 +234,12 @@ static void *map_fd(int fd, const char *path, unsigned long *size)
234 return map;
235 }
236
255 -void *map_loose_object(struct repository *r,
256 - const struct object_id *oid,
257 - unsigned long *size)
237 +void *odb_source_loose_map_object(struct odb_source *source,
238 + const struct object_id *oid,
239 + unsigned long *size)
240 {
241 const char *p;
260 - int fd = open_loose_object(r, oid, &p);
242 + int fd = open_loose_object(source->loose, oid, &p);
243
244 if (fd < 0)
245 return NULL;
@@ -407,9 +389,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)
389 return 0;
390 }
391
410 -int loose_object_info(struct repository *r,
411 - const struct object_id *oid,
412 - struct object_info *oi, int flags)
392 +int odb_source_loose_read_object_info(struct odb_source *source,
393 + const struct object_id *oid,
394 + struct object_info *oi, int flags)
395 {
396 int status = 0;
397 int fd;
@@ -422,7 +404,7 @@ int loose_object_info(struct repository *r,
404 enum object_type type_scratch;
405
406 if (oi->delta_base_oid)
425 - oidclr(oi->delta_base_oid, r->hash_algo);
407 + oidclr(oi->delta_base_oid, source->odb->repo->hash_algo);
408
409 /*
410 * If we don't care about type or size, then we don't
@@ -435,15 +417,15 @@ int loose_object_info(struct repository *r,
417 if (!oi->typep && !oi->sizep && !oi->contentp) {
418 struct stat st;
419 if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
438 - return quick_has_loose(r, oid) ? 0 : -1;
439 - if (stat_loose_object(r, oid, &st, &path) < 0)
420 + return quick_has_loose(source->loose, oid) ? 0 : -1;
421 + if (stat_loose_object(source->loose, oid, &st, &path) < 0)
422 return -1;
423 if (oi->disk_sizep)
424 *oi->disk_sizep = st.st_size;
425 return 0;
426 }
427
446 - fd = open_loose_object(r, oid, &path);
428 + fd = open_loose_object(source->loose, oid, &path);
429 if (fd < 0) {
430 if (errno != ENOENT)
431 error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
object-file.h
+8 -7
@@ -43,6 +43,14 @@ void odb_source_loose_free(struct odb_source_loose *loose);
43 /* Reprepare the loose source by emptying the loose object cache. */
44 void odb_source_loose_reprepare(struct odb_source *source);
45
46 +int odb_source_loose_read_object_info(struct odb_source *source,
47 + const struct object_id *oid,
48 + struct object_info *oi, int flags);
49 +
50 +void *odb_source_loose_map_object(struct odb_source *source,
51 + const struct object_id *oid,
52 + unsigned long *size);
53 +
54 /*
55 * Populate and return the loose object cache array corresponding to the
56 * given object ID.
@@ -66,9 +74,6 @@ const char *odb_loose_path(struct odb_source *source,
74 int has_loose_object(struct odb_source *source,
75 const struct object_id *oid);
76
69 -void *map_loose_object(struct repository *r, const struct object_id *oid,
70 - unsigned long *size);
71 -
77 /*
78 * Iterate over the files in the loose-object parts of the object
79 * directory "path", triggering the following callbacks:
@@ -196,10 +201,6 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
201 */
202 int stream_object_signature(struct repository *r, const struct object_id *oid);
203
199 -int loose_object_info(struct repository *r,
200 - const struct object_id *oid,
201 - struct object_info *oi, int flags);
202 -
204 enum finalize_object_file_flags {
205 FOF_SKIP_COLLISION_CHECK = 1,
206 };
odb.c
+7 -2
@@ -697,13 +697,18 @@ static int do_oid_object_info_extended(struct object_database *odb,
697 return 0;
698 }
699
700 + odb_prepare_alternates(odb);
701 +
702 while (1) {
703 + struct odb_source *source;
704 +
705 if (find_pack_entry(odb->repo, real, &e))
706 break;
707
708 /* Most likely it's a loose object. */
705 - if (!loose_object_info(odb->repo, real, oi, flags))
706 - return 0;
709 + for (source = odb->sources; source; source = source->next)
710 + if (!odb_source_loose_read_object_info(source, real, oi, flags))
711 + return 0;
712
713 /* Not a loose object; someone else may have just packed it. */
714 if (!(flags & OBJECT_INFO_QUICK)) {
streaming.c
+10 -1
@@ -230,12 +230,21 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
230 enum object_type *type)
231 {
232 struct object_info oi = OBJECT_INFO_INIT;
233 + struct odb_source *source;
234 +
235 oi.sizep = &st->size;
236 oi.typep = type;
237
236 - st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize);
238 + odb_prepare_alternates(r->objects);
239 + for (source = r->objects->sources; source; source = source->next) {
240 + st->u.loose.mapped = odb_source_loose_map_object(source, oid,
241 + &st->u.loose.mapsize);
242 + if (st->u.loose.mapped)
243 + break;
244 + }
245 if (!st->u.loose.mapped)
246 return -1;
247 +
248 switch (unpack_loose_header(&st->z, st->u.loose.mapped,
249 st->u.loose.mapsize, st->u.loose.hdr,
250 sizeof(st->u.loose.hdr))) {