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

[PATCH 6/7] commit-graph: parse file format v2

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Feb 24, 2022, 20:38 UTC
Message-ID
<28fe8824ba71ff9cda5ec5c034b6a6fb3c857654.1645735117.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1163.git.1645735117.gitgitgadget@gmail.com>
From: Derrick Stolee <derrickstolee@github.com>

The commit-graph file format v2 alters how it stores the corrected commit date offsets within the Commit Data chunk instead of a separate chunk. The idea is to significantly reduce the amount of data loaded from disk while parsing the commit-graph.

We need to alter the error message when we see a file format version outside of our range now that multiple are possible. This has a non-functional side-effect of altering a use of GRAPH_VERSION within write_commit_graph().

By storing the file format version in 'struct commit_graph', we can alter the parsing code to depend on that version value. This involves changing where we look for the corrected commit date offset, but also which constants we use for jumping into the Generation Data Overflow chunk. The Commit Data chunk only has 30 bits available for the offset while the Generation Data chunk has 32 bits. This only makes a meaningful difference in very malformed repositories.

Also, we need to be careful about how we enable using corrected commit dates and generation numbers to rely upon the read_generation_data value instead of a non-zero value in the Commit Date chunk. In generation_numbers_enabled(), the first_generation variable is attemptint to look for the first topological level stored to see that it is nonzero. However, for a v2 commit-graph, this value is actually likely to be zero because the corrected commit date offset is probably zero.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
---
 commit-graph.c          | 43 +++++++++++++++++++++++++++++------------
 commit-graph.h          |  6 ++++++
 t/t5318-commit-graph.sh |  2 +-
 3 files changed, 38 insertions(+), 13 deletions(-)
diff --git a/commit-graph.c b/commit-graph.c
index b86a6a634fe..366fc4d6e41 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -49,7 +49,9 @@ void git_test_write_commit_graph_or_die(void)
 #define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
 
 #define GRAPH_VERSION_1 0x1
-#define GRAPH_VERSION GRAPH_VERSION_1
+#define GRAPH_VERSION_2 0x2
+#define GRAPH_VERSION_MIN GRAPH_VERSION_1
+#define GRAPH_VERSION_MAX GRAPH_VERSION_2
 
 #define GRAPH_EXTRA_EDGES_NEEDED 0x80000000
 #define GRAPH_EDGE_LAST_MASK 0x7fffffff
@@ -63,6 +65,7 @@ void git_test_write_commit_graph_or_die(void)
 			+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
 
 #define CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW (1ULL << 31)
+#define CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW_V3 (1ULL << 29)
 
 /* Remember to update object flag allocation in object.h */
 #define REACHABLE       (1u<<15)
@@ -358,9 +361,10 @@ struct commit_graph *parse_commit_graph(struct repository *r,
 	}
 
 	graph_version = *(unsigned char*)(data + 4);
-	if (graph_version != GRAPH_VERSION) {
-		error(_("commit-graph version %X does not match version %X"),
-		      graph_version, GRAPH_VERSION);
+	if (graph_version < GRAPH_VERSION_MIN ||
+	    graph_version > GRAPH_VERSION_MAX) {
+		error(_("commit-graph version %X is not understood"),
+		      graph_version);
 		return NULL;
 	}
 
@@ -375,6 +379,7 @@ struct commit_graph *parse_commit_graph(struct repository *r,
 
 	graph = alloc_commit_graph();
 
+	graph->version = graph_version;
 	graph->hash_len = the_hash_algo->rawsz;
 	graph->num_chunks = *(unsigned char*)(data + 6);
 	graph->data = graph_map;
@@ -402,13 +407,17 @@ struct commit_graph *parse_commit_graph(struct repository *r,
 	pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges);
 	pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs);
 
-	if (get_configured_generation_version(r) >= 2) {
-		pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
-			&graph->chunk_generation_data);
+	if (graph_version >= GRAPH_VERSION_2 ||
+	    get_configured_generation_version(r) >= 2) {
+		/* Skip this chunk if GRAPH_VERSION_2 or higher. */
+		if (graph_version == GRAPH_VERSION_1)
+			pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
+				   &graph->chunk_generation_data);
 		pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
 			&graph->chunk_generation_data_overflow);
 
