Skip to content

Commit 65e3e4b

Browse files
committed
Merge branch 'cc/promisor-auto-config-url' into jch
Promisor remote handling has been refactored and fixed in preparation for auto-configuration of advertised remotes. * cc/promisor-auto-config-url: t5710: use proper file:// URIs for absolute paths promisor-remote: remove the 'accepted' strvec promisor-remote: keep accepted promisor_info structs alive promisor-remote: refactor accept_from_server() promisor-remote: refactor has_control_char() promisor-remote: refactor should_accept_remote() control flow promisor-remote: reject empty name or URL in advertised remote promisor-remote: clarify that a remote is ignored promisor-remote: pass config entry to all_fields_match() directly promisor-remote: try accepted remotes before others in get_direct()
2 parents a0143f4 + 8eb8635 commit 65e3e4b

3 files changed

Lines changed: 238 additions & 111 deletions

File tree

Documentation/gitprotocol-v2.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,10 @@ advertised, it can reply with "promisor-remote=<pr-names>" where
848848
where `pr-name` is the urlencoded name of a promisor remote the server
849849
advertised and the client accepts.
850850
851+
The promisor remotes that the client accepted will be tried before the
852+
other configured promisor remotes when the client attempts to fetch
853+
missing objects.
854+
851855
Note that, everywhere in this document, the ';' and ',' characters
852856
MUST be encoded if they appear in `pr-name` or `field-value`.
853857

promisor-remote.c

Lines changed: 119 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,35 @@ static int remove_fetched_oids(struct repository *repo,
268268
return remaining_nr;
269269
}
270270

