commit-graph: prepare for commit-graph chains

To prepare for a chain of commit-graph files, augment the commit_graph struct to point to a base commit_graph. As we load commits from the graph, we may actually want to read from a base file according to the graph position. The "graph position" of a commit is given by concatenating the lexicographic commit orders from each of the commit-graph files in the chain. This means that we must distinguish two values: * lexicographic index : the position within the lexicographic order in a single commit-graph file. * graph position: the position within the concatenated order of multiple commit-graph files Given the lexicographic index of a commit in a graph, we can compute the graph position by adding the number of commits in the lower-level graphs. To find the lexicographic index of a commit, we subtract the number of commits in lower-level graphs. While here, change insert_parent_or_die() to take a uint32_t position, as that is the type used by its only caller and that makes more sense with the limits in the commit-graph format. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Jun 18, 2019 at 11:14 UTC d4f4d60f6da8b93f768b2b4958c04cc1b3cea443
2 files changed +81 -11
commit-graph.c
+78 -11
@@ -359,9 +359,18 @@ int generation_numbers_enabled(struct repository *r)
359 return !!first_generation;
360 }
361
362 +static void close_commit_graph_one(struct commit_graph *g)
363 +{
364 + if (!g)
365 + return;
366 +
367 + close_commit_graph_one(g->base_graph);
368 + free_commit_graph(g);
369 +}
370 +
371 void close_commit_graph(struct raw_object_store *o)
372 {
364 - free_commit_graph(o->commit_graph);
373 + close_commit_graph_one(o->commit_graph);
374 o->commit_graph = NULL;
375 }
376
@@ -371,18 +380,38 @@ static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t
380 g->chunk_oid_lookup, g->hash_len, pos);
381 }
382
383 +static void load_oid_from_graph(struct commit_graph *g,
384 + uint32_t pos,
385 + struct object_id *oid)
386 +{
387 + uint32_t lex_index;
388 +
389 + while (g && pos < g->num_commits_in_base)
390 + g = g->base_graph;
391 +
392 + if (!g)
393 + BUG("NULL commit-graph");
394 +
395 + if (pos >= g->num_commits + g->num_commits_in_base)
396 + die(_("invalid commit position. commit-graph is likely corrupt"));
397 +
398 + lex_index = pos - g->num_commits_in_base;
399 +
400 + hashcpy(oid->hash, g->chunk_oid_lookup + g->hash_len * lex_index);
401 +}
402 +
403 static struct commit_list **insert_parent_or_die(struct repository *r,
404 struct commit_graph *g,
376 - uint64_t pos,
405 + uint32_t pos,
406 struct commit_list **pptr)
407 {
408 struct commit *c;
409 struct object_id oid;
410
382 - if (pos >= g->num_commits)
383 - die("invalid parent position %"PRIu64, pos);
411 + if (pos >= g->num_commits + g->num_commits_in_base)
412 + die("invalid parent position %"PRIu32, pos);
413
385 - hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos);
414 + load_oid_from_graph(g, pos, &oid);
415 c = lookup_commit(r, &oid);
416 if (!c)
417 die(_("could not find commit %s"), oid_to_hex(&oid));
@@ -392,7 +421,14 @@ static struct commit_list **insert_parent_or_die(struct repository *r,
421
422 static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, uint32_t pos)
423 {
395 - const unsigned char *commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * pos;
424 + const unsigned char *commit_data;
425 + uint32_t lex_index;
426 +
427 + while (pos < g->num_commits_in_base)
428 + g = g->base_graph;
429 +
430 + lex_index = pos - g->num_commits_in_base;
431 + commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
432 item->graph_pos = pos;
433 item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
434 }
@@ -405,10 +441,25 @@ static int fill_commit_in_graph(struct repository *r,
441 uint32_t *parent_data_ptr;
442 uint64_t date_low, date_high;
443 struct commit_list **pptr;
408 - const unsigned char *commit_data = g->chunk_commit_data + (g->hash_len + 16) * pos;
444 + const unsigned char *commit_data;
445 + uint32_t lex_index;
446
410 - item->object.parsed = 1;
447 + while (pos < g->num_commits_in_base)
448 + g = g->base_graph;
449 +
450 + if (pos >= g->num_commits + g->num_commits_in_base)
451 + die(_("invalid commit position. commit-graph is likely corrupt"));
452 +
453 + /*
454 + * Store the "full" position, but then use the
455 + * "local" position for the rest of the calculation.
456 + */
457 item->graph_pos = pos;
458 + lex_index = pos - g->num_commits_in_base;
459 +
460 + commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
461 +
462 + item->object.parsed = 1;
463
464 item->maybe_tree = NULL;
465
@@ -452,7 +503,18 @@ static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uin
503 *pos = item->graph_pos;
504 return 1;
505 } else {
455 - return bsearch_graph(g, &(item->object.oid), pos);
506 + struct commit_graph *cur_g = g;
507 + uint32_t lex_index;
508 +
509 + while (cur_g && !bsearch_graph(cur_g, &(item->object.oid), &lex_index))
510 + cur_g = cur_g->base_graph;
511 +
512 + if (cur_g) {
513 + *pos = lex_index + cur_g->num_commits_in_base;
514 + return 1;
515 + }
516 +
517 + return 0;
518 }
519 }
520
@@ -492,8 +554,13 @@ static struct tree *load_tree_for_commit(struct repository *r,
554 struct commit *c)
555 {
556 struct object_id oid;
495 - const unsigned char *commit_data = g->chunk_commit_data +
496 - GRAPH_DATA_WIDTH * (c->graph_pos);
557 + const unsigned char *commit_data;
558 +
559 + while (c->graph_pos < g->num_commits_in_base)
560 + g = g->base_graph;
561 +
562 + commit_data = g->chunk_commit_data +
563 + GRAPH_DATA_WIDTH * (c->graph_pos - g->num_commits_in_base);
564
565 hashcpy(oid.hash, commit_data);
566 c->maybe_tree = lookup_tree(r, &oid);
commit-graph.h
+3
@@ -48,6 +48,9 @@ struct commit_graph {
48 uint32_t num_commits;
49 struct object_id oid;
50
51 + uint32_t num_commits_in_base;
52 + struct commit_graph *base_graph;
53 +
54 const uint32_t *chunk_oid_fanout;
55 const unsigned char *chunk_oid_lookup;
56 const unsigned char *chunk_commit_data;