Re: [PATCH v4 1/5] promisor-remote: refactor to get rid of 'struct strvec'
- From
Justin Tobler <jltobler@gmail.com>
- Date
- Jun 23, 2025, 19:38 UTC
- Message-ID
- <2z3uanklsfnrgxmiqjt2innv2diownbpwtgcj67bi7udwjdqpg@ackusinuz7c6>
- In-Reply-To
- <20250611134506.2975856-2-christian.couder@gmail.com>
On 25/06/11 03:45PM, Christian Couder wrote:
Show 17 quoted lines
> In a following commit, we will use the new 'promisor-remote' protocol > capability introduced by d460267613 (Add 'promisor-remote' capability > to protocol v2, 2025-02-18) to pass and process more information > about promisor remotes than just their name and url. > > For that purpose, we will need to store information about other > fields, especially information that might or might not be available > for different promisor remotes. Unfortunately using 'struct strvec', > as we currently do, to store information about the promisor remotes > with one 'struct strvec' for each field like "name" or "url" does not > scale easily in that case. > > Let's refactor this and introduce a new 'struct promisor_info'. > > It will only store promisor remote information in its members. For now > it has only a 'name' member for the promisor remote name and an 'url' > member for its URL. We will use use a 'struct string_list' to store
s/use use/use/
Show 40 quoted lines
> the instances of 'struct promisor_info'. For each 'item' in the
> string_list, 'item->string' will point to the promisor remote name and
> 'item->util' will point to the corresponding 'struct promisor_info'
> instance.
>
> Explicit members are used within 'struct promisor_info' for type
> safety and clarity regarding the specific information being handled,
> rather than a generic key-value store. We want to specify and document
> each field and its content, so adding new members to the struct as
> more fields are supported is fine.
>
> Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
> ---
> promisor-remote.c | 111 +++++++++++++++++++++++++++++-----------------
> 1 file changed, 70 insertions(+), 41 deletions(-)
>
> diff --git a/promisor-remote.c b/promisor-remote.c
> index 9d058586df..90a063ea53 100644
> --- a/promisor-remote.c
> +++ b/promisor-remote.c
> @@ -314,9 +314,35 @@ static int allow_unsanitized(char ch)
> return ch > 32 && ch < 127;
> }
>
> -static void promisor_info_vecs(struct repository *repo,
> - struct strvec *names,
> - struct strvec *urls)
> +/*
> + * Struct for promisor remotes involved in the "promisor-remote"
> + * protocol capability.
> + *
> + * Except for "name", each <member> in this struct and its <value>
> + * should correspond (either on the client side or on the server side)
> + * to a "remote.<name>.<member>" config variable set to <value> where
> + * "<name>" is a promisor remote name.
> + */
> +struct promisor_info {
> + const char *name;
> + const char *url;
> +};Ok so now all promisor info for a given remote is stored in its own struct. This will enable easier extension in the future to add additional fields.
Show 34 quoted lines
> +
> +static void promisor_info_list_clear(struct string_list *list)
> +{
> + for (size_t i = 0; i < list->nr; i++) {
> + struct promisor_info *p = list->items[i].util;
> + free((char *)p->name);
> + free((char *)p->url);
> + }
> + string_list_clear(list, 1);
> +}
> +
> +/*
> + * Populate 'list' with promisor remote information from the config.
> + * The 'util' pointer of each list item will hold a 'struct promisor_info'.
> + */
> +static void promisor_config_info_list(struct repository *repo, struct string_list *list)
> {
> struct promisor_remote *r;
>
> @@ -328,8 +354,14 @@ static void promisor_info_vecs(struct repository *repo,
>
> /* Only add remotes with a non empty URL */
> if (!git_config_get_string_tmp(url_key, &url) && *url) {
> - strvec_push(names, r->name);
> - strvec_push(urls, url);
> + struct promisor_info *new_info = xcalloc(1, sizeof(*new_info));
> + struct string_list_item *item;
> +
> + new_info->name = xstrdup(r->name);
> + new_info->url = xstrdup(url);
> +
> + item = string_list_append(list, new_info->name);
> + item->util = new_info;
> }In this version, each remote is now stored as a member of a `struct string_list` instead of a custom collection type specific to promisor remote info. Nice
Show 8 quoted lines
> free(url_key);
> @@ -340,47 +372,36 @@ char *promisor_remote_info(struct repository *repo)
> {
> struct strbuf sb = STRBUF_INIT;
> int advertise_promisors = 0;
> - struct strvec names = STRVEC_INIT;
> - struct strvec urls = STRVEC_INIT;
> + struct string_list config_info = STRING_LIST_INIT_NODUP;nit: Ok in this context, "config_info" is specific to the list of promisor_info not just generic git configuration. Something like "promisor_info_list" would be a bit more explicit, but I don't feel super strongly.
Show 21 quoted lines
> + struct string_list_item *item;
>
> git_config_get_bool("promisor.advertise", &advertise_promisors);
>
> if (!advertise_promisors)
> return NULL;
>
> - promisor_info_vecs(repo, &names, &urls);
> + promisor_config_info_list(repo, &config_info);
>
> - if (!names.nr)
> + if (!config_info.nr)
> return NULL;
>
> - for (size_t i = 0; i < names.nr; i++) {
> - if (i)
> + for_each_string_list_item(item, &config_info) {
> + struct promisor_info *p = item->util;
> +
> + if (item != config_info.items)
> strbuf_addch(&sb, ';');Out of curiousity, is it invalid for the trailing promisor remote entry to end with a ';'? It would be simpler if each entry could just end with a semi-colon.
Show 68 quoted lines
> +
> strbuf_addstr(&sb, "name=");
> - strbuf_addstr_urlencode(&sb, names.v[i], allow_unsanitized);
> + strbuf_addstr_urlencode(&sb, p->name, allow_unsanitized);
> strbuf_addstr(&sb, ",url=");
> - strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
> + strbuf_addstr_urlencode(&sb, p->url, allow_unsanitized);
> }
>
> - strvec_clear(&names);
> - strvec_clear(&urls);
> + promisor_info_list_clear(&config_info);
>
> return strbuf_detach(&sb, NULL);
> }
>
> -/*
> - * Find first index of 'nicks' where there is 'nick'. 'nick' is
> - * compared case sensitively to the strings in 'nicks'. If not found
> - * 'nicks->nr' is returned.
> - */
> -static size_t remote_nick_find(struct strvec *nicks, const char *nick)
> -{
> - for (size_t i = 0; i < nicks->nr; i++)
> - if (!strcmp(nicks->v[i], nick))
> - return i;
> - return nicks->nr;
> -}
> -
> enum accept_promisor {
> ACCEPT_NONE = 0,
> ACCEPT_KNOWN_URL,
> @@ -390,19 +411,23 @@ enum accept_promisor {
>
> static int should_accept_remote(enum accept_promisor accept,
> const char *remote_name, const char *remote_url,
> - struct strvec *names, struct strvec *urls)
> + struct string_list *config_info)
> {
> - size_t i;
> + struct promisor_info *p;
> + struct string_list_item *item;
>
> if (accept == ACCEPT_ALL)
> return 1;
>
> - i = remote_nick_find(names, remote_name);
> + /* Get config info for that promisor remote */
> + item = string_list_lookup(config_info, remote_name);
>
> - if (i >= names->nr)
> + if (!item)
> /* We don't know about that remote */
> return 0;
>
> + p = item->util;
> +
> if (accept == ACCEPT_KNOWN_NAME)
> return 1;
>
> @@ -414,11 +439,15 @@ static int should_accept_remote(enum accept_promisor accept,
> return 0;
> }
>
> - if (!strcmp(urls->v[i], remote_url))
> + if (!p->url)
> + BUG("bad config_info (invalid URL) for remote '%s'",
> + remote_name);Ok just to clarify, it is invalid for a promisor remote to not have a URL specified. If so, it might be better to say "empty URL" or something along those lines.
Show 55 quoted lines
> +
> + if (!strcmp(p->url, remote_url))
> return 1;
>
> warning(_("known remote named '%s' but with URL '%s' instead of '%s'"),
> - remote_name, urls->v[i], remote_url);
> + remote_name, p->url, remote_url);
>
> return 0;
> }
> @@ -430,8 +459,7 @@ static void filter_promisor_remote(struct repository *repo,
> struct strbuf **remotes;
> const char *accept_str;
> enum accept_promisor accept = ACCEPT_NONE;
> - struct strvec names = STRVEC_INIT;
> - struct strvec urls = STRVEC_INIT;
> + struct string_list config_info = STRING_LIST_INIT_NODUP;
>
> if (!git_config_get_string_tmp("promisor.acceptfromserver", &accept_str)) {
> if (!*accept_str || !strcasecmp("None", accept_str))
> @@ -450,8 +478,10 @@ static void filter_promisor_remote(struct repository *repo,
> if (accept == ACCEPT_NONE)
> return;
>
> - if (accept != ACCEPT_ALL)
> - promisor_info_vecs(repo, &names, &urls);
> + if (accept != ACCEPT_ALL) {
> + promisor_config_info_list(repo, &config_info);
> + string_list_sort(&config_info);
> + }
>
> /* Parse remote info received */
>
> @@ -482,7 +512,7 @@ static void filter_promisor_remote(struct repository *repo,
> if (remote_url)
> decoded_url = url_percent_decode(remote_url);
>
> - if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url, &names, &urls))
> + if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url, &config_info))
> strvec_push(accepted, decoded_name);
>
> strbuf_list_free(elems);
> @@ -490,8 +520,7 @@ static void filter_promisor_remote(struct repository *repo,
> free(decoded_url);
> }
>
> - strvec_clear(&names);
> - strvec_clear(&urls);
> + promisor_info_list_clear(&config_info);
> strbuf_list_free(remotes);
> }
>
> --
> 2.50.0.rc2.5.ge8efe62b7f
>