promisor-remote: introduce promisor.acceptFromServerUrl

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>
This commit is contained in:
Christian Couder
2026-05-27 16:08:17 +02:00
committed by Junio C Hamano
parent 5395129851
commit 78e0d9b0e4
2 changed files with 111 additions and 0 deletions

View 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);

View File

@@ -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" &&