promisor-remote: keep advertised filters in memory

Currently, advertised filters are only kept in memory temporarily during parsing, or persisted to disk if `promisor.storeFields` contains 'partialCloneFilter'. In a following commit though, we will add a `--filter=auto` option. This option will enable the client to use the filters that the server is suggesting for the promisor remotes the client accepts. To use them even if `promisor.storeFields` is not configured, these filters should be stored somewhere for the current session. Let's add an `advertised_filter` field to `struct promisor_remote` for that purpose. To ensure that the filters are available in all cases, filter_promisor_remote() captures them into a temporary list and applies them to the `promisor_remote` structs after the potential configuration reload. Then the accepted remotes are marked as `accepted` in the repository state. This ensures that subsequent calls to look up accepted remotes (like in the filter construction below) actually find them. 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 enable the client to construct a filter spec based on these filters, let's also add a `promisor_remote_construct_filter(repo)` function. This function: - iterates over all accepted promisor remotes in the repository, - collects the filters advertised for them (using `advertised_filter` added in this commit, and - generates a single filter spec for them. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Christian Couder committed Feb 16, 2026 at 14:23 UTC 257f2db5d3e6d734861890ed4f1d81607f1702fe
2 files changed +65
promisor-remote.c
+58
@@ -193,6 +193,7 @@ void promisor_remote_clear(struct promisor_remote_config *config)
193 while (config->promisors) {
194 struct promisor_remote *r = config->promisors;
195 free(r->partial_clone_filter);
196 + free(r->advertised_filter);
197 config->promisors = config->promisors->next;
198 free(r);
199 }
@@ -837,6 +838,7 @@ static void filter_promisor_remote(struct repository *repo,
838 struct store_info *store_info = NULL;
839 struct string_list_item *item;
840 bool reload_config = false;
841 + struct string_list accepted_filters = STRING_LIST_INIT_DUP;
842
843 if (!repo_config_get_string_tmp(the_repository, "promisor.acceptfromserver", &accept_str)) {
844 if (!*accept_str || !strcasecmp("None", accept_str))
@@ -879,6 +881,13 @@ static void filter_promisor_remote(struct repository *repo,
881 reload_config = true;
882
883 strvec_push(accepted, advertised->name);
884 +
885 + /* Capture advertised filters for accepted remotes */
886 + if (advertised->filter) {
887 + struct string_list_item *i;
888 + i = string_list_append(&accepted_filters, advertised->name);
889 + i->util = xstrdup(advertised->filter);
890 + }
891 }
892
893 promisor_info_free(advertised);
@@ -890,6 +899,25 @@ static void filter_promisor_remote(struct repository *repo,
899
900 if (reload_config)
901 repo_promisor_remote_reinit(repo);
902 +
903 + /* Apply accepted remote filters to the stable repo state */
904 + for_each_string_list_item(item, &accepted_filters) {
905 + struct promisor_remote *r = repo_promisor_remote_find(repo, item->string);
906 + if (r) {
907 + free(r->advertised_filter);
908 + r->advertised_filter = item->util;
909 + item->util = NULL;
910 + }
911 + }
912 +
913 + string_list_clear(&accepted_filters, 1);
914 +
915 + /* Mark the remotes as accepted in the repository state */
916 + for (size_t i = 0; i < accepted->nr; i++) {
917 + struct promisor_remote *r = repo_promisor_remote_find(repo, accepted->v[i]);
918 + if (r)
919 + r->accepted = 1;
920 + }
921 }
922
923 char *promisor_remote_reply(const char *info)
@@ -935,3 +963,33 @@ void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes
963
964 string_list_clear(&accepted_remotes, 0);
965 }
966 +
967 +char *promisor_remote_construct_filter(struct repository *repo)
968 +{
969 + struct promisor_remote *r;
970 + struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
971 + struct strbuf err = STRBUF_INIT;
972 + char *result = NULL;
973 +
974 + promisor_remote_init(repo);
975 +
976 + for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
977 + if (r->accepted && r->advertised_filter)
978 + if (gently_parse_list_objects_filter(&filter_options,
979 + r->advertised_filter,
980 + &err)) {
981 + warning(_("promisor remote '%s' advertised invalid filter '%s': %s"),
982 + r->name, r->advertised_filter, err.buf);
983 + strbuf_reset(&err);
984 + continue;
985 + }
986 + }
987 +
988 + if (filter_options.choice)
989 + result = xstrdup(expand_list_objects_filter_spec(&filter_options));
990 +
991 + list_objects_filter_release(&filter_options);
992 + strbuf_release(&err);
993 +
994 + return result;
995 +}
promisor-remote.h
+7
@@ -15,6 +15,7 @@ struct object_id;
15 struct promisor_remote {
16 struct promisor_remote *next;
17 char *partial_clone_filter;
18 + char *advertised_filter;
19 unsigned int accepted : 1;
20 const char name[FLEX_ARRAY];
21 };
@@ -67,4 +68,10 @@ void mark_promisor_remotes_as_accepted(struct repository *repo, const char *remo
68 */
69 int repo_has_accepted_promisor_remote(struct repository *r);
70
71 +/*
72 + * Use the filters from the accepted remotes to create a combined
73 + * filter (useful in `--filter=auto` mode).
74 + */
75 +char *promisor_remote_construct_filter(struct repository *repo);
76 +
77 #endif /* PROMISOR_REMOTE_H */