fetch-pack: wire up and enable auto filter logic

Previous commits have set up an infrastructure for `--filter=auto` to automatically prepare a partial clone filter based on what the server advertised and the client accepted. Using that infrastructure, let's now enable the `--filter=auto` option in `git clone` and `git fetch` by setting `allow_auto_filter` to 1. Note that these small changes mean that when `git clone --filter=auto` or `git fetch --filter=auto` are used, "auto" is automatically saved as the partial clone filter for the server on the client. Therefore subsequent calls to `git fetch` on the client will automatically use this "auto" mode even without `--filter=auto`. Let's also set `allow_auto_filter` to 1 in `transport.c`, as the transport layer must be able to accept the "auto" filter spec even if the invoking command hasn't fully parsed it yet. When an "auto" filter is requested, let's have the "fetch-pack.c" code in `do_fetch_pack_v2()` compute a filter and send it to the server. In `do_fetch_pack_v2()` the logic also needs to check for the "promisor-remote" capability and call `promisor_remote_reply()` to parse advertised remotes and populate the list of those accepted (and their filters). 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 ef2f1845ec4b683df791bfd956f551b096a38009
8 files changed +134 -15
Documentation/fetch-options.adoc
+14 -5
@@ -92,11 +92,20 @@ precedence over the `fetch.output` config option.
92 Use the partial clone feature and request that the server sends
93 a subset of reachable objects according to a given object filter.
94 When using `--filter`, the supplied _<filter-spec>_ is used for
95 - the partial fetch. For example, `--filter=blob:none` will filter
96 - out all blobs (file contents) until needed by Git. Also,
97 - `--filter=blob:limit=<size>` will filter out all blobs of size
98 - at least _<size>_. For more details on filter specifications, see
99 - the `--filter` option in linkgit:git-rev-list[1].
95 + the partial fetch.
96 ++
97 +If `--filter=auto` is used, the filter specification is determined
98 +automatically by combining the filter specifications advertised by
99 +the server for the promisor remotes that the client accepts (see
100 +linkgit:gitprotocol-v2[5] and the `promisor.acceptFromServer`
101 +configuration option in linkgit:git-config[1]).
102 ++
103 +For details on all other available filter specifications, see the
104 +`--filter=<filter-spec>` option in linkgit:git-rev-list[1].
105 ++
106 +For example, `--filter=blob:none` will filter out all blobs (file
107 +contents) until needed by Git. Also, `--filter=blob:limit=<size>` will
108 +filter out all blobs of size at least _<size>_.
109
110 ifndef::git-pull[]
111 `--write-fetch-head`::
Documentation/git-clone.adoc
+20 -5
@@ -187,11 +187,26 @@ objects from the source repository into a pack in the cloned repository.
187 Use the partial clone feature and request that the server sends
188 a subset of reachable objects according to a given object filter.
189 When using `--filter`, the supplied _<filter-spec>_ is used for
190 - the partial clone filter. For example, `--filter=blob:none` will
191 - filter out all blobs (file contents) until needed by Git. Also,
192 - `--filter=blob:limit=<size>` will filter out all blobs of size
193 - at least _<size>_. For more details on filter specifications, see
194 - the `--filter` option in linkgit:git-rev-list[1].
190 + the partial clone filter.
191 ++
192 +If `--filter=auto` is used the filter specification is determined
193 +automatically through the 'promisor-remote' protocol (see
194 +linkgit:gitprotocol-v2[5]) by combining the filter specifications
195 +advertised by the server for the promisor remotes that the client
196 +accepts (see the `promisor.acceptFromServer` configuration option in
197 +linkgit:git-config[1]). This allows the server to suggest the optimal
198 +filter for the available promisor remotes.
199 ++
200 +As with other filter specifications, the "auto" value is persisted in
201 +the configuration. This ensures that future fetches will continue to
202 +adapt to the server's current recommendation.
203 ++
204 +For details on all other available filter specifications, see the
205 +`--filter=<filter-spec>` option in linkgit:git-rev-list[1].
206 ++
207 +For example, `--filter=blob:none` will filter out all blobs (file
208 +contents) until needed by Git. Also, `--filter=blob:limit=<size>` will
209 +filter out all blobs of size at least _<size>_.
210
211 `--also-filter-submodules`::
212 Also apply the partial clone filter to any submodules in the repository.
Documentation/gitprotocol-v2.adoc
+11 -5
@@ -812,10 +812,15 @@ MUST appear first in each pr-fields, in that order.
812 After these mandatory fields, the server MAY advertise the following
813 optional fields in any order:
814
815 -`partialCloneFilter`:: The filter specification used by the remote.
815 +`partialCloneFilter`:: The filter specification for the remote. It
816 +corresponds to the "remote.<name>.partialCloneFilter" config setting.
817 Clients can use this to determine if the remote's filtering strategy
817 -is compatible with their needs (e.g., checking if both use "blob:none").
818 -It corresponds to the "remote.<name>.partialCloneFilter" config setting.
818 +is compatible with their needs (e.g., checking if both use
819 +"blob:none"). Additionally they can use this through the
820 +`--filter=auto` option in linkgit:git-clone[1]. With that option, the
821 +filter specification of the clone will be automatically computed by
822 +combining the filter specifications of the promisor remotes the client
823 +accepts.
824
825 `token`:: An authentication token that clients can use when
826 connecting to the remote. It corresponds to the "remote.<name>.token"
@@ -828,8 +833,9 @@ future protocol extensions.
833
834 The client can use information transmitted through these fields to
835 decide if it accepts the advertised promisor remote. Also, the client
831 -can be configured to store the values of these fields (see
832 -"promisor.storeFields" in linkgit:git-config[1]).
836 +can be configured to store the values of these fields or use them
837 +to automatically configure the repository (see "promisor.storeFields"
838 +in linkgit:git-config[1] and `--filter=auto` in linkgit:git-clone[1]).
839
840 Field values MUST be urlencoded.
841
builtin/clone.c
+2
@@ -1001,6 +1001,8 @@ int cmd_clone(int argc,
1001 NULL
1002 };
1003
1004 + filter_options.allow_auto_filter = 1;
1005 +
1006 packet_trace_identity("clone");
1007
1008 repo_config(the_repository, git_clone_config, NULL);
builtin/fetch.c
+2
@@ -2580,6 +2580,8 @@ int cmd_fetch(int argc,
2580 OPT_END()
2581 };
2582
2583 + filter_options.allow_auto_filter = 1;
2584 +
2585 packet_trace_identity("fetch");
2586
2587 /* Record the command line for the reflog */
fetch-pack.c
+24
@@ -35,6 +35,7 @@
35 #include "sigchain.h"
36 #include "mergesort.h"
37 #include "prio-queue.h"
38 +#include "promisor-remote.h"
39
40 static int transfer_unpack_limit = -1;
41 static int fetch_unpack_limit = -1;
@@ -1661,6 +1662,29 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
1662 struct string_list packfile_uris = STRING_LIST_INIT_DUP;
1663 int i;
1664 struct strvec index_pack_args = STRVEC_INIT;
1665 + const char *promisor_remote_config;
1666 +
1667 + if (server_feature_v2("promisor-remote", &promisor_remote_config))
1668 + promisor_remote_reply(promisor_remote_config, NULL);
1669 +
1670 + if (args->filter_options.choice == LOFC_AUTO) {
1671 + struct strbuf errbuf = STRBUF_INIT;
1672 + char *constructed_filter = promisor_remote_construct_filter(r);
1673 +
1674 + list_objects_filter_release(&args->filter_options);
1675 + /* Disallow 'auto' as a result of the resolution of this 'auto' filter below */
1676 + args->filter_options.allow_auto_filter = 0;
1677 +
1678 + if (constructed_filter &&
1679 + gently_parse_list_objects_filter(&args->filter_options,
1680 + constructed_filter,
1681 + &errbuf))
1682 + die(_("couldn't resolve 'auto' filter '%s': %s"),
1683 + constructed_filter, errbuf.buf);
1684 +
1685 + free(constructed_filter);
1686 + strbuf_release(&errbuf);
1687 + }
1688
1689 negotiator = &negotiator_alloc;
1690 if (args->refetch)
t/t5710-promisor-remote-capability.sh
+60
@@ -423,6 +423,66 @@ test_expect_success "clone with promisor.storeFields=partialCloneFilter" '
423 test_grep "'\''blob:limit=8k'\'' -> '\''blob:limit=7k'\''" err
424 '
425
426 +test_expect_success "clone and fetch with --filter=auto" '
427 + git -C server config promisor.advertise true &&
428 + test_when_finished "rm -rf client trace" &&
429 +
430 + git -C server config remote.lop.partialCloneFilter "blob:limit=9500" &&
431 + test_config -C server promisor.sendFields "partialCloneFilter" &&
432 +
433 + GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
434 + -c remote.lop.promisor=true \
435 + -c remote.lop.url="file://$(pwd)/lop" \
436 + -c promisor.acceptfromserver=All \
437 + --no-local --filter=auto server client 2>err &&
438 +
439 + test_grep "filter blob:limit=9500" trace &&
440 + test_grep ! "filter auto" trace &&
441 +
442 + # Verify "auto" is persisted in config
443 + echo auto >expected &&
444 + git -C client config remote.origin.partialCloneFilter >actual &&
445 + test_cmp expected actual &&
446 +
447 + # Check that the largest object is still missing on the server
448 + check_missing_objects server 1 "$oid" &&
449 +
450 + # Now change the filter on the server
451 + git -C server config remote.lop.partialCloneFilter "blob:limit=5678" &&
452 +
453 + # Get a new commit on the server to ensure "git fetch" actually runs fetch-pack
454 + test_commit -C template new-commit &&
455 + git -C template push --all "$(pwd)/server" &&
456 +
457 + # Perform a fetch WITH --filter=auto
458 + rm -rf trace &&
459 + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch --filter=auto &&
460 +
461 + # Verify that the new filter was used
462 + test_grep "filter blob:limit=5678" trace &&
463 +
464 + # Check that the largest object is still missing on the server
465 + check_missing_objects server 1 "$oid" &&
466 +
467 + # Change the filter on the server again
468 + git -C server config remote.lop.partialCloneFilter "blob:limit=5432" &&
469 +
470 + # Get yet a new commit on the server to ensure fetch-pack runs
471 + test_commit -C template yet-a-new-commit &&
472 + git -C template push --all "$(pwd)/server" &&
473 +
474 + # Perform a fetch WITHOUT --filter=auto
475 + # Relies on "auto" being persisted in the client config
476 + rm -rf trace &&
477 + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch &&
478 +
479 + # Verify that the new filter was used
480 + test_grep "filter blob:limit=5432" trace &&
481 +
482 + # Check that the largest object is still missing on the server
483 + check_missing_objects server 1 "$oid"
484 +'
485 +
486 test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
487 git -C server config promisor.advertise true &&
488
transport.c
+1
@@ -1219,6 +1219,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
1219 */
1220 struct git_transport_data *data = xcalloc(1, sizeof(*data));
1221 list_objects_filter_init(&data->options.filter_options);
1222 + data->options.filter_options.allow_auto_filter = 1;
1223 ret->data = data;
1224 ret->vtable = &builtin_smart_vtable;
1225 ret->smart_options = &(data->options);