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

[PATCH 6/6] odb: move commit-graph into the object sources

From
Patrick Steinhardt <ps@pks.im>
Date
Sep 4, 2025, 12:50 UTC
Message-ID
<20250904-b4-pks-commit-graph-via-source-v1-6-d932c2481e1a@pks.im>
In-Reply-To
<20250904-b4-pks-commit-graph-via-source-v1-0-d932c2481e1a@pks.im>

Commit graphs are inherently tied to one specific object source. Furthermore, with the upcoming pluggable object sources, it is not even guaranteed that an object source may even have a commit graph as these are specific to the actual on-disk data format.

Prepare for this future by moving the commit-graph pointer from `struct object_database` to `struct odb_source`. Eventually, this will allow us to make commit graphs an implementation detail of an object source's backend.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 commit-graph.c | 65 +++++++++++++++++++++++++++++++++++++++++-----------------
 commit-graph.h |  2 +-
 odb.c          |  9 ++++----
 odb.h          |  6 +++---
 packfile.c     |  3 +--
 5 files changed, 56 insertions(+), 29 deletions(-)
diff --git a/commit-graph.c b/commit-graph.c
index 0e25b14076..9929c1ed87 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -721,11 +721,15 @@ static struct commit_graph *load_commit_graph_chain(struct odb_source *source)
 
 struct commit_graph *read_commit_graph_one(struct odb_source *source)
 {
-	struct commit_graph *g = load_commit_graph_v1(source);
+	struct commit_graph *g;
+
+	if (source->commit_graph_attempted)
+		return NULL;
+	source->commit_graph_attempted = true;
 
+	g = load_commit_graph_v1(source);
 	if (!g)
 		g = load_commit_graph_chain(source);
-
 	return g;
 }
 
@@ -737,6 +741,7 @@ struct commit_graph *read_commit_graph_one(struct odb_source *source)
  */
 static struct commit_graph *prepare_commit_graph(struct repository *r)
 {
+	bool all_attempted = true;
 	struct odb_source *source;
 
 	/*
@@ -749,9 +754,19 @@ static struct commit_graph *prepare_commit_graph(struct repository *r)
 	if (!r->gitdir || r->commit_graph_disabled)
 		return NULL;
 
-	if (r->objects->commit_graph_attempted)
-		return r->objects->commit_graph;
-	r->objects->commit_graph_attempted = 1;
+	odb_prepare_alternates(r->objects);
+	for (source = r->objects->sources; source; source = source->next) {
+		all_attempted &= source->commit_graph_attempted;
+		if (source->commit_graph)
+			return source->commit_graph;
+	}
+
+	/*
+	 * There is no point in re-trying to load commit graphs if we already
+	 * tried loading all of them beforehand.
+	 */
+	if (all_attempted)
+		return NULL;
 
 	prepare_repo_settings(r);
 
@@ -768,14 +783,16 @@ static struct commit_graph *prepare_commit_graph(struct repository *r)
 	if (!commit_graph_compatible(r))
 		return NULL;
 
-	odb_prepare_alternates(r->objects);
 	for (source = r->objects->sources; source; source = source->next) {
-		r->objects->commit_graph = read_commit_graph_one(source);
-		if (r->objects->commit_graph)
-			break;
+		if (source->commit_graph_attempted)
+			continue;
+
+		source->commit_graph = read_commit_graph_one(source);
+		if (source->commit_graph)
+			return source->commit_graph;
 	}
 
-	return r->objects->commit_graph;
+	return NULL;
 }
 
 int generation_numbers_enabled(struct repository *r)
@@ -806,7 +823,7 @@ int corrected_commit_dates_enabled(struct repository *r)
 
 struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
 {
-	struct commit_graph *g = r->objects->commit_graph;
+	struct commit_graph *g = prepare_commit_graph(r);
 	while (g) {
 		if (g->bloom_filter_settings)
 			return g->bloom_filter_settings;
@@ -815,15 +832,16 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
 	return NULL;
 }
 
-void close_commit_graph(struct object_database *o)
+void close_commit_graph(struct odb_source *source)
 {
-	if (!o->commit_graph)
+	if (!source->commit_graph)
 		return;
 
 	clear_commit_graph_data_slab(&commit_graph_data_slab);
 	deinit_bloom_filters();
-	free_commit_graph(o->commit_graph);
-	o->commit_graph = NULL;
+	free_commit_graph(source->commit_graph);
+	source->commit_graph = NULL;
+	source->commit_graph_attempted = 0;
 }
 
 static int bsearch_graph(struct commit_graph *g, const struct object_id *oid, uint32_t *pos)
@@ -1119,7 +1137,15 @@ static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
 
 struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit *c)
 {
-	return get_commit_tree_in_graph_one(r->objects->commit_graph, c);
+	struct odb_source *source;
+
+	for (source = r->objects->sources; source; source = source->next) {
+		if (!source->commit_graph)
+			continue;
+		return get_commit_tree_in_graph_one(source->commit_graph, c);
+	}
+
+	return NULL;
 }
 
 struct packed_commit_list {
@@ -2165,7 +2191,8 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 		ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 2] = new_base_hash;
 	}
 
-	close_commit_graph(ctx->r->objects);
+	for (struct odb_source *s = ctx->r->objects->sources; s; s = s->next)
+		close_commit_graph(s);
 	finalize_hashfile(f, file_hash, FSYNC_COMPONENT_COMMIT_GRAPH,
 			  CSUM_HASH_IN_STREAM | CSUM_FSYNC);
 	free_chunkfile(cf);