-		if (graph->chunk_generation_data)
+		if (graph_version >= GRAPH_VERSION_2 ||
+		    graph->chunk_generation_data)
 			graph->read_generation_data = 1;
 	}
 
@@ -683,6 +692,9 @@ int generation_numbers_enabled(struct repository *r)
 	if (!g->num_commits)
 		return 0;
 
+	if (g->version >= GRAPH_VERSION_2)
+		return g->read_generation_data;
+
 	first_generation = get_be32(g->chunk_commit_data +
 				    g->hash_len + 8) >> 2;
 
@@ -799,13 +811,20 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
 	item->date = (timestamp_t)((date_high << 32) | date_low);
 
 	if (g->read_generation_data) {
-		offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
+		timestamp_t overflow_bit;
+		if (g->version == GRAPH_VERSION_2) {
+			offset = (timestamp_t)(get_be32(commit_data + g->hash_len + 8) >> 2);
+			overflow_bit = CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW_V3;
+		} else {
+			offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
+			overflow_bit = CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
+		}
 
-		if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) {
+		if (offset & overflow_bit) {
 			if (!g->chunk_generation_data_overflow)
 				die(_("commit-graph requires overflow generation data but has none"));
 
-			offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
+			offset_pos = offset ^ overflow_bit;
 			graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
 		} else
 			graph_data->generation = item->date + offset;
@@ -1917,7 +1936,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 
 	hashwrite_be32(f, GRAPH_SIGNATURE);
 
-	hashwrite_u8(f, GRAPH_VERSION);
+	hashwrite_u8(f, GRAPH_VERSION_1);
 	hashwrite_u8(f, oid_version());
 	hashwrite_u8(f, get_num_chunks(cf));
 	hashwrite_u8(f, ctx->num_commit_graphs_after - 1);
diff --git a/commit-graph.h b/commit-graph.h
index 04a94e18302..b379b8eae25 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -63,6 +63,12 @@ struct commit_graph {
 	const unsigned char *data;
 	size_t data_len;
 
+	/**
+	 * The 'version' byte mirrors the file format version. This is
+	 * necessary to consider when parsing commits.
+	 */
+	unsigned version;
+
 	unsigned char hash_len;
 	unsigned char num_chunks;
 	uint32_t num_commits;
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 5e4b0216fa6..a14a13e5f7b 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -605,7 +605,7 @@ test_expect_success 'detect bad signature' '
 '
 
 test_expect_success 'detect bad version' '
-	corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\02" \
+	corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\03" \
 		"graph version"
 '
 
-- 
gitgitgadget
Previous: Derrick StoleeNext: Junio C Hamano
Message 44 of 70 in “Commit-graph: Generation Number v2 Fixes, v3 implementation”
  1. 0/7 Commit-graph: Generation Number v2 Fixes, v3 implementationDerrick Stolee via GitGitGadget, Feb 24, 2022
  2. 2/7 commit-graph: fix ordering bug in generation numbersDerrick Stolee via GitGitGadget, Feb 24, 2022
  3. Junio C HamanoFeb 24, 2022
  4. Derrick StoleeFeb 25, 2022
  5. Junio C HamanoFeb 25, 2022
  6. 1/7 test-read-graph: include extra post-parse infoDerrick Stolee via GitGitGadget, Feb 24, 2022
  7. 3/7 commit-graph: start parsing generation v2 (again)Derrick Stolee via GitGitGadget, Feb 24, 2022
  8. Patrick SteinhardtFeb 28, 2022
  9. Derrick StoleeFeb 28, 2022
  10. Patrick SteinhardtFeb 28, 2022
  11. Derrick StoleeFeb 28, 2022
  12. Patrick SteinhardtMar 1, 2022
  13. Patrick SteinhardtMar 1, 2022
  14. Derrick StoleeMar 1, 2022
  15. Patrick SteinhardtMar 1, 2022
  16. Derrick StoleeMar 1, 2022
  17. Patrick SteinhardtMar 2, 2022
  18. Derrick StoleeMar 2, 2022
  19. Junio C HamanoMar 2, 2022
  20. Derrick StoleeMar 2, 2022
  21. Junio C HamanoMar 2, 2022
  22. Patrick SteinhardtMar 3, 2022
  23. Derrick StoleeMar 3, 2022
  24. Derrick StoleeMar 4, 2022
  25. Patrick SteinhardtMar 7, 2022
  26. Derrick StoleeMar 7, 2022
  27. Junio C HamanoMar 7, 2022
  28. Patrick SteinhardtMar 10, 2022
  29. Derrick StoleeMar 10, 2022
  30. 4/7 commit-graph: fix generation number v2 overflow valuesDerrick Stolee via GitGitGadget, Feb 24, 2022
  31. Junio C HamanoFeb 24, 2022
  32. Derrick StoleeFeb 25, 2022
  33. Junio C HamanoFeb 25, 2022
  34. 5/7 commit-graph: document file format v2Derrick Stolee via GitGitGadget, Feb 24, 2022
  35. Junio C HamanoFeb 24, 2022
  36. Ævar Arnfjörð BjarmasonFeb 25, 2022
  37. Derrick StoleeFeb 28, 2022
  38. Ævar Arnfjörð BjarmasonFeb 28, 2022
  39. Derrick StoleeFeb 28, 2022
  40. Ævar Arnfjörð BjarmasonFeb 28, 2022
  41. Derrick StoleeMar 1, 2022
  42. Ævar Arnfjörð BjarmasonMar 1, 2022
  43. Derrick StoleeMar 1, 2022
  44. 6/7 commit-graph: parse file format v2Derrick Stolee via GitGitGadget, Feb 24, 2022
  45. Junio C HamanoFeb 24, 2022
  46. Derrick StoleeFeb 25, 2022
  47. 7/7 commit-graph: write file format v2Derrick Stolee via GitGitGadget, Feb 24, 2022
  48. Junio C HamanoFeb 24, 2022
  49. Junio C HamanoFeb 24, 2022
  50. Derrick StoleeFeb 25, 2022
  51. 0/4 Commit-graph: Generation Number v2 FixesDerrick Stolee via GitGitGadget, Feb 28, 2022
  52. 1/4 test-read-graph: include extra post-parse infoDerrick Stolee via GitGitGadget, Feb 28, 2022
  53. Ævar Arnfjörð BjarmasonFeb 28, 2022
  54. 2/4 commit-graph: fix ordering bug in generation numbersDerrick Stolee via GitGitGadget, Feb 28, 2022
  55. Ævar Arnfjörð BjarmasonFeb 28, 2022
  56. 3/4 commit-graph: start parsing generation v2 (again)Derrick Stolee via GitGitGadget, Feb 28, 2022
  57. Ævar Arnfjörð BjarmasonFeb 28, 2022
  58. Derrick StoleeFeb 28, 2022
  59. 4/4 commit-graph: fix generation number v2 overflow valuesDerrick Stolee via GitGitGadget, Feb 28, 2022
  60. Ævar Arnfjörð BjarmasonFeb 28, 2022
  61. Ævar Arnfjörð BjarmasonMar 1, 2022
  62. 0/5 Commit-graph: Generation Number v2 FixesDerrick Stolee via GitGitGadget, Mar 1, 2022
  63. 3/5 commit-graph: fix ordering bug in generation numbersDerrick Stolee via GitGitGadget, Mar 1, 2022
  64. Junio C HamanoMar 1, 2022
  65. Junio C HamanoMar 1, 2022
  66. Derrick StoleeMar 2, 2022
  67. 5/5 commit-graph: fix generation number v2 overflow valuesDerrick Stolee via GitGitGadget, Mar 1, 2022
  68. 4/5 commit-graph: start parsing generation v2 (again)Derrick Stolee via GitGitGadget, Mar 1, 2022
  69. 1/5 test-read-graph: include extra post-parse infoDerrick Stolee via GitGitGadget, Mar 1, 2022
  70. 2/5 t5318: extract helpers to lib-commit-graph.shDerrick Stolee via GitGitGadget, Mar 1, 2022

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.