Thread (109 messages) flat view 109 messages, 7 authors, 2026-07-04

Re: [PATCH 2/9] promisor-remote: allow a client to store fields

From: Christian Couder <hidden>
Date: 2026-02-04 10:20:33

On Wed, Jan 7, 2026 at 11:05 AM Patrick Steinhardt [off-list ref] wrote:
On Tue, Dec 23, 2025 at 12:11:06PM +0100, Christian Couder wrote:
quoted
A previous commit allowed a server to pass additional fields through
the "promisor-remote" protocol capability after the "name" and "url"
fields, specifically the "partialCloneFilter" and "token" fields.

Another previous commit, c213820c51 (promisor-remote: allow a client
to check fields, 2025-09-08), has made it possible for a client to
decide if it accepts a promisor remote advertised by a server based
on these additional fields.

Often though, it would be interesting for the client to just store in
its configuration files these additional fields passed by the server,
so that it can use them when needed.

For example if a token is necessary to access a promisor remote, that
token could be updated frequently only on the server side and then
passed to all the clients through the "promisor-remote" capability,
avoiding the need to update it on all the clients manually.

Storing the token on the client side makes sure that the token is
available when the client needs to access the promisor remotes for a
lazy fetch.
I guess another use case is that a client performs a fresh clone and
doesn't know anything about the remote's promisors yet, right? In that
case, the client may want to tell git-clone(1) to accept any of the
remote's advertised promisors, store it and then use that promisor's
filter to perform the actual clone.
Actually there are two issues with this.

The first one is the security issue with the client adding a new
promisor to its config that I will discuss below.

The second one is the fact that it's better if the filter suggested by
the server is used right away during the initial clone, but you have
to pass a `--filter=<filter-spec>` to the clone option in the first
place when you start the initial clone and the filter suggested by the
server might be different than the one you pass. This is why the
second part of the series implements `--filter=auto`.
quoted
In the same way, if it appears that it's better to use a different
filter to access a promisor remote, it could be helpful if the client
could automatically use it.
By the way I have removed this in the version 2 I am going to send
soon, as it could be misleading.
quoted
To allow this, let's introduce a new "promisor.storeFields"
configuration variable.

Like "promisor.checkFields" and "promisor.sendFields", it should
contain a comma or space separated list of field names. Only the
"partialCloneFilter" and "token" field names are supported for now.

When a server advertises a promisor remote, for example "foo", along
with for example "token=XXXXX" to a client, and on the client side
"promisor.storeFields" contains "token", then the client will store
XXXXX for the "remote.foo.token" variable in its configuration file
and reload its configuration so it can immediately use this new
configuration variable.

A message is emitted on stderr to warn users when the config is
changed.

Note that even if "promisor.acceptFromServer" is set to "all", a
promisor remote has to be already configured on the client side for
some of its config to be changed. In any case no new remote is
configured and no new URL is stored.
Hm, okay, so that's not yet part of this series. I assume this is going
to be part of a subsequent patch series then?
My opinion is that we should indeed work on that in a future separate
series, as it could be very useful in setups where clients trust the
server, like corporate setups. For now I prefer to keep things safe by
default and not make it possible.
quoted
diff --git a/promisor-remote.c b/promisor-remote.c
index 5d8151cedb..8d6d2d7b76 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -403,6 +403,14 @@ static struct string_list *fields_checked(void)
      return initialize_fields_list(&fields_list, &initialized, "promisor.checkFields");
 }

+static struct string_list *fields_stored(void)
+{
+     static struct string_list fields_list = STRING_LIST_INIT_NODUP;
+     static int initialized;
+
+     return initialize_fields_list(&fields_list, &initialized, "promisor.storeFields");
+}
I'm a bit worried about all the function-local state that we're
accumulating in those functions. Wouldn't it be preferable if we instead
had a `struct promisor_remote` that encapsulates the information?
I don't think we have a good standard way to manage information from
the config yet. Some suggestions have been made about using a new
struct for some config options, for example in:

https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/ (local)

and perhaps such a good standard way to manage config information will
result from these efforts, but I think it's too early to be sure.

In the meantime, I don't think it's a good idea to spend time on a
specialized way to do it just for promisor remotes.
quoted
@@ -692,6 +700,132 @@ static struct promisor_info *parse_one_advertised_remote(const char *remote_info
      return info;
 }

+static bool store_one_field(struct repository *repo, const char *remote_name,
+                         const char *field_name, const char *field_key,
+                         const char *advertised, const char *current)
+{
+     if (advertised && (!current || strcmp(current, advertised))) {
+             char *key = xstrfmt("remote.%s.%s", remote_name, field_key);
+
+             fprintf(stderr, _("Storing new %s from server for remote '%s'.\n"
+                               "    '%s' -> '%s'\n"),
+                     field_name, remote_name,
+                     current ? current : "",
+                     advertised);
+
+             repo_config_set_worktree_gently(repo, key, advertised);
Why do we store this information in the current per-worktree config? I'd
expect that this should be stored in the local config.
Right, repo_config_set_gently() is used now instead.
quoted
+             free(key);
+
+             return true;
+     }
[...]
quoted
+struct store_info {
+     struct repository *repo;
+     struct string_list config_info;
+     bool store_filter;
+     bool store_token;
+};
+
+static struct store_info *new_store_info(struct repository *repo)
This should be called `store_info_new()` according to our coding
guidelines.
Fine, `store_info_new()` and `store_info_free()` are now used as you suggest.
quoted
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
index 023735d6a8..a726af214a 100755
--- a/t/t5710-promisor-remote-capability.sh
+++ b/t/t5710-promisor-remote-capability.sh
@@ -360,6 +360,55 @@ test_expect_success "clone with promisor.checkFields" '
      check_missing_objects server 1 "$oid"
 '

+test_expect_success "clone with promisor.storeFields=partialCloneFilter" '
+     git -C server config promisor.advertise true &&
+     test_when_finished "rm -rf client" &&
+
+     git -C server remote add otherLop "https://invalid.invalid"  &&
+     git -C server config remote.otherLop.token "fooBar" &&
+     git -C server config remote.otherLop.stuff "baz" &&
+     git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" &&
+     test_when_finished "git -C server remote remove otherLop" &&
+
+     git -C server config remote.lop.token "fooXXX" &&
+     git -C server config remote.lop.partialCloneFilter "blob:limit=8k" &&
+
+     test_config -C server promisor.sendFields "partialCloneFilter, token" &&
+     test_when_finished "rm trace" &&
+
+     # Clone from server to create a client
+     GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
+             -c remote.lop.promisor=true \
+             -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+             -c remote.lop.url="file://$(pwd)/lop" \
+             -c remote.lop.token="fooYYY" \
+             -c remote.lop.partialCloneFilter="blob:none" \
+             -c promisor.acceptfromserver=All \
+             -c promisor.storeFields=partialcloneFilter \
+             --no-local --filter="blob:limit=5k" server client 2>err &&
Onet thing that's missing in these tests is to verify that a subsequent
git-fetch(1) updates the configuration.
Ok, I have added a test using `git fetch`.

Thanks.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help