Skip to content

Commit cb797e2

Browse files
commit-graph: implement generation data chunk
As discovered by Ævar, we cannot increment graph version to distinguish between generation numbers v1 and v2 [1]. Thus, one of pre-requistes before implementing generation number was to distinguish between graph versions in a backwards compatible manner. We are going to introduce a new chunk called Generation Data chunk (or GDAT). GDAT stores generation number v2 (and any subsequent versions), whereas CDAT will still store topological level. Old Git does not understand GDAT chunk and would ignore it, reading topological levels from CDAT. New Git can parse GDAT and take advantage of newer generation numbers, falling back to topological levels when GDAT chunk is missing (as it would happen with a commit graph written by old Git). We introduce a test environment variable 'GIT_TEST_COMMIT_GRAPH_NO_GDAT' which forces commit-graph file to be written without generation data chunk to emulate a commit-graph file written by old Git. [1]: https://lore.kernel.org/git/[email protected]/ Signed-off-by: Abhishek Kumar <[email protected]>
1 parent b254782 commit cb797e2

File tree

8 files changed

+99
-53
lines changed

8 files changed

+99
-53
lines changed

‎commit-graph.c‎

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ void git_test_write_commit_graph_or_die(void)
3838
#define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
3939
#define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
4040
#define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
41+
#define GRAPH_CHUNKID_GENERATION_DATA 0x47444154 /* "GDAT" */
4142
#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
4243
#define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
4344
#define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
4445
#define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */
45-
#define MAX_NUM_CHUNKS 7
46+
#define MAX_NUM_CHUNKS 8
4647

4748
#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
4849

@@ -392,6 +393,13 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
392393
graph->chunk_commit_data = data + chunk_offset;
393394
break;
394395

396+
case GRAPH_CHUNKID_GENERATION_DATA:
397+
if (graph->chunk_generation_data)
398+
chunk_repeated = 1;
399+
else
400+
graph->chunk_generation_data = data + chunk_offset;
401+
break;
402+
395403
case GRAPH_CHUNKID_EXTRAEDGES:
396404
if (graph->chunk_extra_edges)
397405
chunk_repeated = 1;
@@ -758,7 +766,10 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
758766
date_low = get_be32(commit_data + g->hash_len + 12);
759767
item->date = (timestamp_t)((date_high << 32) | date_low);
760768

761-
graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
769+
if (g->chunk_generation_data)
770+
graph_data->generation = get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
771+
else
772+
graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
762773
}
763774