271+
static int try_promisor_remotes(struct repository *repo,
272+
struct object_id **remaining_oids,
273+
int *remaining_nr, int *to_free,
274+
bool accepted_only)
275+
{
276+
struct promisor_remote *r = repo->promisor_remote_config->promisors;
277+
278+
for (; r; r = r->next) {
279+
if (accepted_only != r->accepted)
280+
continue;
281+
if (fetch_objects(repo, r->name, *remaining_oids, *remaining_nr) < 0) {
282+
if (*remaining_nr == 1)
283+
continue;
284+
*remaining_nr = remove_fetched_oids(repo, remaining_oids,
285+
*remaining_nr, *to_free);
286+
if (*remaining_nr) {
287+
*to_free = 1;
288+
continue;
289+
}
290+
}
291+
return 1; /* all fetched */
292+
}
293+
return 0;
294+
}
295+
271296
void promisor_remote_get_direct(struct repository *repo,
272297
const struct object_id *oids,
273298
int oid_nr)
274299
{
275-
struct promisor_remote *r;
276300
struct object_id *remaining_oids = (struct object_id *)oids;
277301
int remaining_nr = oid_nr;
278302
int to_free = 0;
@@ -283,19 +307,13 @@ void promisor_remote_get_direct(struct repository *repo,
283307

284308
promisor_remote_init(repo);
285309

286-
for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
287-
if (fetch_objects(repo, r->name, remaining_oids, remaining_nr) < 0) {
288-
if (remaining_nr == 1)
289-
continue;
290-
remaining_nr = remove_fetched_oids(repo, &remaining_oids,
291-
remaining_nr, to_free);
292-
if (remaining_nr) {
293-
to_free = 1;
294-
continue;
295-
}
296-
}
310+
/* Try accepted remotes first (those the server told us to use) */
311+
if (try_promisor_remotes(repo, &remaining_oids, &remaining_nr,
312+
&to_free, true))
313+
goto all_fetched;
314+
if (try_promisor_remotes(repo, &remaining_oids, &remaining_nr,
315+
&to_free, false))
297316
goto all_fetched;
298-
}
299317

300318
for (i = 0; i < remaining_nr; i++) {
301319
if (is_promisor_object(repo, &remaining_oids[i]))
@@ -557,6 +575,12 @@ enum accept_promisor {
557575
ACCEPT_ALL
558576
};
559577

578+
/*
579+
* Check if a specific field and its advertised value match the local
580+
* configuration of a given promisor remote.
581+
*
582+
* Returns 1 if they match, 0 otherwise.
583+
*/
560584
static int match_field_against_config(const char *field, const char *value,
561585
struct promisor_info *config_info)
562586
{
@@ -568,9 +592,18 @@ static int match_field_against_config(const char *field, const char *value,
568592
return 0;
569593
}
570594

595+
/*
596+
* Check that the advertised fields match the local configuration.
597+
*
598+
* When 'config_entry' is NULL (ACCEPT_ALL mode), every checked field
599+
* must match at least one remote in 'config_info'.
600+
*
601+
* When 'config_entry' points to a specific remote's config, the
602+
* checked fields are compared against that single remote only.
603+
*/
571604
static int all_fields_match(struct promisor_info *advertised,
572605
struct string_list *config_info,
573-
int in_list)
606+
struct promisor_info *config_entry)
574607
{
575608
struct string_list *fields = fields_checked();
576609
struct string_list_item *item_checked;
@@ -579,7 +612,6 @@ static int all_fields_match(struct promisor_info *advertised,
579612
int match = 0;
580613
const char *field = item_checked->string;
581614
const char *value = NULL;
582-
struct string_list_item *item;
583615

584616
if (!strcasecmp(field, promisor_field_filter))
585617
value = advertised->filter;
@@ -589,20 +621,18 @@ static int all_fields_match(struct promisor_info *advertised,
589621
if (!value)
590622
return 0;
591623

592-
if (in_list) {
624+
if (config_entry) {
625+
match = match_field_against_config(field, value,
626+
config_entry);
627+
} else {
628+
struct string_list_item *item;
593629
for_each_string_list_item(item, config_info) {
594630
struct promisor_info *p = item->util;
595631
if (match_field_against_config(field, value, p)) {
596632
match = 1;
597633
break;
598634
}
599635
}
600-
} else {
601-
item = string_list_lookup(config_info, advertised->name);
602-
if (item) {
603-
struct promisor_info *p = item->util;
604-
match = match_field_against_config(field, value, p);
605-
}
606636
}
607637

608638
if (!match)
@@ -612,6 +642,14 @@ static int all_fields_match(struct promisor_info *advertised,
612642
return 1;
613643
}
614644

645+
static bool has_control_char(const char *s)
646+
{
647+
for (const char *c = s; *c; c++)
648+
if (iscntrl(*c))
649+
return true;
650+
return false;
651+
}
652+
615653
static int should_accept_remote(enum accept_promisor accept,
616654
struct promisor_info *advertised,
617655
struct string_list *config_info)
@@ -621,8 +659,13 @@ static int should_accept_remote(enum accept_promisor accept,
621659
const char *remote_name = advertised->name;
622660
const char *remote_url = advertised->url;
623661

662+
if (!remote_url || !*remote_url)
663+
BUG("no or empty URL advertised for remote '%s'; "
664+
"this remote should have been rejected earlier",
665+
remote_name);
666+
624667
if (accept == ACCEPT_ALL)
625-
return all_fields_match(advertised, config_info, 1);
668+
return all_fields_match(advertised, config_info, NULL);
626669

627670
/* Get config info for that promisor remote */
628671
item = string_list_lookup(config_info, remote_name);
@@ -634,23 +677,19 @@ static int should_accept_remote(enum accept_promisor accept,
634677
p = item->util;
635678

636679
if (accept == ACCEPT_KNOWN_NAME)
637-
return all_fields_match(advertised, config_info, 0);
680+
return all_fields_match(advertised, config_info, p);
638681

639682
if (accept != ACCEPT_KNOWN_URL)
640683
BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
641684

642-
if (!remote_url || !*remote_url) {
643-
warning(_("no or empty URL advertised for remote '%s'"), remote_name);
685+
if (strcmp(p->url, remote_url)) {
686+
warning(_("known remote named '%s' but with URL '%s' instead of '%s', "
687+
"ignoring this remote"),
688+
remote_name, p->url, remote_url);
644689
return 0;
645690
}
646691

647-
if (!strcmp(p->url, remote_url))
648-
return all_fields_match(advertised, config_info, 0);
649-
650-
warning(_("known remote named '%s' but with URL '%s' instead of '%s'"),
651-
remote_name, p->url, remote_url);
652-
653-
return 0;
692+
return all_fields_match(advertised, config_info, p);
654693
}
655694

656695
static int skip_field_name_prefix(const char *elem, const char *field_name, const char **value)
@@ -691,9 +730,9 @@ static struct promisor_info *parse_one_advertised_remote(const char *remote_info
691730

692731
string_list_clear(&elem_list, 0);
693732

694-
if (!info->name || !info->url) {
695-
warning(_("server advertised a promisor remote without a name or URL: %s"),
696-
remote_info);
733+
if (!info->name || !*info->name || !info->url || !*info->url) {
734+
warning(_("server advertised a promisor remote without a name or URL: '%s', "
735+
"ignoring this remote"), remote_info);
697736
promisor_info_free(info);
698737
return NULL;
699738
}
@@ -741,18 +780,14 @@ static bool valid_filter(const char *filter, const char *remote_name)
741780
return !res;
742781
}
743782

744-
/* Check that a token doesn't contain any control character */
745783
static bool valid_token(const char *token, const char *remote_name)
746784
{
747-
const char *c = token;
748-
749-
for (; *c; c++)
750-
if (iscntrl(*c)) {
751-
warning(_("invalid token '%s' for remote '%s' "
752-
"will not be stored"),
753-
token, remote_name);
754-
return false;
755-
}
785+
if (has_control_char(token)) {
786+
warning(_("invalid token '%s' for remote '%s' "
787+
"will not be stored"),
788+
token, remote_name);
789+
return false;
790+
}
756791

757792
return true;
758793
}
@@ -827,20 +862,12 @@ static bool promisor_store_advertised_fields(struct promisor_info *advertised,
827862
return reload_config;
828863
}
829864

830-
static void filter_promisor_remote(struct repository *repo,
831-
struct strvec *accepted,
832-
const char *info)
865+
static enum accept_promisor accept_from_server(struct repository *repo)
833866
{
834867
const char *accept_str;
835868
enum accept_promisor accept = ACCEPT_NONE;
836-
struct string_list config_info = STRING_LIST_INIT_NODUP;
837-
struct string_list remote_info = STRING_LIST_INIT_DUP;
838-
struct store_info *store_info = NULL;
839-
struct string_list_item *item;
840-
bool reload_config = false;
841-
struct string_list accepted_filters = STRING_LIST_INIT_DUP;
842869

843-
if (!repo_config_get_string_tmp(the_repository, "promisor.acceptfromserver", &accept_str)) {
870+
if (!repo_config_get_string_tmp(repo, "promisor.acceptfromserver", &accept_str)) {
844871
if (!*accept_str || !strcasecmp("None", accept_str))
845872
accept = ACCEPT_NONE;
846873
else if (!strcasecmp("KnownUrl", accept_str))
@@ -854,6 +881,20 @@ static void filter_promisor_remote(struct repository *repo,
854881
accept_str, "promisor.acceptfromserver");
855882
}
856883

884+
return accept;
885+
}
886+
887+
static void filter_promisor_remote(struct repository *repo,
888+
struct string_list *accepted_remotes,
889+
const char *info)
890+
{
891+
struct string_list config_info = STRING_LIST_INIT_NODUP;
892+
struct string_list remote_info = STRING_LIST_INIT_DUP;
893+
struct store_info *store_info = NULL;
894+
struct string_list_item *item;
895+
bool reload_config = false;
896+
enum accept_promisor accept = accept_from_server(repo);
897+
857898
if (accept == ACCEPT_NONE)
858899
return;
859900

@@ -880,17 +921,10 @@ static void filter_promisor_remote(struct repository *repo,
880921
if (promisor_store_advertised_fields(advertised, store_info))
881922
reload_config = true;
882923

883-
strvec_push(accepted, advertised->name);
884-
885-
/* Capture advertised filters for accepted remotes */
886-
if (advertised->filter) {
887-
struct string_list_item *i;
888-
i = string_list_append(&accepted_filters, advertised->name);
889-
i->util = xstrdup(advertised->filter);
890-
}
924+
string_list_append(accepted_remotes, advertised->name)->util = advertised;
925+
} else {
926+
promisor_info_free(advertised);
891927
}
892-
893-
promisor_info_free(advertised);
894928
}
895929

896930
promisor_info_list_clear(&config_info);
@@ -900,47 +934,44 @@ static void filter_promisor_remote(struct repository *repo,
900934
if (reload_config)
901935
repo_promisor_remote_reinit(repo);
902936

903-
/* Apply accepted remote filters to the stable repo state */
904-
for_each_string_list_item(item, &accepted_filters) {
905-
struct promisor_remote *r = repo_promisor_remote_find(repo, item->string);
906-
if (r) {
907-
free(r->advertised_filter);
908-
r->advertised_filter = item->util;
909-
item->util = NULL;
910-
}
911-
}
937+
/* Apply accepted remotes to the stable repo state */
938+
for_each_string_list_item(item, accepted_remotes) {
939+
struct promisor_info *info = item->util;
940+
struct promisor_remote *r = repo_promisor_remote_find(repo, info->name);
912941

913-
string_list_clear(&accepted_filters, 1);
914-
915-
/* Mark the remotes as accepted in the repository state */
916-
for (size_t i = 0; i < accepted->nr; i++) {
917-
struct promisor_remote *r = repo_promisor_remote_find(repo, accepted->v[i]);
918-
if (r)
942+
if (r) {
919943
r->accepted = 1;
944+
if (info->filter) {
945+
free(r->advertised_filter);
946+
r->advertised_filter = xstrdup(info->filter);
947+
}
948+
}
920949
}
921950
}
922951

923952
void promisor_remote_reply(const char *info, char **accepted_out)
924953
{
925-
struct strvec accepted = STRVEC_INIT;
954+
struct string_list accepted_remotes = STRING_LIST_INIT_NODUP;
926955

927-
filter_promisor_remote(the_repository, &accepted, info);
956+
filter_promisor_remote(the_repository, &accepted_remotes, info);
928957

929958
if (accepted_out) {
930-
if (accepted.nr) {
959+
if (accepted_remotes.nr) {
931960
struct strbuf reply = STRBUF_INIT;
932-
for (size_t i = 0; i < accepted.nr; i++) {
933-
if (i)
961+
struct string_list_item *item;
962+
963+
for_each_string_list_item(item, &accepted_remotes) {
964+
if (reply.len)
934965
strbuf_addch(&reply, ';');
935-
strbuf_addstr_urlencode(&reply, accepted.v[i], allow_unsanitized);
966+
strbuf_addstr_urlencode(&reply, item->string, allow_unsanitized);
936967
}
937968
*accepted_out = strbuf_detach(&reply, NULL);
938969
} else {
939970
*accepted_out = NULL;
940971
}
941972
}
942973

943-
strvec_clear(&accepted);
974+
promisor_info_list_clear(&accepted_remotes);
944975
}
945976

946977
void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes)

0 commit comments

Comments
 (0)