diff --git a/odb.h b/odb.h index a1e222f605..67d0b34942 100644 --- a/odb.h +++ b/odb.h @@ -8,6 +8,7 @@ #include "thread-utils.h" struct cached_object_entry; +struct list_objects_filter_options; struct odb_source_inmemory; struct packed_git; struct repository; @@ -490,6 +491,17 @@ struct odb_for_each_object_options { */ const struct object_id *prefix; size_t prefix_hex_len; + + /* + * Optional object filter that allows backends to skip yielding + * objects that are excluded by the filter as an optimization. The + * filter is a best-effort hint: backends may use it to skip + * excluded objects (e.g. by consulting a reachability bitmap), but + * are also free to ignore it entirely and yield every object. As a + * consequence, callers must re-apply the filter on yielded objects + * if they require strict filtering semantics. + */ + const struct list_objects_filter_options *filter; }; /* diff --git a/odb/source-packed.c b/odb/source-packed.c index 9cfa02b7a2..4777395053 100644 --- a/odb/source-packed.c +++ b/odb/source-packed.c @@ -3,11 +3,13 @@ #include "chdir-notify.h" #include "dir.h" #include "git-zlib.h" +#include "list-objects-filter-options.h" #include "mergesort.h" #include "midx.h" #include "odb/source-packed.h" #include "odb/streaming.h" #include "packfile.h" +#include "pack-bitmap.h" static int find_pack_entry(struct odb_source_packed *store, const struct object_id *oid, @@ -315,6 +317,37 @@ out: return ret; } +struct bitmapped_for_each_object_data { + struct odb_source_packed *packed; + const struct object_info *request; + const struct odb_for_each_object_options *opts; + odb_for_each_object_cb cb; + void *cb_data; +}; + +static int bitmapped_for_each_object(const struct object_id *oid, + enum object_type type UNUSED, + int flags UNUSED, + uint32_t hash UNUSED, + struct packed_git *pack, + off_t offset, + void *cb_data) +{ + struct bitmapped_for_each_object_data *data = cb_data; + + if (should_exclude_pack(pack, data->opts->flags)) + return 0; + + if (data->request) { + struct object_info oi = *data->request; + if (packed_object_info(data->packed, pack, offset, &oi) < 0) + return -1; + return data->cb(oid, &oi, data->cb_data); + } + + return data->cb(oid, NULL, data->cb_data); +} + static int odb_source_packed_for_each_object(struct odb_source *source, const struct object_info *request, odb_for_each_object_cb cb, @@ -328,12 +361,33 @@ static int odb_source_packed_for_each_object(struct odb_source *source, .cb = cb, .cb_data = cb_data, }; + struct bitmap_index *bitmap = NULL; struct packfile_list_entry *e; int pack_errors = 0, ret; if (opts->prefix) return odb_source_packed_for_each_prefixed_object(packed, opts, &data); + if (opts->filter && + opts->filter->choice != LOFC_DISABLED && + can_filter_bitmap(opts->filter)) + bitmap = prepare_bitmap_git_for_source(packed); + if (bitmap) { + struct bitmapped_for_each_object_data bitmap_data = { + .packed = packed, + .request = request, + .opts = opts, + .cb = cb, + .cb_data = cb_data, + }; + + ret = for_each_bitmapped_object(bitmap, opts->filter, + bitmapped_for_each_object, + &bitmap_data); + if (ret) + goto out; + } + packed->skip_mru_updates = true; for (e = packfile_store_get_packs(packed); e; e = e->next) { @@ -342,6 +396,13 @@ static int odb_source_packed_for_each_object(struct odb_source *source, if (should_exclude_pack(p, opts->flags)) continue; + /* + * Objects covered by the bitmap have already been yielded + * above; skip them here to avoid duplicates. + */ + if (bitmap && bitmap_index_contains_pack(bitmap, p)) + continue; + if (open_pack_index(p)) { pack_errors = 1; continue; @@ -357,6 +418,7 @@ static int odb_source_packed_for_each_object(struct odb_source *source, out: packed->skip_mru_updates = false; + free_bitmap_index(bitmap); if (!ret && pack_errors) ret = -1; diff --git a/pack-bitmap.c b/pack-bitmap.c index 09ba15d26b..f55a0859ea 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -2039,12 +2039,11 @@ static int filter_bitmap(struct bitmap_index *bitmap_git, return -1; } -static int can_filter_bitmap(const struct list_objects_filter_options *filter) +bool can_filter_bitmap(const struct list_objects_filter_options *filter) { return !filter_bitmap(NULL, NULL, NULL, filter); } - static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git, struct bitmap *result) { diff --git a/pack-bitmap.h b/pack-bitmap.h index 9f20fb6e56..1385027c1f 100644 --- a/pack-bitmap.h +++ b/pack-bitmap.h @@ -92,6 +92,9 @@ int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n); struct list_objects_filter_options; +/* Check whether the filter can be computed via the bitmap. */ +bool can_filter_bitmap(const struct list_objects_filter_options *filter); + /* * Filter bitmapped objects and iterate through all resulting objects, * executing `show_reach` for each of them. Returns `-1` in case the filter is