764775
static inline void set_commit_tree(struct commit *c, struct tree *t)
@@ -951,7 +962,8 @@ struct write_commit_graph_context {
951962
report_progress:1,
952963
split:1,
953964
changed_paths:1,
954-
order_by_pack:1;
965+
order_by_pack:1,
966+
write_generation_data:1;
955967

956968
const struct split_commit_graph_opts *split_opts;
957969
size_t total_bloom_filter_data_size;
@@ -1105,8 +1117,21 @@ static int write_graph_chunk_data(struct hashfile *f,
11051117
return 0;
11061118
}
11071119

1120+
static int write_graph_chunk_generation_data(struct hashfile *f,
1121+
struct write_commit_graph_context *ctx)
1122+
{
1123+
int i;
1124+
for (i = 0; i < ctx->commits.nr; i++) {
1125+
struct commit *c = ctx->commits.list[i];
1126+
display_progress(ctx->progress, ++ctx->progress_cnt);
1127+
hashwrite_be32(f, commit_graph_data_at(c)->generation);
1128+
}
1129+
1130+
return 0;
1131+
}
1132+
11081133
static int write_graph_chunk_extra_edges(struct hashfile *f,
1109-
struct write_commit_graph_context *ctx)
1134+
struct write_commit_graph_context *ctx)
11101135
{
11111136
struct commit **list = ctx->commits.list;
11121137
struct commit **last = ctx->commits.list + ctx->commits.nr;
@@ -1710,6 +1735,12 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
17101735
chunks[2].id = GRAPH_CHUNKID_DATA;
17111736
chunks[2].size = (hashsz + 16) * ctx->commits.nr;
17121737
chunks[2].write_fn = write_graph_chunk_data;
1738+
if (ctx->write_generation_data) {
1739+
chunks[num_chunks].id = GRAPH_CHUNKID_GENERATION_DATA;
1740+
chunks[num_chunks].size = sizeof(uint32_t) * ctx->commits.nr;
1741+
chunks[num_chunks].write_fn = write_graph_chunk_generation_data;
1742+
num_chunks++;
1743+
}
17131744
if (ctx->num_extra_edges) {
17141745
chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
17151746
chunks[num_chunks].size = 4 * ctx->num_extra_edges;
@@ -2113,6 +2144,7 @@ int write_commit_graph(struct object_directory *odb,
21132144
ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
21142145
ctx->split_opts = split_opts;
21152146
ctx->total_bloom_filter_data_size = 0;
2147+
ctx->write_generation_data = !git_env_bool(GIT_TEST_COMMIT_GRAPH_NO_GDAT, 0);
21162148

21172149
if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
21182150
ctx->changed_paths = 1;

‎commit-graph.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "oidset.h"
77

88
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
9+
#define GIT_TEST_COMMIT_GRAPH_NO_GDAT "GIT_TEST_COMMIT_GRAPH_NO_GDAT"
910
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
1011
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
1112

@@ -67,6 +68,7 @@ struct commit_graph {
6768
const uint32_t *chunk_oid_fanout;
6869
const unsigned char *chunk_oid_lookup;
6970
const unsigned char *chunk_commit_data;
71+
const unsigned char *chunk_generation_data;
7072
const unsigned char *chunk_extra_edges;
7173
const unsigned char *chunk_base_graphs;
7274
const unsigned char *chunk_bloom_indexes;

‎t/README‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
379379
be written after every 'git commit' command, and overrides the
380380
'core.commitGraph' setting to true.
381381

382+
GIT_TEST_COMMIT_GRAPH_NO_GDAT=<boolean>, when true, forces the
383+
commit-graph to be written without generation data chunk.
384+
382385
GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces
383386
commit-graph write to compute and write changed path Bloom filters for
384387
every 'git commit-graph write', as if the `--changed-paths` option was

‎t/helper/test-read-graph.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ int cmd__read_graph(int argc, const char **argv)
3232
printf(" oid_lookup");
3333
if (graph->chunk_commit_data)
3434
printf(" commit_metadata");
35+
if (graph->chunk_generation_data)
36+
printf(" generation_data");
3537
if (graph->chunk_extra_edges)
3638
printf(" extra_edges");
3739
if (graph->chunk_bloom_indexes)

‎t/t4216-log-bloom.sh‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ test_expect_success 'setup test - repo, commits, commit graph, log outputs' '
3333
git commit-graph write --reachable --changed-paths
3434
'
3535
graph_read_expect () {
36-
NUM_CHUNKS=5
36+
NUM_CHUNKS=6
3737
cat >expect <<- EOF
3838
header: 43475048 1 1 $NUM_CHUNKS 0
3939
num_commits: $1
40-
chunks: oid_fanout oid_lookup commit_metadata bloom_indexes bloom_data
40+
chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
4141
EOF
4242
test-tool read-graph >actual &&
4343
test_cmp expect actual

‎t/t5318-commit-graph.sh‎

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ graph_git_behavior 'no graph' full commits/3 commits/1
7272
graph_read_expect() {
7373
OPTIONAL=""
7474
NUM_CHUNKS=3
75-
if test ! -z $2
75+
if test ! -z "$2"
7676
then
7777
OPTIONAL=" $2"
7878
NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
@@ -99,14 +99,14 @@ test_expect_success 'exit with correct error on bad input to --stdin-commits' '
9999
# valid commit and tree OID
100100
git rev-parse HEAD HEAD^{tree} >in &&
101101
git commit-graph write --stdin-commits <in &&
102-
graph_read_expect 3
102+
graph_read_expect 3 generation_data
103103
'
104104

105105
test_expect_success 'write graph' '
106106
cd "$TRASH_DIRECTORY/full" &&
107107
git commit-graph write &&
108108
test_path_is_file $objdir/info/commit-graph &&
109-
graph_read_expect "3"
109+
graph_read_expect "3" generation_data
110110
'
111111

112112
test_expect_success POSIXPERM 'write graph has correct permissions' '
@@ -215,7 +215,7 @@ test_expect_success 'write graph with merges' '
215215
cd "$TRASH_DIRECTORY/full" &&
216216
git commit-graph write &&
217217
test_path_is_file $objdir/info/commit-graph &&
218-
graph_read_expect "10" "extra_edges"
218+
graph_read_expect "10" "generation_data extra_edges"
219219
'
220220

221221
graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -250,7 +250,7 @@ test_expect_success 'write graph with new commit' '
250250
cd "$TRASH_DIRECTORY/full" &&
251251
git commit-graph write &&
252252
test_path_is_file $objdir/info/commit-graph &&
253-
graph_read_expect "11" "extra_edges"
253+
graph_read_expect "11" "generation_data extra_edges"
254254
'
255255

256256
graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -260,7 +260,7 @@ test_expect_success 'write graph with nothing new' '
260260
cd "$TRASH_DIRECTORY/full" &&
261261
git commit-graph write &&
262262
test_path_is_file $objdir/info/commit-graph &&
263-
graph_read_expect "11" "extra_edges"
263+
graph_read_expect "11" "generation_data extra_edges"
264264
'
265265

266266
graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -270,7 +270,7 @@ test_expect_success 'build graph from latest pack with closure' '
270270
cd "$TRASH_DIRECTORY/full" &&
271271
cat new-idx | git commit-graph write --stdin-packs &&
272272
test_path_is_file $objdir/info/commit-graph &&
273-
graph_read_expect "9" "extra_edges"
273+
graph_read_expect "9" "generation_data extra_edges"
274274
'
275275

276276
graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
@@ -283,7 +283,7 @@ test_expect_success 'build graph from commits with closure' '
283283
git rev-parse merge/1 >>commits-in &&
284284
cat commits-in | git commit-graph write --stdin-commits &&
285285
test_path_is_file $objdir/info/commit-graph &&
286-
graph_read_expect "6"
286+
graph_read_expect "6" "generation_data"
287287
'
288288

289289
graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
@@ -293,7 +293,7 @@ test_expect_success 'build graph from commits with append' '
293293
cd "$TRASH_DIRECTORY/full" &&
294294
git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
295295
test_path_is_file $objdir/info/commit-graph &&
296-
graph_read_expect "10" "extra_edges"
296+
graph_read_expect "10" "generation_data extra_edges"
297297
'
298298

299299
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -303,7 +303,7 @@ test_expect_success 'build graph using --reachable' '
303303
cd "$TRASH_DIRECTORY/full" &&
304304
git commit-graph write --reachable &&
305305
test_path_is_file $objdir/info/commit-graph &&
306-
graph_read_expect "11" "extra_edges"
306+
graph_read_expect "11" "generation_data extra_edges"
307307
'
308308

309309
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -324,7 +324,7 @@ test_expect_success 'write graph in bare repo' '
324324
cd "$TRASH_DIRECTORY/bare" &&
325325
git commit-graph write &&
326326
test_path_is_file $baredir/info/commit-graph &&
327-
graph_read_expect "11" "extra_edges"
327+
graph_read_expect "11" "generation_data extra_edges"
328328
'
329329

330330
graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
@@ -421,8 +421,9 @@ test_expect_success 'replace-objects invalidates commit-graph' '
421421

422422
test_expect_success 'git commit-graph verify' '
423423
cd "$TRASH_DIRECTORY/full" &&
424-
git rev-parse commits/8 | git commit-graph write --stdin-commits &&
425-
git commit-graph verify >output
424+
git rev-parse commits/8 | GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --stdin-commits &&
425+
git commit-graph verify >output &&
426+
graph_read_expect 9 extra_edges
426427
'
427428

428429
NUM_COMMITS=9

‎t/t5324-split-commit-graph.sh‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ test_expect_success 'setup repo' '
1414
graphdir="$infodir/commit-graphs" &&
1515
test_oid_init &&
1616
test_oid_cache <<-EOM
17-
shallow sha1:1760
18-
shallow sha256:2064
17+
shallow sha1:2132
18+
shallow sha256:2436
1919
20-
base sha1:1376
21-
base sha256:1496
20+
base sha1:1408
21+
base sha256:1528
2222
EOM
2323
'
2424

@@ -29,9 +29,9 @@ graph_read_expect() {
2929
NUM_BASE=$2
3030
fi
3131
cat >expect <<- EOF
32-
header: 43475048 1 1 3 $NUM_BASE
32+
header: 43475048 1 1 4 $NUM_BASE
3333
num_commits: $1
34-
chunks: oid_fanout oid_lookup commit_metadata
34+
chunks: oid_fanout oid_lookup commit_metadata generation_data
3535
EOF
3636
test-tool read-graph >output &&
3737
test_cmp expect output

0 commit comments

Comments
 (0)