pack-bitmap: add function to iterate over filtered bitmapped objects

Introduce a function that allows the caller to iterate over all bitmapped objects that match a given filter. This mechanism will be used in a subsequent commit to optimize object filters in git-cat-file(1). Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Apr 2, 2025 at 13:13 UTC 5420901bde8043a298b8ae5e5b3c3cfc1b67039b
2 files changed +65 -6
pack-bitmap.c
+53 -6
@@ -1662,6 +1662,7 @@ static void init_type_iterator(struct ewah_or_iterator *it,
1662
1663 static void show_objects_for_type(
1664 struct bitmap_index *bitmap_git,
1665 + struct bitmap *objects,
1666 enum object_type object_type,
1667 show_reachable_fn show_reach,
1668 void *payload)
@@ -1672,8 +1673,6 @@ static void show_objects_for_type(
1673 struct ewah_or_iterator it;
1674 eword_t filter;
1675
1675 - struct bitmap *objects = bitmap_git->result;
1676 -
1676 init_type_iterator(&it, bitmap_git, object_type);
1677
1678 for (i = 0; i < objects->word_alloc &&
@@ -2025,6 +2024,50 @@ static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git,
2024 }
2025 }
2026
2027 +int for_each_bitmapped_object(struct bitmap_index *bitmap_git,
2028 + struct list_objects_filter_options *filter,
2029 + show_reachable_fn show_reach,
2030 + void *payload)
2031 +{
2032 + struct bitmap *filtered_bitmap = NULL;
2033 + uint32_t objects_nr;
2034 + size_t full_word_count;
2035 + int ret;
2036 +
2037 + if (!can_filter_bitmap(filter)) {
2038 + ret = -1;
2039 + goto out;
2040 + }
2041 +
2042 + objects_nr = bitmap_num_objects(bitmap_git);
2043 + full_word_count = objects_nr / BITS_IN_EWORD;
2044 +
2045 + /* We start from the all-1 bitmap and then filter down from there. */
2046 + filtered_bitmap = bitmap_word_alloc(full_word_count + !!(objects_nr % BITS_IN_EWORD));
2047 + memset(filtered_bitmap->words, 0xff, full_word_count * sizeof(*filtered_bitmap->words));
2048 + for (size_t i = full_word_count * BITS_IN_EWORD; i < objects_nr; i++)
2049 + bitmap_set(filtered_bitmap, i);
2050 +
2051 + if (filter_bitmap(bitmap_git, NULL, filtered_bitmap, filter) < 0) {
2052 + ret = -1;
2053 + goto out;
2054 + }
2055 +
2056 + show_objects_for_type(bitmap_git, filtered_bitmap,
2057 + OBJ_COMMIT, show_reach, payload);
2058 + show_objects_for_type(bitmap_git, filtered_bitmap,
2059 + OBJ_TREE, show_reach, payload);
2060 + show_objects_for_type(bitmap_git, filtered_bitmap,
2061 + OBJ_BLOB, show_reach, payload);
2062 + show_objects_for_type(bitmap_git, filtered_bitmap,
2063 + OBJ_TAG, show_reach, payload);
2064 +
2065 + ret = 0;
2066 +out:
2067 + bitmap_free(filtered_bitmap);
2068 + return ret;
2069 +}
2070 +
2071 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
2072 int filter_provided_objects)
2073 {
@@ -2519,13 +2562,17 @@ void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,
2562 {
2563 assert(bitmap_git->result);
2564
2522 - show_objects_for_type(bitmap_git, OBJ_COMMIT, show_reachable, NULL);
2565 + show_objects_for_type(bitmap_git, bitmap_git->result,
2566 + OBJ_COMMIT, show_reachable, NULL);
2567 if (revs->tree_objects)
2524 - show_objects_for_type(bitmap_git, OBJ_TREE, show_reachable, NULL);
2568 + show_objects_for_type(bitmap_git, bitmap_git->result,
2569 + OBJ_TREE, show_reachable, NULL);
2570 if (revs->blob_objects)
2526 - show_objects_for_type(bitmap_git, OBJ_BLOB, show_reachable, NULL);
2571 + show_objects_for_type(bitmap_git, bitmap_git->result,
2572 + OBJ_BLOB, show_reachable, NULL);
2573 if (revs->tag_objects)
2528 - show_objects_for_type(bitmap_git, OBJ_TAG, show_reachable, NULL);
2574 + show_objects_for_type(bitmap_git, bitmap_git->result,
2575 + OBJ_TAG, show_reachable, NULL);
2576
2577 show_extended_objects(bitmap_git, revs, show_reachable);
2578 }
pack-bitmap.h
+12
@@ -79,6 +79,18 @@ int test_bitmap_pseudo_merges(struct repository *r);
79 int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n);
80 int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n);
81
82 +struct list_objects_filter_options;
83 +
84 +/*
85 + * Filter bitmapped objects and iterate through all resulting objects,
86 + * executing `show_reach` for each of them. Returns `-1` in case the filter is
87 + * not supported, `0` otherwise.
88 + */
89 +int for_each_bitmapped_object(struct bitmap_index *bitmap_git,
90 + struct list_objects_filter_options *filter,
91 + show_reachable_fn show_reach,
92 + void *payload);
93 +
94 #define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
95 "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
96