git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 09/13] object-file: read objects via the loose object source

From
Patrick Steinhardt <ps@pks.im>
Date
Oct 31, 2025, 06:12 UTC
Message-ID
<20251031-b4-pks-odb-loose-backend-v2-9-920f721aef71@pks.im>
In-Reply-To
<20251031-b4-pks-odb-loose-backend-v2-0-920f721aef71@pks.im>

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>
---
 object-file.c | 68 ++++++++++++++++++++++-------------------------------------
 object-file.h | 15 +++++++------
 odb.c         |  9 ++++++--
 streaming.c   | 11 +++++++++-
 4 files changed, 50 insertions(+), 53 deletions(-)
diff --git a/object-file.c b/object-file.c
index e8877876d77..64a3d45376a 100644
--- a/object-file.c
+++ b/object-file.c
@@ -167,25 +167,22 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
 }
 
 /*
- * Find "oid" as a loose object in the local repository or in an alternate.
+ * Find "oid" as a loose object in given source.
  * Returns 0 on success, negative on failure.
  *
  * The "path" out-parameter will give the path of the object we found (if any).
  * Note that it may point to static storage and is only valid until another
  * call to stat_loose_object().
  */
-static int stat_loose_object(struct repository *r, const struct object_id *oid,
+static int stat_loose_object(struct odb_loose_source *loose,
+			     const struct object_id *oid,
 			     struct stat *st, const char **path)
 {
-	struct odb_source *source;
 	static struct strbuf buf = STRBUF_INIT;
 
-	odb_prepare_alternates(r->objects);
-	for (source = r->objects->sources; source; source = source->next) {
-		*path = odb_loose_path(source, &buf, oid);
-		if (!lstat(*path, st))
-			return 0;
-	}
+	*path = odb_loose_path(loose->source, &buf, oid);
+	if (!lstat(*path, st))
+		return 0;
 
 	return -1;
 }
@@ -194,39 +191,24 @@ static int stat_loose_object(struct repository *r, const struct object_id *oid,
  * Like stat_loose_object(), but actually open the object and return the
  * descriptor. See the caveats on the "path" parameter above.
  */
-static int open_loose_object(struct repository *r,
+static int open_loose_object(struct odb_loose_source *loose,
 			     const struct object_id *oid, const char **path)
 {
-	int fd;
-	struct odb_source *source;
-	int most_interesting_errno = ENOENT;
 	static struct strbuf buf = STRBUF_INIT;
+	int fd;
 
-	odb_prepare_alternates(r->objects);
-	for (source = r->objects->sources; source; source = source->next) {
-		*path = odb_loose_path(source, &buf, oid);
-		fd = git_open(*path);
-		if (fd >= 0)
-			return fd;
+	*path = odb_loose_path(loose->source, &buf, oid);
+	fd = git_open(*path);
+	if (fd >= 0)
+		return fd;
 
-		if (most_interesting_errno == ENOENT)
-			most_interesting_errno = errno;
-	}
-	errno = most_interesting_errno;
 	return -1;
 }
 
-static int quick_has_loose(struct repository *r,
+static int quick_has_loose(struct odb_loose_source *loose,
 			   const struct object_id *oid)
 {
-	struct odb_source *source;
-
-	odb_prepare_alternates(r->objects);
-	for (source = r->objects->sources; source; source = source->next) {
-		if (oidtree_contains(odb_loose_source_cache(source, oid), oid))
-			return 1;
-	}
-	return 0;
+	return !!oidtree_contains(odb_loose_source_cache(loose->source, oid), oid);
 }
 
 /*
@@ -252,12 +234,12 @@ static void *map_fd(int fd, const char *path, unsigned long *size)
 	return map;
 }
 
-void *map_loose_object(struct repository *r,
-		       const struct object_id *oid,
-		       unsigned long *size)
+void *odb_loose_source_map_object(struct odb_source *source,
+				  const struct object_id *oid,
+				  unsigned long *size)
 {
 	const char *p;
-	int fd = open_loose_object(r, oid, &p);
+	int fd = open_loose_object(source->loose, oid, &p);
 
 	if (fd < 0)
 		return NULL;
@@ -407,9 +389,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)
 	return 0;
 }
 
-int loose_object_info(struct repository *r,
-		      const struct object_id *oid,
-		      struct object_info *oi, int flags)
+int odb_loose_source_read_object_info(struct odb_source *source,
+				      const struct object_id *oid,
+				      struct object_info *oi, int flags)
 {
 	int status = 0;
 	int fd;
@@ -422,7 +404,7 @@ int loose_object_info(struct repository *r,
 	enum object_type type_scratch;
 
 	if (oi->delta_base_oid)
-		oidclr(oi->delta_base_oid, r->hash_algo);
+		oidclr(oi->delta_base_oid, source->odb->repo->hash_algo);
 
 	/*
 	 * If we don't care about type or size, then we don't
@@ -435,15 +417,15 @@ int loose_object_info(struct repository *r,
 	if (!oi->typep && !oi->sizep && !oi->contentp) {
 		struct stat st;
 		if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
-			return quick_has_loose(r, oid) ? 0 : -1;
-		if (stat_loose_object(r, oid, &st, &path) < 0)
+			return quick_has_loose(source->loose, oid) ? 0 : -1;
+		if (stat_loose_object(source->loose, oid, &st, &path) < 0)
 			return -1;
 		if (oi->disk_sizep)
 			*oi->disk_sizep = st.st_size;
 		return 0;
 	}
 
-	fd = open_loose_object(r, oid, &path);
+	fd = open_loose_object(source->loose, oid, &path);
 	if (fd < 0) {
 		if (errno != ENOENT)
 			error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
diff --git a/object-file.h b/object-file.h
index 74bae27612f..58ec22d187e 100644
--- a/object-file.h
+++ b/object-file.h
@@ -43,6 +43,14 @@ void odb_loose_source_free(struct odb_loose_source *loose);
 /* Reprepare the loose source by emptying the loose object cache. */
 void odb_loose_source_reprepare(struct odb_source *source);
 
+int odb_loose_source_read_object_info(struct odb_source *source,
+				      const struct object_id *oid,
+				      struct object_info *oi, int flags);
+
+void *odb_loose_source_map_object(struct odb_source *source,
+				  const struct object_id *oid,
+				  unsigned long *size);
+
 /*
  * Populate and return the loose object cache array corresponding to the
  * given object ID.
@@ -66,9 +74,6 @@ const char *odb_loose_path(struct odb_source *source,
 int has_loose_object(struct odb_source *source,
 		     const struct object_id *oid);
 
-void *map_loose_object(struct repository *r, const struct object_id *oid,
-		       unsigned long *size);
-
 /*
  * Iterate over the files in the loose-object parts of the object
  * directory "path", triggering the following callbacks:
@@ -196,10 +201,6 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
  */
 int stream_object_signature(struct repository *r, const struct object_id *oid);
 
-int loose_object_info(struct repository *r,
-		      const struct object_id *oid,
-		      struct object_info *oi, int flags);
-
 enum finalize_object_file_flags {
 	FOF_SKIP_COLLISION_CHECK = 1,
 };
diff --git a/odb.c b/odb.c
index 96059456f20..5dc1e2c7eb0 100644
--- a/odb.c
+++ b/odb.c
@@ -697,13 +697,18 @@ static int do_oid_object_info_extended(struct object_database *odb,
 		return 0;
 	}
 
+	odb_prepare_alternates(odb);
+
 	while (1) {
+		struct odb_source *source;
+
 		if (find_pack_entry(odb->repo, real, &e))
 			break;
 
 		/* Most likely it's a loose object. */
-		if (!loose_object_info(odb->repo, real, oi, flags))
-			return 0;
+		for (source = odb->sources; source; source = source->next)
+			if (!odb_loose_source_read_object_info(source, real, oi, flags))
+				return 0;
 
 		/* Not a loose object; someone else may have just packed it. */
 		if (!(flags & OBJECT_INFO_QUICK)) {
diff --git a/streaming.c b/streaming.c
index 4b13827668e..8e554abd084 100644
--- a/streaming.c
+++ b/streaming.c
@@ -230,12 +230,21 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
 			      enum object_type *type)
 {
 	struct object_info oi = OBJECT_INFO_INIT;
+	struct odb_source *source;
+
 	oi.sizep = &st->size;
 	oi.typep = type;
 
-	st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize);
+	odb_prepare_alternates(r->objects);
+	for (source = r->objects->sources; source; source = source->next) {
+		st->u.loose.mapped = odb_loose_source_map_object(source, oid,
+								 &st->u.loose.mapsize);
+		if (st->u.loose.mapped)
+			break;
+	}
 	if (!st->u.loose.mapped)
 		return -1;
+
 	switch (unpack_loose_header(&st->z, st->u.loose.mapped,
 				    st->u.loose.mapsize, st->u.loose.hdr,
 				    sizeof(st->u.loose.hdr))) {
-- 
2.51.2.1041.gc1ab5b90ca.dirty
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 39 of 58 in “Carve out loose object source”
  1. 00/13 Carve out loose object sourcePatrick Steinhardt, Oct 24, 2025
  2. 01/13 odb: fix subtle logic to check whether an alternate is usablePatrick Steinhardt, Oct 24, 2025
  3. Junio C HamanoOct 24, 2025
  4. Karthik NayakOct 30, 2025
  5. 02/13 odb: introduce `odb_source_new()`Patrick Steinhardt, Oct 24, 2025
  6. Junio C HamanoOct 24, 2025
  7. Patrick SteinhardtOct 27, 2025
  8. 03/13 odb: adjust naming to free object sourcesPatrick Steinhardt, Oct 24, 2025
  9. Karthik NayakOct 30, 2025
  10. 04/13 object-file: move `fetch_if_missing`Patrick Steinhardt, Oct 24, 2025
  11. 05/13 object-file: introduce `struct odb_loose_source`Patrick Steinhardt, Oct 24, 2025
  12. Karthik NayakOct 30, 2025
  13. Patrick SteinhardtOct 30, 2025
  14. Patrick SteinhardtOct 31, 2025
  15. Junio C HamanoOct 31, 2025
  16. Patrick SteinhardtNov 3, 2025
  17. Junio C HamanoOct 31, 2025
  18. 06/13 object-file: move loose object cache into loose sourcePatrick Steinhardt, Oct 24, 2025
  19. Junio C HamanoOct 24, 2025
  20. Patrick SteinhardtOct 27, 2025
  21. 07/13 object-file: hide internals when we need to reprepare loose sourcesPatrick Steinhardt, Oct 24, 2025
  22. 08/13 object-file: move loose object map into loose sourcePatrick Steinhardt, Oct 24, 2025
  23. 09/13 object-file: read objects via the loose object sourcePatrick Steinhardt, Oct 24, 2025
  24. Karthik NayakOct 30, 2025
  25. 10/13 object-file: rename `has_loose_object()`Patrick Steinhardt, Oct 24, 2025
  26. 11/13 object-file: refactor freshening of objectsPatrick Steinhardt, Oct 24, 2025
  27. 12/13 object-file: rename `write_object_file()`Patrick Steinhardt, Oct 24, 2025
  28. 13/13 object-file: refactor writing objects via a streamPatrick Steinhardt, Oct 24, 2025
  29. Karthik NayakOct 30, 2025
  30. 00/13 Carve out loose object sourcePatrick Steinhardt, Oct 31, 2025
  31. 01/13 odb: fix subtle logic to check whether an alternate is usablePatrick Steinhardt, Oct 31, 2025
  32. 02/13 odb: introduce `odb_source_new()`Patrick Steinhardt, Oct 31, 2025
  33. 03/13 odb: adjust naming to free object sourcesPatrick Steinhardt, Oct 31, 2025
  34. 04/13 object-file: move `fetch_if_missing`Patrick Steinhardt, Oct 31, 2025
  35. 05/13 object-file: introduce `struct odb_loose_source`Patrick Steinhardt, Oct 31, 2025
  36. 06/13 object-file: move loose object cache into loose sourcePatrick Steinhardt, Oct 31, 2025
  37. 07/13 object-file: hide internals when we need to reprepare loose sourcesPatrick Steinhardt, Oct 31, 2025
  38. 08/13 object-file: move loose object map into loose sourcePatrick Steinhardt, Oct 31, 2025
  39. 09/13 object-file: read objects via the loose object sourcePatrick Steinhardt, Oct 31, 2025
  40. 10/13 object-file: rename `has_loose_object()`Patrick Steinhardt, Oct 31, 2025
  41. 11/13 object-file: refactor freshening of objectsPatrick Steinhardt, Oct 31, 2025
  42. 12/13 object-file: rename `write_object_file()`Patrick Steinhardt, Oct 31, 2025
  43. 13/13 object-file: refactor writing objects via a streamPatrick Steinhardt, Oct 31, 2025
  44. 00/13 Carve out loose object sourcePatrick Steinhardt, Nov 3, 2025
  45. 01/13 odb: fix subtle logic to check whether an alternate is usablePatrick Steinhardt, Nov 3, 2025
  46. 02/13 odb: introduce `odb_source_new()`Patrick Steinhardt, Nov 3, 2025
  47. 03/13 odb: adjust naming to free object sourcesPatrick Steinhardt, Nov 3, 2025
  48. 04/13 object-file: move `fetch_if_missing`Patrick Steinhardt, Nov 3, 2025
  49. 05/13 object-file: introduce `struct odb_source_loose`Patrick Steinhardt, Nov 3, 2025
  50. 06/13 object-file: move loose object cache into loose sourcePatrick Steinhardt, Nov 3, 2025
  51. 07/13 object-file: hide internals when we need to reprepare loose sourcesPatrick Steinhardt, Nov 3, 2025
  52. 08/13 object-file: move loose object map into loose sourcePatrick Steinhardt, Nov 3, 2025
  53. 09/13 object-file: read objects via the loose object sourcePatrick Steinhardt, Nov 3, 2025
  54. 10/13 object-file: rename `has_loose_object()`Patrick Steinhardt, Nov 3, 2025
  55. 11/13 object-file: refactor freshening of objectsPatrick Steinhardt, Nov 3, 2025
  56. 12/13 object-file: rename `write_object_file()`Patrick Steinhardt, Nov 3, 2025
  57. 13/13 object-file: refactor writing objects via a streamPatrick Steinhardt, Nov 3, 2025
  58. Karthik NayakNov 3, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.