@@ -2667,8 +2694,8 @@ int write_commit_graph(struct odb_source *source,
 	oid_array_clear(&ctx.oids);
 	clear_topo_level_slab(&topo_levels);
 
-	if (ctx.r->objects->commit_graph) {
-		struct commit_graph *g = ctx.r->objects->commit_graph;
+	if (source->commit_graph) {
+		struct commit_graph *g = source->commit_graph;
 
 		while (g) {
 			g->topo_levels = NULL;
diff --git a/commit-graph.h b/commit-graph.h
index f6a5433641..33cb6a7577 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -185,7 +185,7 @@ int write_commit_graph(struct odb_source *source,
 
 int verify_commit_graph(struct commit_graph *g, int flags);
 
-void close_commit_graph(struct object_database *);
+void close_commit_graph(struct odb_source *);
 void free_commit_graph(struct commit_graph *);
 
 /*
diff --git a/odb.c b/odb.c
index 2a92a018c4..fdcc6849a8 100644
--- a/odb.c
+++ b/odb.c
@@ -363,6 +363,11 @@ static void free_object_directory(struct odb_source *source)
 	free(source->path);
 	odb_clear_loose_cache(source);
 	loose_object_map_clear(&source->loose_map);
+
+	free_commit_graph(source->commit_graph);
+	source->commit_graph = NULL;
+	source->commit_graph_attempted = 0;
+
 	free(source);
 }
 
@@ -1023,10 +1028,6 @@ void odb_clear(struct object_database *o)
 	oidmap_clear(&o->replace_map, 1);
 	pthread_mutex_destroy(&o->replace_mutex);
 
-	free_commit_graph(o->commit_graph);
-	o->commit_graph = NULL;
-	o->commit_graph_attempted = 0;
-
 	free_object_directories(o);
 	o->sources_tail = NULL;
 	o->loaded_alternates = 0;
diff --git a/odb.h b/odb.h
index 3dfc66d75a..a4835db685 100644
--- a/odb.h
+++ b/odb.h
@@ -63,6 +63,9 @@ struct odb_source {
 	 */
 	struct multi_pack_index *midx;
 
+	struct commit_graph *commit_graph;
+	bool commit_graph_attempted; /* if loading has been attempted */
+
 	/*
 	 * This is a temporary object store created by the tmp_objdir
 	 * facility. Disable ref updates since the objects in the store
@@ -120,9 +123,6 @@ struct object_database {
 	unsigned replace_map_initialized : 1;
 	pthread_mutex_t replace_mutex; /* protect object replace functions */
 
-	struct commit_graph *commit_graph;
-	unsigned commit_graph_attempted : 1; /* if loading has been attempted */
-
 	/*
 	 * private data
 	 *
diff --git a/packfile.c b/packfile.c
index 5d73932f50..5d3a25816f 100644
--- a/packfile.c
+++ b/packfile.c
@@ -374,9 +374,8 @@ void close_object_store(struct object_database *o)
 		if (source->midx)
 			close_midx(source->midx);
 		source->midx = NULL;
+		close_commit_graph(source);
 	}
-
-	close_commit_graph(o);
 }
 
 void unlink_pack_path(const char *pack_name, int force_delete)
-- 
2.51.0.417.g1ba7204a04.dirty
Previous: Patrick SteinhardtNext: Taylor Blau
Message 11 of 26 in “odb: track commit graphs via object source”
  1. 0/6 odb: track commit graphs via object sourcePatrick Steinhardt, Sep 4, 2025
  2. 1/6 blame: drop explicit check for commit graphPatrick Steinhardt, Sep 4, 2025
  3. Taylor BlauSep 11, 2025
  4. 2/6 revision: drop explicit check for commit graphPatrick Steinhardt, Sep 4, 2025
  5. Taylor BlauSep 11, 2025
  6. 3/6 commit-graph: return the prepared commit graph from `prepare_commit_graph()`Patrick Steinhardt, Sep 4, 2025
  7. Taylor BlauSep 11, 2025
  8. 4/6 commit-graph: return commit graph from `repo_find_commit_pos_in_graph()`Patrick Steinhardt, Sep 4, 2025
  9. Taylor BlauSep 11, 2025
  10. 5/6 commit-graph: pass graphs that are to be merged as parameterPatrick Steinhardt, Sep 4, 2025
  11. 6/6 odb: move commit-graph into the object sourcesPatrick Steinhardt, Sep 4, 2025
  12. Taylor BlauSep 11, 2025
  13. Junio C HamanoSep 4, 2025
  14. Derrick StoleeSep 5, 2025
  15. Patrick SteinhardtSep 8, 2025
  16. Derrick StoleeSep 8, 2025
  17. Patrick SteinhardtSep 10, 2025
  18. Junio C HamanoSep 25, 2025
  19. Patrick SteinhardtSep 26, 2025
  20. Patrick SteinhardtOct 2, 2025
  21. Patrick SteinhardtOct 2, 2025
  22. Junio C HamanoOct 2, 2025
  23. Derrick StoleeOct 3, 2025
  24. Taylor BlauSep 11, 2025
  25. Junio C HamanoSep 4, 2025
  26. Patrick SteinhardtSep 5, 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.