From: Christian Couder Date: Tue, 23 Dec 2025 11:11:11 +0000 (+0100) Subject: list-objects-filter-options: implement auto filter resolution X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c8c941181aaacd2d3b40a092e7c5e8d01919a86d;p=thirdparty%2Fgit.git list-objects-filter-options: implement auto filter resolution In a following commit, we will need to aggregate filters from multiple accepted promisor remotes into a single filter. For that purpose, let's add a `list_objects_filter_combine()` helper function that takes a list of filter specifications and combines them into a single string. If multiple filters are provided, it constructs a "combine:..." filter, ensuring that sub-filters are properly URL-encoded using the existing `allow_unencoded` logic. In a following commit, we will add a `--filter=auto` option that will enable a client to use the filters suggested by the server for the promisor remotes the client accepted. To simplify the filter processing related to this new feature, let's also add a small `list_objects_filter_resolve_auto()` function. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index f13ae5caeb..4a9c1991c1 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -230,6 +230,41 @@ static void filter_spec_append_urlencode( filter->filter_spec.buf + orig_len); } +char *list_objects_filter_combine(const struct string_list *specs) +{ + struct strbuf buf = STRBUF_INIT; + + if (!specs->nr) + return NULL; + + if (specs->nr == 1) + return xstrdup(specs->items[0].string); + + strbuf_addstr(&buf, "combine:"); + + for (size_t i = 0; i < specs->nr; i++) { + const char *spec = specs->items[i].string; + if (i > 0) + strbuf_addch(&buf, '+'); + + strbuf_addstr_urlencode(&buf, spec, allow_unencoded); + } + + return strbuf_detach(&buf, NULL); +} + +void list_objects_filter_resolve_auto(struct list_objects_filter_options *filter_options, + char *new_filter, struct strbuf *errbuf) +{ + if (filter_options->choice != LOFC_AUTO) + return; + + list_objects_filter_release(filter_options); + + if (new_filter) + gently_parse_list_objects_filter(filter_options, new_filter, errbuf); +} + /* * Changes filter_options into an equivalent LOFC_COMBINE filter options * instance. Does not do anything if filter_options is already LOFC_COMBINE. diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h index 77d7bbc846..832d615c17 100644 --- a/list-objects-filter-options.h +++ b/list-objects-filter-options.h @@ -6,6 +6,7 @@ #include "strbuf.h" struct option; +struct string_list; /* * The list of defined filters for list-objects. @@ -168,4 +169,22 @@ void list_objects_filter_copy( struct list_objects_filter_options *dest, const struct list_objects_filter_options *src); +/* + * Combine the filter specs in 'specs' into a combined filter string + * like "combine:+", where , , etc are + * properly urlencoded. If 'specs' contains no element, NULL is + * returned. If 'specs' contains a single element, a copy of that + * element is returned. + */ +char *list_objects_filter_combine(const struct string_list *specs); + +/* + * Check if 'filter_options' are an 'auto' filter, and if that's the + * case populate it with the filter specified by 'new_filter'. + */ +void list_objects_filter_resolve_auto( + struct list_objects_filter_options *filter_options, + char *new_filter, + struct strbuf *errbuf); + #endif /* LIST_OBJECTS_FILTER_OPTIONS_H */ diff --git a/t/unit-tests/u-list-objects-filter-options.c b/t/unit-tests/u-list-objects-filter-options.c index f7d73701b5..84a012af3c 100644 --- a/t/unit-tests/u-list-objects-filter-options.c +++ b/t/unit-tests/u-list-objects-filter-options.c @@ -1,6 +1,7 @@ #include "unit-test.h" #include "list-objects-filter-options.h" #include "strbuf.h" +#include "string-list.h" /* Helper to test gently_parse_list_objects_filter() */ static void check_gentle_parse(const char *filter_spec, @@ -51,3 +52,35 @@ void test_list_objects_filter_options__combine_auto_fails(void) check_gentle_parse("combine:blob:none+auto", 0, 1, 0); check_gentle_parse("combine:auto+auto", 0, 1, 0); } + +/* Helper to test list_objects_filter_combine() */ +static void check_combine(const char **specs, size_t nr, const char *expected) +{ + struct string_list spec_list = STRING_LIST_INIT_NODUP; + char *actual; + + for (size_t i = 0; i < nr; i++) + string_list_append(&spec_list, specs[i]); + + actual = list_objects_filter_combine(&spec_list); + + cl_assert_equal_s(actual, expected); + + free(actual); + string_list_clear(&spec_list, 0); +} + +void test_list_objects_filter_options__combine_helper(void) +{ + const char *empty[] = { NULL }; + const char *one[] = { "blob:none" }; + const char *two[] = { "blob:none", "tree:0" }; + const char *complex[] = { "blob:limit=1k", "object:type=tag" }; + const char *needs_encoding[] = { "blob:none", "combine:tree:0+blob:limit=1k" }; + + check_combine(empty, 0, NULL); + check_combine(one, 1, "blob:none"); + check_combine(two, 2, "combine:blob:none+tree:0"); + check_combine(complex, 2, "combine:blob:limit=1k+object:type=tag"); + check_combine(needs_encoding, 2, "combine:blob:none+combine:tree:0%2bblob:limit=1k"); +}