]> git.ipfire.org Git - thirdparty/git.git/commitdiff
promisor-remote: introduce promisor.acceptFromServerUrl
authorChristian Couder <christian.couder@gmail.com>
Wed, 27 May 2026 14:08:17 +0000 (16:08 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 May 2026 20:20:15 +0000 (05:20 +0900)
The "promisor-remote" protocol capability allows servers to advertise
promisor remotes, but doesn't allow these remotes to be automatically
configured on the client.

Let's introduce a new `promisor.acceptFromServerUrl` config variable
which contains a glob pattern, so that advertised remotes with a URL
matching that pattern will be automatically configured.

The glob pattern can optionally be prefixed with a remote name which
will be used as the name of the new local remote.

For now though, let's only introduce the functions to read and validate
the glob patterns and the optional prefixes.

Checking if the URLs of the advertised remotes match the glob patterns
and taking the appropriate action is left for a following commit.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
promisor-remote.c
t/t5710-promisor-remote-capability.sh

index 138a41289350d646ef4b1dd525f2e6b6215b0e4c..8d4f6e0a72251b570daf5c0c70261b9ebffb9a95 100644 (file)
@@ -12,6 +12,7 @@
 #include "packfile.h"
 #include "environment.h"
 #include "url.h"
+#include "urlmatch.h"
 #include "version.h"
 
 struct promisor_remote_config {
@@ -657,6 +658,90 @@ static bool has_control_char(const char *s)
        return false;
 }
 
+struct allowed_url {
+       char *remote_name;
+       char *url_pattern;
+       struct url_info pattern_info;
+};
+
+static void allowed_url_free(void *util, const char *str UNUSED)
+{
+       struct allowed_url *allowed = util;
+
+       if (!allowed)
+               return;
+
+       /* Depending on prefix, free either remote_name or url_pattern */
+       free(allowed->remote_name ? allowed->remote_name : allowed->url_pattern);
+       free(allowed->pattern_info.url);
+       free(allowed);
+}
+
+static struct allowed_url *valid_accept_url(const char *url)
+{
+       char *dup, *p;
+       struct allowed_url *allowed;
+
+       if (!url)
+               return NULL;
+
+       dup = xstrdup(url);
+       p = strchr(dup, '=');
+       if (p) {
+               *p = '\0';
+               if (!valid_remote_name(dup)) {
+                       warning(_("invalid remote name '%s' before '=' sign "
+                                 "in '%s' from promisor.acceptFromServerUrl config"),
+                               dup, url);
+                       free(dup);
+                       return NULL;
+               }
+               p++;
+       } else {
+               p = dup;
+       }
+
+       if (has_control_char(p)) {
+               warning(_("invalid url pattern '%s' "
+                         "in '%s' from promisor.acceptFromServerUrl config"), p, url);
+               free(dup);
+               return NULL;
+       }
+
+       allowed = xmalloc(sizeof(*allowed));
+       allowed->remote_name = (p == dup) ? NULL : dup;
+       allowed->url_pattern = p;
+       allowed->pattern_info.url = url_normalize_pattern(p, &allowed->pattern_info);
+       if (!allowed->pattern_info.url) {
+               warning(_("invalid url pattern '%s' "
+                         "in '%s' from promisor.acceptFromServerUrl config"), p, url);
+               free(dup);
+               free(allowed);
+               return NULL;
+       }
+
+       return allowed;
+}
+
+static void load_accept_from_server_url(struct repository *repo,
+                                       struct string_list *accept_urls)
+{
+       const struct string_list *config_urls;
+
+       if (!repo_config_get_string_multi(repo, "promisor.acceptfromserverurl", &config_urls)) {
+               struct string_list_item *item;
+
+               for_each_string_list_item(item, config_urls) {
+                       struct allowed_url *allowed = valid_accept_url(item->string);
+                       if (allowed) {
+                               struct string_list_item *new;
+                               new = string_list_append(accept_urls, item->string);
+                               new->util = allowed;
+                       }
+               }
+       }
+}
+
 static int should_accept_remote(enum accept_promisor accept,
                                struct promisor_info *advertised,
                                struct string_list *config_info)
@@ -901,6 +986,10 @@ static void filter_promisor_remote(struct repository *repo,
        struct string_list_item *item;
        bool reload_config = false;
        enum accept_promisor accept = accept_from_server(repo);
+       struct string_list accept_urls = STRING_LIST_INIT_DUP;
+
+       /* Load and validate the acceptFromServerUrl config */
+       load_accept_from_server_url(repo, &accept_urls);
 
        if (accept == ACCEPT_NONE)
                return;
@@ -934,6 +1023,7 @@ static void filter_promisor_remote(struct repository *repo,
                }
        }
 
+       string_list_clear_func(&accept_urls, allowed_url_free);
        promisor_info_list_clear(&config_info);
        string_list_clear(&remote_info, 0);
        store_info_free(store_info);
index bf1cc54605394e02a0ea3e0d56bb2d834804d477..3b39505380caa763d09117882a3f227c595bf47b 100755 (executable)
@@ -387,6 +387,27 @@ test_expect_success "clone with 'KnownUrl' and empty url, so not advertised" '
        check_missing_objects server 1 "$oid"
 '
 
+test_expect_success "clone with invalid promisor.acceptFromServerUrl" '
+       git -C server config promisor.advertise true &&
+       test_when_finished "rm -rf client" &&
+
+       # As "bad name" contains a space, which is not a valid remote name,
+       # the pattern should be rejected with a warning and no remote created.
+       GIT_NO_LAZY_FETCH=0 git clone \
+               -c promisor.acceptfromserver=None \
+               -c "promisor.acceptFromServerUrl=bad name=https://example.com/*" \
+               --no-local --filter="blob:limit=5k" server client 2>err &&
+
+       # Check that a warning was emitted
+       test_grep "invalid remote name '\''bad name'\''" err &&
+
+       # Check that the largest object is not missing on the server
+       check_missing_objects server 0 "" &&
+
+       # Reinitialize server so that the largest object is missing again
+       initialize_server 1 "$oid"
+'
+
 test_expect_success "clone with promisor.sendFields" '
        git -C server config promisor.advertise true &&
        test_when_finished "rm -rf client" &&