]> git.ipfire.org Git - thirdparty/git.git/commitdiff
list-objects-filter-options: implement auto filter resolution
authorChristian Couder <christian.couder@gmail.com>
Tue, 23 Dec 2025 11:11:11 +0000 (12:11 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 23 Dec 2025 13:43:06 +0000 (22:43 +0900)
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 <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
list-objects-filter-options.c
list-objects-filter-options.h
t/unit-tests/u-list-objects-filter-options.c

index f13ae5caeb6d21fa8c0e56110e3f001af2c4c2b0..4a9c1991c1a75ea07bd86e1876aaf30dbbd223e5 100644 (file)
@@ -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.
index 77d7bbc846e1c1bbe5f2ca11fb119c0cf25b136d..832d615c171e44a7286ddd412b28bfffd2c66d59 100644 (file)
@@ -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:<spec1>+<spec2>", where <spec1>, <spec2>, 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 */
index f7d73701b5c69e8063c4627b7bc3ec2529733250..84a012af3c9998a9c7698ce6bbfae8a329dab908 100644 (file)
@@ -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");
+}