Extend the path-walk API to handle the 'blob:limit=<size>' object
filter natively. This filter omits blobs whose size is equal to or
greater than the given limit, matching the semantics used by the
list-objects-filter machinery.
When revs->filter.choice is LOFC_BLOB_LIMIT, the prepare_filters()
method stores the limit value in info->blob_limit and clears the filter
from revs. If the limit is zero, this degenerates to blob:none (all
blobs excluded), so info->blobs is set to 0 instead.
During walk_path(), blob batches are filtered before being delivered to
the callback: each blob's size is checked via odb_read_object_info(),
and only blobs strictly smaller than the limit are included. Blobs whose
size cannot be determined (e.g. missing in a partial clone) are
conservatively included, matching the existing filter behavior. Empty
batches after filtering are skipped entirely.
The check for inclusion in the path batch looks a little strange at
first glance. We use odb_read_object_info() to read the object's size.
Based on all of the assumptions to this point, this _should_ return
OBJ_BLOB. Since we are focused on the size filter, we use a
short-circuited OR (||) to skip the size check if that method returns a
different object type.
Notice that this inspection of object sizes requires the content to be
present in the repository. The odb_read_object_info() call will download
a missing blob on-demand. This means that the use of the path-walk API
within 'git backfill' would not operate nicely with this filter type.
The intention of that command is to download missing blobs in batches.
Downloading objects one-by-one would go against the point. Update the
validation in 'git backfill' to add its own compatibility check on top
of path_walk_filter_compatible().
Add tests for blob:limit=0 (equivalent to blob:none) and blob:limit=3
(which exercises partial filtering within a batch where some blobs are
kept and others are excluded).
Co-authored-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Derrick Stolee committedMay 22, 2026 at 18:24 UTCf1b5d3da163f90093ab4f1816df24be54e75a1d9
6 files changed+130-6
Documentation/git-pack-objects.adoc
+1-1
index 2994faf988..85ae48b699 100644--- a/Documentation/git-pack-objects.adoc+++ b/Documentation/git-pack-objects.adoc@@ -404,7 +404,7 @@ will be automatically changed to version `1`. + Incompatible with `--delta-islands`. The `--use-bitmap-index` option is ignored in the presence of `--path-walk`. The `--path-walk` option-supports the `--filter=<spec>` form `blob:none`.+supports the `--filter=<spec>` forms `blob:none` and `blob:limit=<n>`. DELTA ISLANDS
builtin/backfill.c
+2
index b80f9ebe69..5254a42711 100644--- a/builtin/backfill.c+++ b/builtin/backfill.c@@ -98,6 +98,8 @@ static void reject_unsupported_rev_list_options(struct rev_info *revs) "--diff-merges"); if (!path_walk_filter_compatible(&revs->filter)) die(_("cannot backfill with these filter options"));+ if (revs->filter.blob_limit_value)+ die(_("cannot backfill with blob size limits")); } static int do_backfill(struct backfill_context *ctx)
path-walk.c
+37-4
index bd81508163..04b924d4de 100644--- a/path-walk.c+++ b/path-walk.c@@ -10,6 +10,7 @@ #include "hex.h" #include "list-objects.h" #include "list-objects-filter-options.h"+#include "odb.h" #include "object.h" #include "oid-array.h" #include "path.h"@@ -327,13 +328,35 @@ static int walk_path(struct path_walk_context *ctx, /* * Evaluate function pointer on this data, if requested. * Ignore object type filters for tagged objects (path starts- * with `/`).+ * with `/`), first for blobs and then other types. */- if ((list->type == OBJ_TREE && (ctx->info->trees || path_is_for_direct_objects(path))) ||- (list->type == OBJ_BLOB && (ctx->info->blobs || path_is_for_direct_objects(path))) ||- (list->type == OBJ_TAG && ctx->info->tags))+ if (list->type == OBJ_BLOB &&+ ctx->info->blob_limit &&+ !path_is_for_direct_objects(path)) {+ struct oid_array filtered = OID_ARRAY_INIT;++ for (size_t i = 0; i < list->oids.nr; i++) {+ unsigned long size;++ if (odb_read_object_info(ctx->repo->objects,+ &list->oids.oid[i],+ &size) != OBJ_BLOB ||+ size < ctx->info->blob_limit)+ oid_array_append(&filtered,+ &list->oids.oid[i]);+ }++ if (filtered.nr)+ ret = ctx->info->path_fn(path, &filtered, list->type,+ ctx->info->path_fn_data);+ oid_array_clear(&filtered);+ } else if (path_is_for_direct_objects(path) ||+ (list->type == OBJ_TREE && ctx->info->trees) ||+ (list->type == OBJ_BLOB && ctx->info->blobs) ||+ (list->type == OBJ_TAG && ctx->info->tags)) { ret = ctx->info->path_fn(path, &list->oids, list->type, ctx->info->path_fn_data);+ } /* Expand data for children. */ if (list->type == OBJ_TREE) {@@ -510,6 +533,16 @@ static int prepare_filters(struct path_walk_info *info, } return 1;+ case LOFC_BLOB_LIMIT:+ if (info) {+ if (!options->blob_limit_value)+ info->blobs = 0;+ else+ info->blob_limit = options->blob_limit_value;+ list_objects_filter_release(options);+ }+ return 1;+ default: error(_("object filter '%s' not supported by the path-walk API"), list_objects_filter_spec(options));
path-walk.h
+7
index a1736ecb2b..60ceb65433 100644--- a/path-walk.h+++ b/path-walk.h@@ -47,6 +47,13 @@ struct path_walk_info { int blobs; int tags;+ /**+ * If non-zero, specifies a maximum blob size. Blobs with a+ * size equal to or greater than this limit will not be+ * emitted unless included in 'pending'.+ */+ unsigned long blob_limit;+ /** * When 'prune_all_uninteresting' is set and a path has all objects * marked as UNINTERESTING, then the path-walk will not visit those
t/t5620-backfill.sh
+1-1
index ede89f8c33..d2ea68e065 100755--- a/t/t5620-backfill.sh+++ b/t/t5620-backfill.sh@@ -20,7 +20,7 @@ test_expect_success 'backfill rejects incompatible filter options' ' test_grep "cannot backfill with these filter options" err && test_must_fail git backfill --objects --filter=blob:limit=10m 2>err &&- test_grep "cannot backfill with these filter options" err+ test_grep "cannot backfill with blob size limits" err ' # We create objects in the 'src' repo.