pack-bitmap.c: teach `rev-list --test-bitmap` about incremental MIDXs

Implement support for the special `--test-bitmap` mode of `git rev-list` when using incremental MIDXs. The bitmap_test_data structure is extended to contain a "base" pointer that mirrors the structure of the bitmap chain that it is being used to test. When we find a commit to test, we first chase down the ->base pointer to find the appropriate bitmap_test_data for the bitmap layer that the given commit is contained within, and then perform the test on that bitmap. In order to implement this, light modifications are made to bitmap_for_commit() to reimplement it in terms of a new function, find_bitmap_for_commit(), which fills out a pointer which indicates the bitmap layer which contains the given commit. Signed-off-by: Taylor Blau <me@ttaylorr.com> Acked-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed Mar 20, 2025 at 13:56 UTC faab03f9a16cbca85415b73504eb95e6b8ed3d6b
1 file changed +86 -21
pack-bitmap.c
+86 -21
@@ -938,8 +938,9 @@ corrupt:
938 return NULL;
939 }
940
941 -struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
942 - struct commit *commit)
941 +static struct ewah_bitmap *find_bitmap_for_commit(struct bitmap_index *bitmap_git,
942 + struct commit *commit,
943 + struct bitmap_index **found)
944 {
945 khiter_t hash_pos;
946 if (!bitmap_git)
@@ -949,18 +950,30 @@ struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
950 if (hash_pos >= kh_end(bitmap_git->bitmaps)) {
951 struct stored_bitmap *bitmap = NULL;
952 if (!bitmap_git->table_lookup)
952 - return bitmap_for_commit(bitmap_git->base, commit);
953 + return find_bitmap_for_commit(bitmap_git->base, commit,
954 + found);
955
956 /* this is a fairly hot codepath - no trace2_region please */
957 /* NEEDSWORK: cache misses aren't recorded */
958 bitmap = lazy_bitmap_for_commit(bitmap_git, commit);
959 if (!bitmap)
958 - return bitmap_for_commit(bitmap_git->base, commit);
960 + return find_bitmap_for_commit(bitmap_git->base, commit,
961 + found);
962 + if (found)
963 + *found = bitmap_git;
964 return lookup_stored_bitmap(bitmap);
965 }
966 + if (found)
967 + *found = bitmap_git;
968 return lookup_stored_bitmap(kh_value(bitmap_git->bitmaps, hash_pos));
969 }
970
971 +struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
972 + struct commit *commit)
973 +{
974 + return find_bitmap_for_commit(bitmap_git, commit, NULL);
975 +}
976 +
977 static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
978 const struct object_id *oid)
979 {
@@ -2513,6 +2526,8 @@ struct bitmap_test_data {
2526 struct bitmap *tags;
2527 struct progress *prg;
2528 size_t seen;
2529 +
2530 + struct bitmap_test_data *base_tdata;
2531 };
2532
2533 static void test_bitmap_type(struct bitmap_test_data *tdata,
@@ -2521,6 +2536,11 @@ static void test_bitmap_type(struct bitmap_test_data *tdata,
2536 enum object_type bitmap_type = OBJ_NONE;
2537 int bitmaps_nr = 0;
2538
2539 + if (bitmap_is_midx(tdata->bitmap_git)) {
2540 + while (pos < tdata->bitmap_git->midx->num_objects_in_base)
2541 + tdata = tdata->base_tdata;
2542 + }
2543 +
2544 if (bitmap_get(tdata->commits, pos)) {
2545 bitmap_type = OBJ_COMMIT;
2546 bitmaps_nr++;
@@ -2584,13 +2604,57 @@ static void test_show_commit(struct commit *commit, void *data)
2604 display_progress(tdata->prg, ++tdata->seen);
2605 }
2606
2607 +static uint32_t bitmap_total_entry_count(struct bitmap_index *bitmap_git)
2608 +{
2609 + uint32_t total = 0;
2610 + do {
2611 + total = st_add(total, bitmap_git->entry_count);
2612 + bitmap_git = bitmap_git->base;
2613 + } while (bitmap_git);
2614 +
2615 + return total;
2616 +}
2617 +
2618 +static void bitmap_test_data_prepare(struct bitmap_test_data *tdata,
2619 + struct bitmap_index *bitmap_git)
2620 +{
2621 + memset(tdata, 0, sizeof(struct bitmap_test_data));
2622 +
2623 + tdata->bitmap_git = bitmap_git;
2624 + tdata->base = bitmap_new();
2625 + tdata->commits = ewah_to_bitmap(bitmap_git->commits);
2626 + tdata->trees = ewah_to_bitmap(bitmap_git->trees);
2627 + tdata->blobs = ewah_to_bitmap(bitmap_git->blobs);
2628 + tdata->tags = ewah_to_bitmap(bitmap_git->tags);
2629 +
2630 + if (bitmap_git->base) {
2631 + tdata->base_tdata = xmalloc(sizeof(struct bitmap_test_data));
2632 + bitmap_test_data_prepare(tdata->base_tdata, bitmap_git->base);
2633 + }
2634 +}
2635 +
2636 +static void bitmap_test_data_release(struct bitmap_test_data *tdata)
2637 +{
2638 + if (!tdata)
2639 + return;
2640 +
2641 + bitmap_test_data_release(tdata->base_tdata);
2642 + free(tdata->base_tdata);
2643 +
2644 + bitmap_free(tdata->base);
2645 + bitmap_free(tdata->commits);
2646 + bitmap_free(tdata->trees);
2647 + bitmap_free(tdata->blobs);
2648 + bitmap_free(tdata->tags);
2649 +}
2650 +
2651 void test_bitmap_walk(struct rev_info *revs)
2652 {
2653 struct object *root;
2654 struct bitmap *result = NULL;
2655 size_t result_popcnt;
2656 struct bitmap_test_data tdata;
2593 - struct bitmap_index *bitmap_git;
2657 + struct bitmap_index *bitmap_git, *found;
2658 struct ewah_bitmap *bm;
2659
2660 if (!(bitmap_git = prepare_bitmap_git(revs->repo)))
@@ -2599,17 +2663,28 @@ void test_bitmap_walk(struct rev_info *revs)
2663 if (revs->pending.nr != 1)
2664 die(_("you must specify exactly one commit to test"));
2665
2602 - fprintf_ln(stderr, "Bitmap v%d test (%d entries%s)",
2666 + fprintf_ln(stderr, "Bitmap v%d test (%d entries%s, %d total)",
2667 bitmap_git->version,
2668 bitmap_git->entry_count,
2605 - bitmap_git->table_lookup ? "" : " loaded");
2669 + bitmap_git->table_lookup ? "" : " loaded",
2670 + bitmap_total_entry_count(bitmap_git));
2671
2672 root = revs->pending.objects[0].item;
2608 - bm = bitmap_for_commit(bitmap_git, (struct commit *)root);
2673 + bm = find_bitmap_for_commit(bitmap_git, (struct commit *)root, &found);
2674
2675 if (bm) {
2676 fprintf_ln(stderr, "Found bitmap for '%s'. %d bits / %08x checksum",
2612 - oid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));
2677 + oid_to_hex(&root->oid),
2678 + (int)bm->bit_size, ewah_checksum(bm));
2679 +
2680 + if (bitmap_is_midx(found))
2681 + fprintf_ln(stderr, "Located via MIDX '%s'.",
2682 + hash_to_hex_algop(get_midx_checksum(found->midx),
2683 + revs->repo->hash_algo));
2684 + else
2685 + fprintf_ln(stderr, "Located via pack '%s'.",
2686 + hash_to_hex_algop(found->pack->hash,
2687 + revs->repo->hash_algo));
2688
2689 result = ewah_to_bitmap(bm);
2690 }
@@ -2626,16 +2701,10 @@ void test_bitmap_walk(struct rev_info *revs)
2701 if (prepare_revision_walk(revs))
2702 die(_("revision walk setup failed"));
2703
2629 - tdata.bitmap_git = bitmap_git;
2630 - tdata.base = bitmap_new();
2631 - tdata.commits = ewah_to_bitmap(bitmap_git->commits);
2632 - tdata.trees = ewah_to_bitmap(bitmap_git->trees);
2633 - tdata.blobs = ewah_to_bitmap(bitmap_git->blobs);
2634 - tdata.tags = ewah_to_bitmap(bitmap_git->tags);
2704 + bitmap_test_data_prepare(&tdata, bitmap_git);
2705 tdata.prg = start_progress(revs->repo,
2706 "Verifying bitmap entries",
2707 result_popcnt);
2638 - tdata.seen = 0;
2708
2709 traverse_commit_list(revs, &test_show_commit, &test_show_object, &tdata);
2710
@@ -2647,11 +2716,7 @@ void test_bitmap_walk(struct rev_info *revs)
2716 die(_("mismatch in bitmap results"));
2717
2718 bitmap_free(result);
2650 - bitmap_free(tdata.base);
2651 - bitmap_free(tdata.commits);
2652 - bitmap_free(tdata.trees);
2653 - bitmap_free(tdata.blobs);
2654 - bitmap_free(tdata.tags);
2719 + bitmap_test_data_release(&tdata);
2720 free_bitmap_index(bitmap_git);
2721 }
2722