{"thread":{"id":"55944","subject":"[PATCH 0/5] serve: add \"configure\" callback","startedAt":"2021-06-16T14:16:35Z","lastAt":"2021-08-24T16:55:12Z","messageCount":58,"participants":["Ævar Arnfjörð Bjarmason","Jeff King","Eric Sunshine","Junio C Hamano","Han-Wen Nienhuys","Josh Steadmon","Carlo Marcelo Arenas Belón","Derrick Stolee"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"427610","messageId":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","threadId":"55944","inReplyTo":null,"subject":"[PATCH 0/5] serve: add \"configure\" callback","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-16T14:16:16Z","receivedAt":"2021-06-16T14:16:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This is a refactoring of what a callback in serve.c needs to do to\naquire config. Currently two of them want that, and grab it in ad-hoc\nways themselves, now they can insted configure a \"configure\" callback\nalong with the existing \"advertise\" and \"command\", by the time they're\ncalled their config will already be read with their callback.\n\nI split this prep work off from an upcoming series where I wanted to\nadd a new capability, but I think this stands nicely on its own, and\nsimplifies the existing code.\n\nThe line count increase is mostly converting things to designated\ninitializers.\n\nÆvar Arnfjörð Bjarmason (5):\n  serve: mark has_capability() as static\n  transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n  transport: use designated initializers\n  serve: use designated initializers\n  serve: add support for a git_config() callback\n\n ls-refs.c            | 55 +++++++++++---------------------\n ls-refs.h            |  1 +\n serve.c              | 76 ++++++++++++++++++++++++++++++++++++++------\n serve.h              |  3 --\n transport-helper.c   | 18 +++++------\n transport-internal.h |  2 +-\n transport.c          | 32 ++++++++-----------\n 7 files changed, 108 insertions(+), 79 deletions(-)\n\n-- \n2.32.0.576.g59759b6ca7d\n\n"},{"id":"427611","messageId":"patch-1.5-4f74d7d34c4-20210616T141332Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"[PATCH 1/5] serve: mark has_capability() as static","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-16T14:16:17Z","receivedAt":"2021-06-16T14:16:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The has_capability() function introduced in ed10cb952d3 (serve:\nintroduce git-serve, 2018-03-15) has never been used anywhere except\nserve.c, so let's mark it as static.\n\nIt was later changed from \"extern\" in 554544276a6 (*.[ch]: remove\nextern from function declarations using spatch, 2019-04-29), but we\ncould have simply marked it as \"static\" instead.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 4 ++--\n serve.h | 3 ---\n 2 files changed, 2 insertions(+), 5 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex aa8209f147e..6748c590b74 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -156,8 +156,8 @@ static int is_command(const char *key, struct protocol_capability **command)\n \treturn 0;\n }\n \n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value)\n+static int has_capability(const struct strvec *keys, const char *capability,\n+\t\t\t  const char **value)\n {\n \tint i;\n \tfor (i = 0; i < keys->nr; i++) {\ndiff --git a/serve.h b/serve.h\nindex fc2683e24d3..b31dd69434b 100644\n--- a/serve.h\n+++ b/serve.h\n@@ -2,9 +2,6 @@\n #define SERVE_H\n \n struct strvec;\n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value);\n-\n struct serve_options {\n \tunsigned advertise_capabilities;\n \tunsigned stateless_rpc;\n-- \n2.32.0.576.g59759b6ca7d\n\n"},{"id":"427612","messageId":"patch-2.5-c6720d1bf33-20210616T141332Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"[PATCH 2/5] transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-16T14:16:18Z","receivedAt":"2021-06-16T14:16:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"fetch\" member of the transport_vtable to \"fetch_refs\" for\nconsistency with the existing \"push_refs\". Neither of them just push\n\"refs\" but refs and objects, but having the two match makes the code\nmore readable than having it be inconsistent, especially since\n\"fetch_refs\" is a lot easier to grep for than \"fetch\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c   | 8 ++++----\n transport-internal.h | 2 +-\n transport.c          | 2 +-\n 3 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 4be035edb8b..8d445a8f3ee 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -671,8 +671,8 @@ static int connect_helper(struct transport *transport, const char *name,\n static struct ref *get_refs_list_using_list(struct transport *transport,\n \t\t\t\t\t    int for_push);\n \n-static int fetch(struct transport *transport,\n-\t\t int nr_heads, struct ref **to_fetch)\n+static int fetch_refs(struct transport *transport,\n+\t\t      int nr_heads, struct ref **to_fetch)\n {\n \tstruct helper_data *data = transport->data;\n \tint i, count;\n@@ -681,7 +681,7 @@ static int fetch(struct transport *transport,\n \n \tif (process_connect(transport, 0)) {\n \t\tdo_take_over(transport);\n-\t\treturn transport->vtable->fetch(transport, nr_heads, to_fetch);\n+\t\treturn transport->vtable->fetch_refs(transport, nr_heads, to_fetch);\n \t}\n \n \t/*\n@@ -1263,7 +1263,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n static struct transport_vtable vtable = {\n \tset_helper_option,\n \tget_refs_list,\n-\tfetch,\n+\tfetch_refs,\n \tpush_refs,\n \tconnect_helper,\n \trelease_helper\ndiff --git a/transport-internal.h b/transport-internal.h\nindex b60f1ba9077..c4ca0b733ac 100644\n--- a/transport-internal.h\n+++ b/transport-internal.h\n@@ -34,7 +34,7 @@ struct transport_vtable {\n \t * get_refs_list(), it should set the old_sha1 fields in the\n \t * provided refs now.\n \t **/\n-\tint (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);\n+\tint (*fetch_refs)(struct transport *transport, int refs_nr, struct ref **refs);\n \n \t/**\n \t * Push the objects and refs. Send the necessary objects, and\ndiff --git a/transport.c b/transport.c\nindex 50f5830eb6b..f60985e2dbd 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1449,7 +1449,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)\n \t\t\theads[nr_heads++] = rm;\n \t}\n \n-\trc = transport->vtable->fetch(transport, nr_heads, heads);\n+\trc = transport->vtable->fetch_refs(transport, nr_heads, heads);\n \n \tfree(heads);\n \treturn rc;\n-- \n2.32.0.576.g59759b6ca7d\n\n"},{"id":"427613","messageId":"patch-3.5-369efe0eed5-20210616T141332Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"[PATCH 3/5] transport: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-16T14:16:19Z","receivedAt":"2021-06-16T14:16:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the assignments to the various transport_vtables to use\ndesignated initializers, this makes the code easier to read and\nmaintain.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c | 12 ++++++------\n transport.c        | 30 ++++++++++++------------------\n 2 files changed, 18 insertions(+), 24 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 8d445a8f3ee..e8dbdd11530 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -1261,12 +1261,12 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n }\n \n static struct transport_vtable vtable = {\n-\tset_helper_option,\n-\tget_refs_list,\n-\tfetch_refs,\n-\tpush_refs,\n-\tconnect_helper,\n-\trelease_helper\n+\t.set_option\t= set_helper_option,\n+\t.get_refs_list\t= get_refs_list,\n+\t.fetch_refs\t= fetch_refs,\n+\t.push_refs\t= push_refs,\n+\t.connect\t= connect_helper,\n+\t.disconnect\t= release_helper\n };\n \n int transport_helper_init(struct transport *transport, const char *name)\ndiff --git a/transport.c b/transport.c\nindex f60985e2dbd..bdd9b5a93cf 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -880,12 +880,10 @@ static int disconnect_git(struct transport *transport)\n }\n \n static struct transport_vtable taken_over_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tNULL,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.disconnect\t= disconnect_git\n };\n \n void transport_take_over(struct transport *transport,\n@@ -1029,21 +1027,17 @@ void transport_check_allowed(const char *type)\n }\n \n static struct transport_vtable bundle_vtable = {\n-\tNULL,\n-\tget_refs_from_bundle,\n-\tfetch_refs_from_bundle,\n-\tNULL,\n-\tNULL,\n-\tclose_bundle\n+\t.get_refs_list\t= get_refs_from_bundle,\n+\t.fetch_refs\t= fetch_refs_from_bundle,\n+\t.disconnect\t= close_bundle\n };\n \n static struct transport_vtable builtin_smart_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tconnect_git,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.connect\t= connect_git,\n+\t.disconnect\t= disconnect_git\n };\n \n struct transport *transport_get(struct remote *remote, const char *url)\n-- \n2.32.0.576.g59759b6ca7d\n\n"},{"id":"427614","messageId":"patch-5.5-c8625025125-20210616T141332Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"[PATCH 5/5] serve: add support for a git_config() callback","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-16T14:16:21Z","receivedAt":"2021-06-16T14:16:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the introduction of serve.c we've added git_config() callbacks\nand other config reading for capabilities in the following commits:\n\n- e20b4192a37 (upload-pack: support hidden refs with protocol v2, 2018-12-18)\n- 59e1205d167 (ls-refs: report unborn targets of symrefs, 2021-02-05)\n- 6b5b6e422ee (serve: advertise session ID in v2 capabilities, 2020-11-11)\n\nThis code can be simplified by simply adding support to serve.c itself\nfor reading config at the right time before the capability is\ncalled. In particular the ensure_config_read() function being gone\nmakes this whole thing much simpler.\n\nA behavior change here is that we're eagerly doing the configuration\nfor all our capabilities regardless of which one we end up serving,\nwhereas before we'd defer it until the point where we were processing\nthe specific command.\n\nWe could try to be more lazy here and only read the config if we find\nout that we're serving \"ls-refs\" or \"session-id\", but it's just a fast\ngit_config() callback, I think in this case simplicity trumps a\npremature optimization.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n ls-refs.c | 55 ++++++++++++++++++-------------------------------------\n ls-refs.h |  1 +\n serve.c   | 34 +++++++++++++++++++++++++++++++++-\n 3 files changed, 52 insertions(+), 38 deletions(-)\n\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 88f6c3f60d8..a8a3e90bd71 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -7,38 +7,9 @@\n #include \"pkt-line.h\"\n #include \"config.h\"\n \n-static int config_read;\n-static int advertise_unborn;\n-static int allow_unborn;\n-\n-static void ensure_config_read(void)\n-{\n-\tconst char *str = NULL;\n-\n-\tif (config_read)\n-\t\treturn;\n-\n-\tif (repo_config_get_string_tmp(the_repository, \"lsrefs.unborn\", &str)) {\n-\t\t/*\n-\t\t * If there is no such config, advertise and allow it by\n-\t\t * default.\n-\t\t */\n-\t\tadvertise_unborn = 1;\n-\t\tallow_unborn = 1;\n-\t} else {\n-\t\tif (!strcmp(str, \"advertise\")) {\n-\t\t\tadvertise_unborn = 1;\n-\t\t\tallow_unborn = 1;\n-\t\t} else if (!strcmp(str, \"allow\")) {\n-\t\t\tallow_unborn = 1;\n-\t\t} else if (!strcmp(str, \"ignore\")) {\n-\t\t\t/* do nothing */\n-\t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n-\t\t}\n-\t}\n-\tconfig_read = 1;\n-}\n+/* \"unborn\" is on by default if there's no lsrefs.unborn config */\n+static int advertise_unborn = 1;\n+static int allow_unborn = 1;\n \n /*\n  * Check if one of the prefixes is a prefix of the ref.\n@@ -128,8 +99,22 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \tstrbuf_release(&namespaced);\n }\n \n-static int ls_refs_config(const char *var, const char *value, void *data)\n+int ls_refs_configure(const char *var, const char *value, void *data)\n {\n+\tif (!strcmp(var, \"lsrefs.unborn\")) {\n+\t\tif (!strcmp(value, \"advertise\")) {\n+\t\t\t/* Allowed and advertised by default */\n+\t\t} else if (!strcmp(value, \"allow\")) {\n+\t\t\tadvertise_unborn = 0;\n+\t\t\tallow_unborn = 1;\n+\t\t} else if (!strcmp(value, \"ignore\")) {\n+\t\t\tadvertise_unborn = 0;\n+\t\t\tallow_unborn = 0;\n+\t\t} else {\n+\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), value);\n+\t\t}\n+\t}\n+\n \t/*\n \t * We only serve fetches over v2 for now, so respect only \"uploadpack\"\n \t * config. This may need to eventually be expanded to \"receive\", but we\n@@ -146,9 +131,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n \tmemset(&data, 0, sizeof(data));\n \tstrvec_init(&data.prefixes);\n \n-\tensure_config_read();\n-\tgit_config(ls_refs_config, NULL);\n-\n \twhile (packet_reader_read(request) == PACKET_READ_NORMAL) {\n \t\tconst char *arg = request->line;\n \t\tconst char *out;\n@@ -179,7 +161,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n int ls_refs_advertise(struct repository *r, struct strbuf *value)\n {\n \tif (value) {\n-\t\tensure_config_read();\n \t\tif (advertise_unborn)\n \t\t\tstrbuf_addstr(value, \"unborn\");\n \t}\ndiff --git a/ls-refs.h b/ls-refs.h\nindex a99e4be0bdd..afa6dfeb259 100644\n--- a/ls-refs.h\n+++ b/ls-refs.h\n@@ -6,6 +6,7 @@ struct strvec;\n struct packet_reader;\n int ls_refs(struct repository *r, struct strvec *keys,\n \t    struct packet_reader *request);\n+int ls_refs_configure(const char *var, const char *value, void *data);\n int ls_refs_advertise(struct repository *r, struct strbuf *value);\n \n #endif /* LS_REFS_H */\ndiff --git a/serve.c b/serve.c\nindex 49ea9fc8fd5..62cb8aeacdb 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -33,6 +33,13 @@ static int object_format_advertise(struct repository *r,\n \treturn 1;\n }\n \n+static int session_id_configure(const char *var, const char *value, void *data)\n+{\n+\tif (!strcmp(var, \"transfer.advertisesid\"))\n+\t\tadvertise_sid = git_config_bool(var, value);\n+\treturn 0;\n+}\n+\n static int session_id_advertise(struct repository *r, struct strbuf *value)\n {\n \tif (!advertise_sid)\n@@ -50,6 +57,13 @@ struct protocol_capability {\n \t */\n \tconst char *name;\n \n+\t/*\n+\t * A git_config() callback. If defined it'll be dispatched too\n+\t * sometime before the other functions are called, giving the\n+\t * capability a chance to configure itself.\n+\t */\n+\tint (*configure)(const char *var, const char *value, void *data);\n+\n \t/*\n \t * Function queried to see if a capability should be advertised.\n \t * Optionally a value can be specified by adding it to 'value'.\n@@ -79,6 +93,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"ls-refs\",\n+\t\t.configure = ls_refs_configure,\n \t\t.advertise = ls_refs_advertise,\n \t\t.command = ls_refs,\n \t},\n@@ -97,6 +112,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"session-id\",\n+\t\t.configure = session_id_configure,\n \t\t.advertise = session_id_advertise,\n \t},\n \t{\n@@ -106,6 +122,22 @@ static struct protocol_capability capabilities[] = {\n \t},\n };\n \n+static int git_serve_config(const char *var, const char *value, void *data)\n+{\n+\tint i;\n+\tfor (i = 0; i < ARRAY_SIZE(capabilities); i++) {\n+\t\tstruct protocol_capability *c = &capabilities[i];\n+\t\tconfig_fn_t fn = c->configure;\n+\t\tint ret;\n+\t\tif (!fn)\n+\t\t\tcontinue;\n+\t\tret = fn(var, value, data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\treturn 0;\n+}\n+\n static void advertise_capabilities(void)\n {\n \tstruct strbuf capability = STRBUF_INIT;\n@@ -303,7 +335,7 @@ static int process_request(void)\n /* Main serve loop for protocol version 2 */\n void serve(struct serve_options *options)\n {\n-\tgit_config_get_bool(\"transfer.advertisesid\", &advertise_sid);\n+\tgit_config(git_serve_config, NULL);\n \n \tif (options->advertise_capabilities || !options->stateless_rpc) {\n \t\t/* serve by default supports v2 */\n-- \n2.32.0.576.g59759b6ca7d\n\n"},{"id":"427615","messageId":"patch-4.5-8e97412d584-20210616T141332Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"[PATCH 4/5] serve: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-16T14:16:20Z","receivedAt":"2021-06-16T14:16:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the declaration of the protocol_capability struct to use\ndesignated initializers, this makes this more verbose now, but a\nfollow-up commit will add a new field. At that point these lines would\nbe too dense to be on one line comfortably.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 38 +++++++++++++++++++++++++++++++-------\n 1 file changed, 31 insertions(+), 7 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 6748c590b74..49ea9fc8fd5 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -73,13 +73,37 @@ struct protocol_capability {\n };\n \n static struct protocol_capability capabilities[] = {\n-\t{ \"agent\", agent_advertise, NULL },\n-\t{ \"ls-refs\", ls_refs_advertise, ls_refs },\n-\t{ \"fetch\", upload_pack_advertise, upload_pack_v2 },\n-\t{ \"server-option\", always_advertise, NULL },\n-\t{ \"object-format\", object_format_advertise, NULL },\n-\t{ \"session-id\", session_id_advertise, NULL },\n-\t{ \"object-info\", always_advertise, cap_object_info },\n+\t{\n+\t\t.name = \"agent\",\n+\t\t.advertise = agent_advertise,\n+\t},\n+\t{\n+\t\t.name = \"ls-refs\",\n+\t\t.advertise = ls_refs_advertise,\n+\t\t.command = ls_refs,\n+\t},\n+\t{\n+\t\t.name = \"fetch\",\n+\t\t.advertise = upload_pack_advertise,\n+\t\t.command = upload_pack_v2,\n+\t},\n+\t{\n+\t\t.name = \"server-option\",\n+\t\t.advertise = always_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-format\",\n+\t\t.advertise = object_format_advertise,\n+\t},\n+\t{\n+\t\t.name = \"session-id\",\n+\t\t.advertise = session_id_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-info\",\n+\t\t.advertise = always_advertise,\n+\t\t.command = cap_object_info,\n+\t},\n };\n \n static void advertise_capabilities(void)\n-- \n2.32.0.576.g59759b6ca7d\n\n"},{"id":"427620","messageId":"YMolS8iJAMgbbg9D@coredump.intra.peff.net","threadId":"55944","inReplyTo":"patch-5.5-c8625025125-20210616T141332Z-avarab@gmail.com","subject":"Re: [PATCH 5/5] serve: add support for a git_config() callback","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-06-16T16:22:35Z","receivedAt":"2021-06-16T16:23:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 16, 2021 at 04:16:21PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> Since the introduction of serve.c we've added git_config() callbacks\n> and other config reading for capabilities in the following commits:\n> \n> - e20b4192a37 (upload-pack: support hidden refs with protocol v2, 2018-12-18)\n> - 59e1205d167 (ls-refs: report unborn targets of symrefs, 2021-02-05)\n> - 6b5b6e422ee (serve: advertise session ID in v2 capabilities, 2020-11-11)\n> \n> This code can be simplified by simply adding support to serve.c itself\n> for reading config at the right time before the capability is\n> called. In particular the ensure_config_read() function being gone\n> makes this whole thing much simpler.\n> \n> A behavior change here is that we're eagerly doing the configuration\n> for all our capabilities regardless of which one we end up serving,\n> whereas before we'd defer it until the point where we were processing\n> the specific command.\n\nHmm. I like simplifying things, but what's the plan for when we have a\nv2 receive-pack? We'll need different config based on whether we're\nserving a fetch or a push.\n\nWhile working on e20b4192a37, I had originally proposed patches that\nwould pass that context into the serve mechanism (based on whether we\nwere called by upload-pack or receive-pack). But the review there\nindicated that v2 receive-pack would probably involve an extra\ncapability argument passed by the client to ls-refs.\n\nSo we'd eventually need to parse config _after_ we've received the\nactual ls-refs command. To be clear, this patch isn't breaking anything\nnow. But I think it's painting us into a corner that we'll regret later.\n\nOf course there are ways around this. E.g., we could read config for\nboth operations ahead of time, and then decide which to use later. But\nthat definitely isn't how it works now, and we'd have to refactor the\nwhole ref_is_hidden() system to fix that. Definitely do-able, but given\nthe relatively limited upside of this patch, I'm not sure if it's worth\nit.\n\nI'd also note that this patch doesn't touch upload_pack_v2() at all,\nwhich also reads config. That one is extra-weird because it reads the\nconfig fresh for every round of interaction with the client, and then\ncleans it up. Which led to fixes like the one in 08450ef791\n(upload-pack: clear filter_options for each v2 fetch command,\n2020-05-08).\n\nDoing a single config read would probably make that simpler. But the\nconfig setup is shared between the v0 and v2 functions, so extracting it\ninto serve() would potentially be awkward.\n\n> In particular the ensure_config_read() function being gone\n> makes this whole thing much simpler.\n\nI do agree this is awkward. The two calls for that function seem to come\nfrom the \"should we advertise ls-refs\" and \"actually do ls-refs\"\nfunctions. Should we be able to assume that the former ran if we are in\nthe latter? I'm not sure if that's true or not (i.e., because it's\nrare but possible for a client to make two separate HTTP requests, one\nto discover our v2 capabilities and another to actually issue ls-refs).\n\n-Peff\n"},{"id":"427621","messageId":"YMoljEd/0Nv19dl7@coredump.intra.peff.net","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"Re: [PATCH 0/5] serve: add \"configure\" callback","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-06-16T16:23:40Z","receivedAt":"2021-06-16T16:23:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 16, 2021 at 04:16:16PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> This is a refactoring of what a callback in serve.c needs to do to\n> aquire config. Currently two of them want that, and grab it in ad-hoc\n> ways themselves, now they can insted configure a \"configure\" callback\n> along with the existing \"advertise\" and \"command\", by the time they're\n> called their config will already be read with their callback.\n> \n> I split this prep work off from an upcoming series where I wanted to\n> add a new capability, but I think this stands nicely on its own, and\n> simplifies the existing code.\n\nThe first four seem like obviously-good cleanups. I'm not so sure\nabout the last one, though. I left some comments there.\n\n-Peff\n"},{"id":"427622","messageId":"CAPig+cQLRGL+jUkJi9n8_6LUpzxn-1XiCyvzCgiY38wjTi+TbQ@mail.gmail.com","threadId":"55944","inReplyTo":"patch-1.5-4f74d7d34c4-20210616T141332Z-avarab@gmail.com","subject":"Re: [PATCH 1/5] serve: mark has_capability() as static","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-06-16T16:28:01Z","receivedAt":"2021-06-16T16:28:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 16, 2021 at 10:16 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> The has_capability() function introduced in ed10cb952d3 (serve:\n> introduce git-serve, 2018-03-15) has never been used anywhere except\n> serve.c, so let's mark it as static.\n>\n> It was later changed from \"extern\" in 554544276a6 (*.[ch]: remove\n> extern from function declarations using spatch, 2019-04-29), but we\n> could have simply marked it as \"static\" instead.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/serve.h b/serve.h\n> @@ -2,9 +2,6 @@\n>  struct strvec;\n> -int has_capability(const struct strvec *keys, const char *capability,\n> -                  const char **value);\n> -\n\n`strvec` isn't used anywhere in this header following removal of\nhas_capability(), so the forward declaration could be dropped, as\nwell.\n"},{"id":"427647","messageId":"xmqqczslnwsw.fsf@gitster.g","threadId":"55944","inReplyTo":"CAPig+cQLRGL+jUkJi9n8_6LUpzxn-1XiCyvzCgiY38wjTi+TbQ@mail.gmail.com","subject":"Re: [PATCH 1/5] serve: mark has_capability() as static","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-17T00:45:35Z","receivedAt":"2021-06-17T00:45:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Wed, Jun 16, 2021 at 10:16 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> The has_capability() function introduced in ed10cb952d3 (serve:\n>> introduce git-serve, 2018-03-15) has never been used anywhere except\n>> serve.c, so let's mark it as static.\n>>\n>> It was later changed from \"extern\" in 554544276a6 (*.[ch]: remove\n>> extern from function declarations using spatch, 2019-04-29), but we\n>> could have simply marked it as \"static\" instead.\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>> diff --git a/serve.h b/serve.h\n>> @@ -2,9 +2,6 @@\n>>  struct strvec;\n>> -int has_capability(const struct strvec *keys, const char *capability,\n>> -                  const char **value);\n>> -\n>\n> `strvec` isn't used anywhere in this header following removal of\n> has_capability(), so the forward declaration could be dropped, as\n> well.\n\nGood eyes.\n\n"},{"id":"427648","messageId":"xmqq8s39nwln.fsf@gitster.g","threadId":"55944","inReplyTo":"YMoljEd/0Nv19dl7@coredump.intra.peff.net","subject":"Re: [PATCH 0/5] serve: add \"configure\" callback","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-17T00:49:56Z","receivedAt":"2021-06-17T00:50:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Wed, Jun 16, 2021 at 04:16:16PM +0200, Ævar Arnfjörð Bjarmason wrote:\n>\n>> This is a refactoring of what a callback in serve.c needs to do to\n>> aquire config. Currently two of them want that, and grab it in ad-hoc\n>> ways themselves, now they can insted configure a \"configure\" callback\n>> along with the existing \"advertise\" and \"command\", by the time they're\n>> called their config will already be read with their callback.\n>> \n>> I split this prep work off from an upcoming series where I wanted to\n>> add a new capability, but I think this stands nicely on its own, and\n>> simplifies the existing code.\n>\n> The first four seem like obviously-good cleanups. I'm not so sure\n> about the last one, though. I left some comments there.\n\nI had the same impression.  Thanks, both.\n"},{"id":"428626","messageId":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.5-00000000000-20210616T141332Z-avarab@gmail.com","subject":"[PATCH v2 0/8] serve: add \"startup_config\" callback","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:17Z","receivedAt":"2021-06-28T19:19:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As Jeff pointed out on v1 the control flow around serve.c was more\nsubtle than I expected, i.e. we might process N requests:\nhttp://lore.kernel.org/git/YMolS8iJAMgbbg9D@coredump.intra.peff.net\n\nThis v2 has a 1-4/8 that's a trivially improved version of what Junio\npicked up for \"seen\", the only change is addressing Eric's feedback\nthat the \"struct strvec;\" could also be removed.\n\nThen 5-6/8 is a new trivial refactoring of the various callback\ninvocations into a rapper function, allowing us to add a trace2 region\nfor the advertisements & command we run.\n\nAfter that 7-8/8 is a new approach of doing this \"configure\" callback,\nI was on the fence about whether it should be another series on top,\nbut decided to submit it as one thing.\n\nNow instead of there being a \"configure\" we explicitly have a\n\"startup_config\" where we expect to stick all our config that doesn't\nchange, won't be different depending on what \"mode\" or \"command\" we\noperate as, and in the case of upload-pack doesn't depend on anything\nin \"upload_pack_data\".\n\nAs the diffstat shows it doesn't make the code shorter, but I think it\nmakes it easier to read and maintain, as we now clearly separate the\ntypes of config we're reading.\n\nÆvar Arnfjörð Bjarmason (8):\n  serve: mark has_capability() as static\n  transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n  transport: use designated initializers\n  serve: use designated initializers\n  serve.c: add call_{advertise,command}() indirection\n  serve.c: add trace2 regions for advertise & command\n  serve: add support for a \"startup\" git_config() callback\n  upload-pack.c: convert to new serve.c \"startup\" config cb\n\n ls-refs.c                             |  42 +++-----\n ls-refs.h                             |   1 +\n serve.c                               | 138 +++++++++++++++++++++----\n serve.h                               |   4 -\n t/t5705-session-id-in-capabilities.sh |  16 ++-\n transport-helper.c                    |  18 ++--\n transport-internal.h                  |   2 +-\n transport.c                           |  32 +++---\n upload-pack.c                         | 139 +++++++++++++++-----------\n upload-pack.h                         |   2 +\n 10 files changed, 255 insertions(+), 139 deletions(-)\n\nRange-diff against v1:\n1:  4f74d7d34c4 ! 1:  fdb0c5f4df1 serve: mark has_capability() as static\n    @@ serve.c: static int is_command(const char *key, struct protocol_capability **com\n     \n      ## serve.h ##\n     @@\n    + #ifndef SERVE_H\n      #define SERVE_H\n      \n    - struct strvec;\n    +-struct strvec;\n     -int has_capability(const struct strvec *keys, const char *capability,\n     -\t\t   const char **value);\n     -\n2:  c6720d1bf33 = 2:  7b8101dca71 transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n3:  369efe0eed5 = 3:  766045e5f1d transport: use designated initializers\n4:  8e97412d584 = 4:  bd920de1629 serve: use designated initializers\n-:  ----------- > 5:  d0b02aa0c7d serve.c: add call_{advertise,command}() indirection\n-:  ----------- > 6:  baeee6539ad serve.c: add trace2 regions for advertise & command\n5:  c8625025125 ! 7:  0a4fb01ae38 serve: add support for a git_config() callback\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    serve: add support for a git_config() callback\n    +    serve: add support for a \"startup\" git_config() callback\n     \n         Since the introduction of serve.c we've added git_config() callbacks\n         and other config reading for capabilities in the following commits:\n     \n         - e20b4192a37 (upload-pack: support hidden refs with protocol v2, 2018-12-18)\n    -    - 59e1205d167 (ls-refs: report unborn targets of symrefs, 2021-02-05)\n    +    - 08450ef7918 (upload-pack: clear filter_options for each v2 fetch command, 2020-05-08)\n         - 6b5b6e422ee (serve: advertise session ID in v2 capabilities, 2020-11-11)\n    +    - 59e1205d167 (ls-refs: report unborn targets of symrefs, 2021-02-05)\n     \n    -    This code can be simplified by simply adding support to serve.c itself\n    -    for reading config at the right time before the capability is\n    -    called. In particular the ensure_config_read() function being gone\n    -    makes this whole thing much simpler.\n    +    Of these 08450ef7918 fixed code that needed to read config on a\n    +    per-request basis, whereas most of the config reading just wants to\n    +    check if we've enabled one semi-static config variable or other. We'd\n    +    like to re-read that value eventually, but from request-to-request\n    +    it's OK if we retain the old one, and it isn't impacted by other\n    +    request data.\n     \n    -    A behavior change here is that we're eagerly doing the configuration\n    -    for all our capabilities regardless of which one we end up serving,\n    -    whereas before we'd defer it until the point where we were processing\n    -    the specific command.\n    +    So let's support this common pattern as a \"startup_config\" callback,\n    +    making use of our recently added \"call_{advertise,command}()\"\n    +    functions. This allows us to simplify e.g. the \"ensure_config_read()\"\n    +    function added in 59e1205d167 (ls-refs: report unborn targets of\n    +    symrefs, 2021-02-05).\n     \n    -    We could try to be more lazy here and only read the config if we find\n    -    out that we're serving \"ls-refs\" or \"session-id\", but it's just a fast\n    -    git_config() callback, I think in this case simplicity trumps a\n    -    premature optimization.\n    +    We could read all the config for all the protocol capabilities, but\n    +    let's do it one callback at a time in anticipation that some won't be\n    +    called at all, and that some might be more expensive than others in\n    +    the future.\n    +\n    +    I'm not migrating over the code in the upload_pack_v2 function in\n    +    upload-pack.c yet, that case is more complex since it deals with both\n    +    v1 and v2. It will be dealt with in a code a subsequent commit.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ ls-refs.c\n     -static int config_read;\n     -static int advertise_unborn;\n     -static int allow_unborn;\n    --\n    ++/* \"unborn\" is on by default if there's no lsrefs.unborn config */\n    ++static int advertise_unborn = 1;\n    ++static int allow_unborn = 1;\n    + \n     -static void ensure_config_read(void)\n    --{\n    ++int ls_refs_startup_config(const char *var, const char *value, void *data)\n    + {\n     -\tconst char *str = NULL;\n     -\n     -\tif (config_read)\n    @@ ls-refs.c\n     -\t} else {\n     -\t\tif (!strcmp(str, \"advertise\")) {\n     -\t\t\tadvertise_unborn = 1;\n    --\t\t\tallow_unborn = 1;\n    --\t\t} else if (!strcmp(str, \"allow\")) {\n    --\t\t\tallow_unborn = 1;\n    --\t\t} else if (!strcmp(str, \"ignore\")) {\n    --\t\t\t/* do nothing */\n    --\t\t} else {\n    --\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n    --\t\t}\n    --\t}\n    --\tconfig_read = 1;\n    --}\n    -+/* \"unborn\" is on by default if there's no lsrefs.unborn config */\n    -+static int advertise_unborn = 1;\n    -+static int allow_unborn = 1;\n    - \n    - /*\n    -  * Check if one of the prefixes is a prefix of the ref.\n    -@@ ls-refs.c: static void send_possibly_unborn_head(struct ls_refs_data *data)\n    - \tstrbuf_release(&namespaced);\n    - }\n    - \n    --static int ls_refs_config(const char *var, const char *value, void *data)\n    -+int ls_refs_configure(const char *var, const char *value, void *data)\n    - {\n     +\tif (!strcmp(var, \"lsrefs.unborn\")) {\n     +\t\tif (!strcmp(value, \"advertise\")) {\n     +\t\t\t/* Allowed and advertised by default */\n     +\t\t} else if (!strcmp(value, \"allow\")) {\n     +\t\t\tadvertise_unborn = 0;\n    -+\t\t\tallow_unborn = 1;\n    + \t\t\tallow_unborn = 1;\n    +-\t\t} else if (!strcmp(str, \"allow\")) {\n    +-\t\t\tallow_unborn = 1;\n    +-\t\t} else if (!strcmp(str, \"ignore\")) {\n    +-\t\t\t/* do nothing */\n     +\t\t} else if (!strcmp(value, \"ignore\")) {\n     +\t\t\tadvertise_unborn = 0;\n     +\t\t\tallow_unborn = 0;\n    -+\t\t} else {\n    + \t\t} else {\n    +-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n     +\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), value);\n    -+\t\t}\n    -+\t}\n    -+\n    - \t/*\n    - \t * We only serve fetches over v2 for now, so respect only \"uploadpack\"\n    - \t * config. This may need to eventually be expanded to \"receive\", but we\n    + \t\t}\n    + \t}\n    +-\tconfig_read = 1;\n    ++\treturn 0;\n    + }\n    + \n    + /*\n     @@ ls-refs.c: int ls_refs(struct repository *r, struct strvec *keys,\n    + \n      \tmemset(&data, 0, sizeof(data));\n      \tstrvec_init(&data.prefixes);\n    - \n    --\tensure_config_read();\n    --\tgit_config(ls_refs_config, NULL);\n     -\n    +-\tensure_config_read();\n    + \tgit_config(ls_refs_config, NULL);\n    + \n      \twhile (packet_reader_read(request) == PACKET_READ_NORMAL) {\n    - \t\tconst char *arg = request->line;\n    - \t\tconst char *out;\n     @@ ls-refs.c: int ls_refs(struct repository *r, struct strvec *keys,\n      int ls_refs_advertise(struct repository *r, struct strbuf *value)\n      {\n    @@ ls-refs.h: struct strvec;\n      struct packet_reader;\n      int ls_refs(struct repository *r, struct strvec *keys,\n      \t    struct packet_reader *request);\n    -+int ls_refs_configure(const char *var, const char *value, void *data);\n    ++int ls_refs_startup_config(const char *var, const char *value, void *data);\n      int ls_refs_advertise(struct repository *r, struct strbuf *value);\n      \n      #endif /* LS_REFS_H */\n    @@ serve.c: static int object_format_advertise(struct repository *r,\n      \treturn 1;\n      }\n      \n    -+static int session_id_configure(const char *var, const char *value, void *data)\n    ++static int session_id_startup_config(const char *var, const char *value, void *data)\n     +{\n     +\tif (!strcmp(var, \"transfer.advertisesid\"))\n     +\t\tadvertise_sid = git_config_bool(var, value);\n    @@ serve.c: struct protocol_capability {\n      \tconst char *name;\n      \n     +\t/*\n    -+\t * A git_config() callback. If defined it'll be dispatched too\n    -+\t * sometime before the other functions are called, giving the\n    -+\t * capability a chance to configure itself.\n    ++\t * A git_config() callback that'll be called only once for the\n    ++\t * lifetime of the process, possibly over many different\n    ++\t * requests. Used for reading config that's expected to be\n    ++\t * static.\n    ++\t *\n    ++\t * The \"command\" or \"advertise\" callbacks themselves are\n    ++\t * expected to read config that needs to be more current than\n    ++\t * that, or which is dependent on request data.\n     +\t */\n    -+\tint (*configure)(const char *var, const char *value, void *data);\n    ++\tint (*startup_config)(const char *var, const char *value, void *data);\n    ++\n    ++\t/*\n    ++\t * A boolean to check if we've called our \"startup_config\"\n    ++\t * callback.\n    ++\t */\n    ++\tint have_startup_config;\n     +\n      \t/*\n      \t * Function queried to see if a capability should be advertised.\n    @@ serve.c: static struct protocol_capability capabilities[] = {\n      \t},\n      \t{\n      \t\t.name = \"ls-refs\",\n    -+\t\t.configure = ls_refs_configure,\n    ++\t\t.startup_config = ls_refs_startup_config,\n      \t\t.advertise = ls_refs_advertise,\n      \t\t.command = ls_refs,\n      \t},\n    @@ serve.c: static struct protocol_capability capabilities[] = {\n      \t},\n      \t{\n      \t\t.name = \"session-id\",\n    -+\t\t.configure = session_id_configure,\n    ++\t\t.startup_config = session_id_startup_config,\n      \t\t.advertise = session_id_advertise,\n      \t},\n      \t{\n    @@ serve.c: static struct protocol_capability capabilities[] = {\n      \t},\n      };\n      \n    -+static int git_serve_config(const char *var, const char *value, void *data)\n    ++static void read_startup_config(struct protocol_capability *command)\n     +{\n    -+\tint i;\n    -+\tfor (i = 0; i < ARRAY_SIZE(capabilities); i++) {\n    -+\t\tstruct protocol_capability *c = &capabilities[i];\n    -+\t\tconfig_fn_t fn = c->configure;\n    -+\t\tint ret;\n    -+\t\tif (!fn)\n    -+\t\t\tcontinue;\n    -+\t\tret = fn(var, value, data);\n    -+\t\tif (ret)\n    -+\t\t\treturn ret;\n    -+\t}\n    -+\treturn 0;\n    ++\tif (!command->startup_config)\n    ++\t\treturn;\n    ++\tif (command->have_startup_config++)\n    ++\t\treturn;\n    ++\tgit_config(command->startup_config, NULL);\n     +}\n     +\n    - static void advertise_capabilities(void)\n    + static int call_advertise(struct protocol_capability *command,\n    + \t\t\t  struct repository *r, struct strbuf *value)\n      {\n    - \tstruct strbuf capability = STRBUF_INIT;\n    +@@ serve.c: static int call_advertise(struct protocol_capability *command,\n    + \tstruct strbuf sb = STRBUF_INIT;\n    + \tconst char *msg;\n    + \n    ++\tread_startup_config(command);\n    ++\n    + \tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n    + \ttrace2_region_enter(\"serve\", sb.buf, r);\n    + \tret = command->advertise(r, value);\n    +@@ serve.c: static int call_command(struct protocol_capability *command,\n    + \tint ret;\n    + \tstruct strbuf sb = STRBUF_INIT;\n    + \n    ++\tread_startup_config(command);\n    ++\n    + \tstrbuf_addf(&sb, \"command/%s\", command->name);\n    + \ttrace2_region_enter(\"serve\", sb.buf, r);\n    + \tret = command->command(r, keys, request);\n     @@ serve.c: static int process_request(void)\n      /* Main serve loop for protocol version 2 */\n      void serve(struct serve_options *options)\n      {\n     -\tgit_config_get_bool(\"transfer.advertisesid\", &advertise_sid);\n    -+\tgit_config(git_serve_config, NULL);\n    - \n    +-\n      \tif (options->advertise_capabilities || !options->stateless_rpc) {\n      \t\t/* serve by default supports v2 */\n    + \t\tpacket_write_fmt(1, \"version 2\\n\");\n-:  ----------- > 8:  9fd6138aa62 upload-pack.c: convert to new serve.c \"startup\" config cb\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428627","messageId":"patch-1.8-fdb0c5f4df1-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 1/8] serve: mark has_capability() as static","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:18Z","receivedAt":"2021-06-28T19:19:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The has_capability() function introduced in ed10cb952d3 (serve:\nintroduce git-serve, 2018-03-15) has never been used anywhere except\nserve.c, so let's mark it as static.\n\nIt was later changed from \"extern\" in 554544276a6 (*.[ch]: remove\nextern from function declarations using spatch, 2019-04-29), but we\ncould have simply marked it as \"static\" instead.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 4 ++--\n serve.h | 4 ----\n 2 files changed, 2 insertions(+), 6 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex aa8209f147e..6748c590b74 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -156,8 +156,8 @@ static int is_command(const char *key, struct protocol_capability **command)\n \treturn 0;\n }\n \n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value)\n+static int has_capability(const struct strvec *keys, const char *capability,\n+\t\t\t  const char **value)\n {\n \tint i;\n \tfor (i = 0; i < keys->nr; i++) {\ndiff --git a/serve.h b/serve.h\nindex fc2683e24d3..56da77a87af 100644\n--- a/serve.h\n+++ b/serve.h\n@@ -1,10 +1,6 @@\n #ifndef SERVE_H\n #define SERVE_H\n \n-struct strvec;\n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value);\n-\n struct serve_options {\n \tunsigned advertise_capabilities;\n \tunsigned stateless_rpc;\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428628","messageId":"patch-3.8-766045e5f1d-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 3/8] transport: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:20Z","receivedAt":"2021-06-28T19:19:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the assignments to the various transport_vtables to use\ndesignated initializers, this makes the code easier to read and\nmaintain.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c | 12 ++++++------\n transport.c        | 30 ++++++++++++------------------\n 2 files changed, 18 insertions(+), 24 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 8d445a8f3ee..e8dbdd11530 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -1261,12 +1261,12 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n }\n \n static struct transport_vtable vtable = {\n-\tset_helper_option,\n-\tget_refs_list,\n-\tfetch_refs,\n-\tpush_refs,\n-\tconnect_helper,\n-\trelease_helper\n+\t.set_option\t= set_helper_option,\n+\t.get_refs_list\t= get_refs_list,\n+\t.fetch_refs\t= fetch_refs,\n+\t.push_refs\t= push_refs,\n+\t.connect\t= connect_helper,\n+\t.disconnect\t= release_helper\n };\n \n int transport_helper_init(struct transport *transport, const char *name)\ndiff --git a/transport.c b/transport.c\nindex f60985e2dbd..bdd9b5a93cf 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -880,12 +880,10 @@ static int disconnect_git(struct transport *transport)\n }\n \n static struct transport_vtable taken_over_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tNULL,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.disconnect\t= disconnect_git\n };\n \n void transport_take_over(struct transport *transport,\n@@ -1029,21 +1027,17 @@ void transport_check_allowed(const char *type)\n }\n \n static struct transport_vtable bundle_vtable = {\n-\tNULL,\n-\tget_refs_from_bundle,\n-\tfetch_refs_from_bundle,\n-\tNULL,\n-\tNULL,\n-\tclose_bundle\n+\t.get_refs_list\t= get_refs_from_bundle,\n+\t.fetch_refs\t= fetch_refs_from_bundle,\n+\t.disconnect\t= close_bundle\n };\n \n static struct transport_vtable builtin_smart_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tconnect_git,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.connect\t= connect_git,\n+\t.disconnect\t= disconnect_git\n };\n \n struct transport *transport_get(struct remote *remote, const char *url)\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428629","messageId":"patch-2.8-7b8101dca71-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 2/8] transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:19Z","receivedAt":"2021-06-28T19:19:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"fetch\" member of the transport_vtable to \"fetch_refs\" for\nconsistency with the existing \"push_refs\". Neither of them just push\n\"refs\" but refs and objects, but having the two match makes the code\nmore readable than having it be inconsistent, especially since\n\"fetch_refs\" is a lot easier to grep for than \"fetch\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c   | 8 ++++----\n transport-internal.h | 2 +-\n transport.c          | 2 +-\n 3 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 4be035edb8b..8d445a8f3ee 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -671,8 +671,8 @@ static int connect_helper(struct transport *transport, const char *name,\n static struct ref *get_refs_list_using_list(struct transport *transport,\n \t\t\t\t\t    int for_push);\n \n-static int fetch(struct transport *transport,\n-\t\t int nr_heads, struct ref **to_fetch)\n+static int fetch_refs(struct transport *transport,\n+\t\t      int nr_heads, struct ref **to_fetch)\n {\n \tstruct helper_data *data = transport->data;\n \tint i, count;\n@@ -681,7 +681,7 @@ static int fetch(struct transport *transport,\n \n \tif (process_connect(transport, 0)) {\n \t\tdo_take_over(transport);\n-\t\treturn transport->vtable->fetch(transport, nr_heads, to_fetch);\n+\t\treturn transport->vtable->fetch_refs(transport, nr_heads, to_fetch);\n \t}\n \n \t/*\n@@ -1263,7 +1263,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n static struct transport_vtable vtable = {\n \tset_helper_option,\n \tget_refs_list,\n-\tfetch,\n+\tfetch_refs,\n \tpush_refs,\n \tconnect_helper,\n \trelease_helper\ndiff --git a/transport-internal.h b/transport-internal.h\nindex b60f1ba9077..c4ca0b733ac 100644\n--- a/transport-internal.h\n+++ b/transport-internal.h\n@@ -34,7 +34,7 @@ struct transport_vtable {\n \t * get_refs_list(), it should set the old_sha1 fields in the\n \t * provided refs now.\n \t **/\n-\tint (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);\n+\tint (*fetch_refs)(struct transport *transport, int refs_nr, struct ref **refs);\n \n \t/**\n \t * Push the objects and refs. Send the necessary objects, and\ndiff --git a/transport.c b/transport.c\nindex 50f5830eb6b..f60985e2dbd 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1449,7 +1449,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)\n \t\t\theads[nr_heads++] = rm;\n \t}\n \n-\trc = transport->vtable->fetch(transport, nr_heads, heads);\n+\trc = transport->vtable->fetch_refs(transport, nr_heads, heads);\n \n \tfree(heads);\n \treturn rc;\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428630","messageId":"patch-5.8-d0b02aa0c7d-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 5/8] serve.c: add call_{advertise,command}() indirection","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:22Z","receivedAt":"2021-06-28T19:19:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Instead of directly calling the callbacks defined in the \"struct\nprotocol_capability\", let's call them via new\n\"call_{advertise,command}()\" functions. There's no use in this\nindirection now, but it'll be used in a subsequent commit to ensure\nthat config is read before they're called.\n\nSee ed10cb952d3 (serve: introduce git-serve, 2018-03-15) for the\nintroduction of the code being changed here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 33 ++++++++++++++++++++++++---------\n 1 file changed, 24 insertions(+), 9 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 49ea9fc8fd5..85cd3eab26e 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -42,6 +42,10 @@ static int session_id_advertise(struct repository *r, struct strbuf *value)\n \treturn 1;\n }\n \n+typedef int (*advertise_fn_t)(struct repository *r, struct strbuf *value);\n+typedef int (*command_fn_t)(struct repository *r, struct strvec *keys,\n+\t\t\t    struct packet_reader *request);\n+\n struct protocol_capability {\n \t/*\n \t * The name of the capability.  The server uses this name when\n@@ -56,7 +60,7 @@ struct protocol_capability {\n \t * If a value is added to 'value', the server will advertise this\n \t * capability as \"<name>=<value>\" instead of \"<name>\".\n \t */\n-\tint (*advertise)(struct repository *r, struct strbuf *value);\n+\tadvertise_fn_t advertise;\n \n \t/*\n \t * Function called when a client requests the capability as a command.\n@@ -67,9 +71,7 @@ struct protocol_capability {\n \t *\n \t * This field should be NULL for capabilities which are not commands.\n \t */\n-\tint (*command)(struct repository *r,\n-\t\t       struct strvec *keys,\n-\t\t       struct packet_reader *request);\n+\tcommand_fn_t command;\n };\n \n static struct protocol_capability capabilities[] = {\n@@ -106,6 +108,19 @@ static struct protocol_capability capabilities[] = {\n \t},\n };\n \n+static int call_advertise(struct protocol_capability *command,\n+\t\t\t  struct repository *r, struct strbuf *value)\n+{\n+\treturn command->advertise(r, value);\n+}\n+\n+static int call_command(struct protocol_capability *command,\n+\t\t\tstruct repository *r, struct strvec *keys,\n+\t\t\tstruct packet_reader *request)\n+{\n+\treturn command->command(r, keys, request);\n+}\n+\n static void advertise_capabilities(void)\n {\n \tstruct strbuf capability = STRBUF_INIT;\n@@ -115,7 +130,7 @@ static void advertise_capabilities(void)\n \tfor (i = 0; i < ARRAY_SIZE(capabilities); i++) {\n \t\tstruct protocol_capability *c = &capabilities[i];\n \n-\t\tif (c->advertise(the_repository, &value)) {\n+\t\tif (call_advertise(c, the_repository, &value)) {\n \t\t\tstrbuf_addstr(&capability, c->name);\n \n \t\t\tif (value.len) {\n@@ -155,9 +170,9 @@ static struct protocol_capability *get_capability(const char *key)\n \n static int is_valid_capability(const char *key)\n {\n-\tconst struct protocol_capability *c = get_capability(key);\n+\tstruct protocol_capability *c = get_capability(key);\n \n-\treturn c && c->advertise(the_repository, NULL);\n+\treturn c && call_advertise(c, the_repository, NULL);\n }\n \n static int is_command(const char *key, struct protocol_capability **command)\n@@ -170,7 +185,7 @@ static int is_command(const char *key, struct protocol_capability **command)\n \t\tif (*command)\n \t\t\tdie(\"command '%s' requested after already requesting command '%s'\",\n \t\t\t    out, (*command)->name);\n-\t\tif (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)\n+\t\tif (!cmd || !call_advertise(cmd, the_repository, NULL) || !cmd->command)\n \t\t\tdie(\"invalid command '%s'\", out);\n \n \t\t*command = cmd;\n@@ -294,7 +309,7 @@ static int process_request(void)\n \tif (has_capability(&keys, \"session-id\", &client_sid))\n \t\ttrace2_data_string(\"transfer\", NULL, \"client-sid\", client_sid);\n \n-\tcommand->command(the_repository, &keys, &reader);\n+\tcall_command(command, the_repository, &keys, &reader);\n \n \tstrvec_clear(&keys);\n \treturn 0;\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428631","messageId":"patch-4.8-bd920de1629-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 4/8] serve: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:21Z","receivedAt":"2021-06-28T19:19:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the declaration of the protocol_capability struct to use\ndesignated initializers, this makes this more verbose now, but a\nfollow-up commit will add a new field. At that point these lines would\nbe too dense to be on one line comfortably.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 38 +++++++++++++++++++++++++++++++-------\n 1 file changed, 31 insertions(+), 7 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 6748c590b74..49ea9fc8fd5 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -73,13 +73,37 @@ struct protocol_capability {\n };\n \n static struct protocol_capability capabilities[] = {\n-\t{ \"agent\", agent_advertise, NULL },\n-\t{ \"ls-refs\", ls_refs_advertise, ls_refs },\n-\t{ \"fetch\", upload_pack_advertise, upload_pack_v2 },\n-\t{ \"server-option\", always_advertise, NULL },\n-\t{ \"object-format\", object_format_advertise, NULL },\n-\t{ \"session-id\", session_id_advertise, NULL },\n-\t{ \"object-info\", always_advertise, cap_object_info },\n+\t{\n+\t\t.name = \"agent\",\n+\t\t.advertise = agent_advertise,\n+\t},\n+\t{\n+\t\t.name = \"ls-refs\",\n+\t\t.advertise = ls_refs_advertise,\n+\t\t.command = ls_refs,\n+\t},\n+\t{\n+\t\t.name = \"fetch\",\n+\t\t.advertise = upload_pack_advertise,\n+\t\t.command = upload_pack_v2,\n+\t},\n+\t{\n+\t\t.name = \"server-option\",\n+\t\t.advertise = always_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-format\",\n+\t\t.advertise = object_format_advertise,\n+\t},\n+\t{\n+\t\t.name = \"session-id\",\n+\t\t.advertise = session_id_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-info\",\n+\t\t.advertise = always_advertise,\n+\t\t.command = cap_object_info,\n+\t},\n };\n \n static void advertise_capabilities(void)\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428632","messageId":"patch-7.8-0a4fb01ae38-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:24Z","receivedAt":"2021-06-28T19:19:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the introduction of serve.c we've added git_config() callbacks\nand other config reading for capabilities in the following commits:\n\n- e20b4192a37 (upload-pack: support hidden refs with protocol v2, 2018-12-18)\n- 08450ef7918 (upload-pack: clear filter_options for each v2 fetch command, 2020-05-08)\n- 6b5b6e422ee (serve: advertise session ID in v2 capabilities, 2020-11-11)\n- 59e1205d167 (ls-refs: report unborn targets of symrefs, 2021-02-05)\n\nOf these 08450ef7918 fixed code that needed to read config on a\nper-request basis, whereas most of the config reading just wants to\ncheck if we've enabled one semi-static config variable or other. We'd\nlike to re-read that value eventually, but from request-to-request\nit's OK if we retain the old one, and it isn't impacted by other\nrequest data.\n\nSo let's support this common pattern as a \"startup_config\" callback,\nmaking use of our recently added \"call_{advertise,command}()\"\nfunctions. This allows us to simplify e.g. the \"ensure_config_read()\"\nfunction added in 59e1205d167 (ls-refs: report unborn targets of\nsymrefs, 2021-02-05).\n\nWe could read all the config for all the protocol capabilities, but\nlet's do it one callback at a time in anticipation that some won't be\ncalled at all, and that some might be more expensive than others in\nthe future.\n\nI'm not migrating over the code in the upload_pack_v2 function in\nupload-pack.c yet, that case is more complex since it deals with both\nv1 and v2. It will be dealt with in a code a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n ls-refs.c | 42 ++++++++++++++----------------------------\n ls-refs.h |  1 +\n serve.c   | 42 ++++++++++++++++++++++++++++++++++++++++--\n 3 files changed, 55 insertions(+), 30 deletions(-)\n\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 88f6c3f60d8..02fbcfd9bde 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -7,37 +7,26 @@\n #include \"pkt-line.h\"\n #include \"config.h\"\n \n-static int config_read;\n-static int advertise_unborn;\n-static int allow_unborn;\n+/* \"unborn\" is on by default if there's no lsrefs.unborn config */\n+static int advertise_unborn = 1;\n+static int allow_unborn = 1;\n \n-static void ensure_config_read(void)\n+int ls_refs_startup_config(const char *var, const char *value, void *data)\n {\n-\tconst char *str = NULL;\n-\n-\tif (config_read)\n-\t\treturn;\n-\n-\tif (repo_config_get_string_tmp(the_repository, \"lsrefs.unborn\", &str)) {\n-\t\t/*\n-\t\t * If there is no such config, advertise and allow it by\n-\t\t * default.\n-\t\t */\n-\t\tadvertise_unborn = 1;\n-\t\tallow_unborn = 1;\n-\t} else {\n-\t\tif (!strcmp(str, \"advertise\")) {\n-\t\t\tadvertise_unborn = 1;\n+\tif (!strcmp(var, \"lsrefs.unborn\")) {\n+\t\tif (!strcmp(value, \"advertise\")) {\n+\t\t\t/* Allowed and advertised by default */\n+\t\t} else if (!strcmp(value, \"allow\")) {\n+\t\t\tadvertise_unborn = 0;\n \t\t\tallow_unborn = 1;\n-\t\t} else if (!strcmp(str, \"allow\")) {\n-\t\t\tallow_unborn = 1;\n-\t\t} else if (!strcmp(str, \"ignore\")) {\n-\t\t\t/* do nothing */\n+\t\t} else if (!strcmp(value, \"ignore\")) {\n+\t\t\tadvertise_unborn = 0;\n+\t\t\tallow_unborn = 0;\n \t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n+\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), value);\n \t\t}\n \t}\n-\tconfig_read = 1;\n+\treturn 0;\n }\n \n /*\n@@ -145,8 +134,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n \n \tmemset(&data, 0, sizeof(data));\n \tstrvec_init(&data.prefixes);\n-\n-\tensure_config_read();\n \tgit_config(ls_refs_config, NULL);\n \n \twhile (packet_reader_read(request) == PACKET_READ_NORMAL) {\n@@ -179,7 +166,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n int ls_refs_advertise(struct repository *r, struct strbuf *value)\n {\n \tif (value) {\n-\t\tensure_config_read();\n \t\tif (advertise_unborn)\n \t\t\tstrbuf_addstr(value, \"unborn\");\n \t}\ndiff --git a/ls-refs.h b/ls-refs.h\nindex a99e4be0bdd..5fcab0d1dca 100644\n--- a/ls-refs.h\n+++ b/ls-refs.h\n@@ -6,6 +6,7 @@ struct strvec;\n struct packet_reader;\n int ls_refs(struct repository *r, struct strvec *keys,\n \t    struct packet_reader *request);\n+int ls_refs_startup_config(const char *var, const char *value, void *data);\n int ls_refs_advertise(struct repository *r, struct strbuf *value);\n \n #endif /* LS_REFS_H */\ndiff --git a/serve.c b/serve.c\nindex 6dbd05248b9..cce96dd5a81 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -33,6 +33,13 @@ static int object_format_advertise(struct repository *r,\n \treturn 1;\n }\n \n+static int session_id_startup_config(const char *var, const char *value, void *data)\n+{\n+\tif (!strcmp(var, \"transfer.advertisesid\"))\n+\t\tadvertise_sid = git_config_bool(var, value);\n+\treturn 0;\n+}\n+\n static int session_id_advertise(struct repository *r, struct strbuf *value)\n {\n \tif (!advertise_sid)\n@@ -54,6 +61,24 @@ struct protocol_capability {\n \t */\n \tconst char *name;\n \n+\t/*\n+\t * A git_config() callback that'll be called only once for the\n+\t * lifetime of the process, possibly over many different\n+\t * requests. Used for reading config that's expected to be\n+\t * static.\n+\t *\n+\t * The \"command\" or \"advertise\" callbacks themselves are\n+\t * expected to read config that needs to be more current than\n+\t * that, or which is dependent on request data.\n+\t */\n+\tint (*startup_config)(const char *var, const char *value, void *data);\n+\n+\t/*\n+\t * A boolean to check if we've called our \"startup_config\"\n+\t * callback.\n+\t */\n+\tint have_startup_config;\n+\n \t/*\n \t * Function queried to see if a capability should be advertised.\n \t * Optionally a value can be specified by adding it to 'value'.\n@@ -81,6 +106,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"ls-refs\",\n+\t\t.startup_config = ls_refs_startup_config,\n \t\t.advertise = ls_refs_advertise,\n \t\t.command = ls_refs,\n \t},\n@@ -99,6 +125,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"session-id\",\n+\t\t.startup_config = session_id_startup_config,\n \t\t.advertise = session_id_advertise,\n \t},\n \t{\n@@ -108,6 +135,15 @@ static struct protocol_capability capabilities[] = {\n \t},\n };\n \n+static void read_startup_config(struct protocol_capability *command)\n+{\n+\tif (!command->startup_config)\n+\t\treturn;\n+\tif (command->have_startup_config++)\n+\t\treturn;\n+\tgit_config(command->startup_config, NULL);\n+}\n+\n static int call_advertise(struct protocol_capability *command,\n \t\t\t  struct repository *r, struct strbuf *value)\n {\n@@ -115,6 +151,8 @@ static int call_advertise(struct protocol_capability *command,\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *msg;\n \n+\tread_startup_config(command);\n+\n \tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n \ttrace2_region_enter(\"serve\", sb.buf, r);\n \tret = command->advertise(r, value);\n@@ -132,6 +170,8 @@ static int call_command(struct protocol_capability *command,\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n \n+\tread_startup_config(command);\n+\n \tstrbuf_addf(&sb, \"command/%s\", command->name);\n \ttrace2_region_enter(\"serve\", sb.buf, r);\n \tret = command->command(r, keys, request);\n@@ -338,8 +378,6 @@ static int process_request(void)\n /* Main serve loop for protocol version 2 */\n void serve(struct serve_options *options)\n {\n-\tgit_config_get_bool(\"transfer.advertisesid\", &advertise_sid);\n-\n \tif (options->advertise_capabilities || !options->stateless_rpc) {\n \t\t/* serve by default supports v2 */\n \t\tpacket_write_fmt(1, \"version 2\\n\");\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428633","messageId":"patch-6.8-baeee6539ad-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 6/8] serve.c: add trace2 regions for advertise & command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:23Z","receivedAt":"2021-06-28T19:19:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that we've factored out \"call_{advertise,command}()\" in a\npreceding commit it becomes easy to trace all these callbacks with\ntrace2. Let's do that. As the tests demonstrate there's no v2 push\nprotocol, which the tests assert.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c                               | 24 ++++++++++++++++++++++--\n t/t5705-session-id-in-capabilities.sh | 16 ++++++++++++++--\n 2 files changed, 36 insertions(+), 4 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 85cd3eab26e..6dbd05248b9 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -111,14 +111,34 @@ static struct protocol_capability capabilities[] = {\n static int call_advertise(struct protocol_capability *command,\n \t\t\t  struct repository *r, struct strbuf *value)\n {\n-\treturn command->advertise(r, value);\n+\tint ret;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *msg;\n+\n+\tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n+\ttrace2_region_enter(\"serve\", sb.buf, r);\n+\tret = command->advertise(r, value);\n+\tmsg = ret ? \"advertised\" : \"hidden\";\n+\ttrace2_region_leave_printf(\"serve\", sb.buf, r, \"%s\", msg);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n }\n \n static int call_command(struct protocol_capability *command,\n \t\t\tstruct repository *r, struct strvec *keys,\n \t\t\tstruct packet_reader *request)\n {\n-\treturn command->command(r, keys, request);\n+\tint ret;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tstrbuf_addf(&sb, \"command/%s\", command->name);\n+\ttrace2_region_enter(\"serve\", sb.buf, r);\n+\tret = command->command(r, keys, request);\n+\ttrace2_region_leave(\"serve\", sb.buf, r);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n }\n \n static void advertise_capabilities(void)\ndiff --git a/t/t5705-session-id-in-capabilities.sh b/t/t5705-session-id-in-capabilities.sh\nindex f1d189d5bcc..cda78fa7a1d 100755\n--- a/t/t5705-session-id-in-capabilities.sh\n+++ b/t/t5705-session-id-in-capabilities.sh\n@@ -57,7 +57,13 @@ do\n \t\t\t--upload-pack \"GIT_TRACE2_EVENT=\\\"$(pwd)/tr2-server-events\\\" git-upload-pack\" \\\n \t\t\torigin &&\n \t\tgrep \\\"key\\\":\\\"server-sid\\\" tr2-client-events &&\n-\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events\n+\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events &&\n+\n+\t\tif test \"$PROTO\" = 2\n+\t\tthen\n+\t\t\tgrep \\\"event\\\":\\\"region_enter\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events &&\n+\t\t\tgrep \\\"event\\\":\\\"region_leave\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events\n+\t\tfi\n \t'\n \n \ttest_expect_success \"session IDs advertised (push v${PROTO})\" '\n@@ -71,7 +77,13 @@ do\n \t\t\t--receive-pack \"GIT_TRACE2_EVENT=\\\"$(pwd)/tr2-server-events\\\" git-receive-pack\" \\\n \t\t\torigin HEAD:new-branch &&\n \t\tgrep \\\"key\\\":\\\"server-sid\\\" tr2-client-events &&\n-\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events\n+\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events &&\n+\n+\t\tif test \"$PROTO\" = 2\n+\t\tthen\n+\t\t\t! grep \\\"event\\\":\\\"region_enter\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events &&\n+\t\t\t! grep \\\"event\\\":\\\"region_leave\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events\n+\t\tfi\n \t'\n done\n \n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428634","messageId":"patch-8.8-9fd6138aa62-20210628T191634Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v2 8/8] upload-pack.c: convert to new serve.c \"startup\" config cb","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-06-28T19:19:25Z","receivedAt":"2021-06-28T19:19:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Convert the config reading code in upload-pack.c to use the new\n\"startup_config\" callback when we're using the v2 protocol, and\nlightly fake up the same when we're using v0 and v1.\n\nBefore the series that fixed 08450ef7918 (upload-pack: clear\nfilter_options for each v2 fetch command, 2020-05-08) landed, most of\nwhat's now \"struct upload_pack_data\" used to be static variables at\nthe top of this file.\n\nThis moves some of them back. See f203a88cf14 (upload-pack: move\nkeepalive to upload_pack_data, 2020-06-04), f1514c6aad0 (upload-pack:\nmove allow_unadvertised_object_request to upload_pack_data,\n2020-06-11) etc.\n\nI think this makes it easier to understand the this code, as we're now\nclearly separating data and config that changes on a\nrequest-to-request basis (see 08450ef7918), and the sort of config\nthat serve.c's \"startup_config\" is aimed at.\n\nThus it's clear that the \"allow_uor\" passed to functions like\n\"is_our_ref()\" is constant after startup (usually it'll never change\nfor a given server's configuration, or change once).\n\nThis requires a very light compatibly layer with the serve.c callback\nmechanism in the form of \"upload_pack\" for the v0 and v1 protocols.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c       |   1 +\n upload-pack.c | 139 ++++++++++++++++++++++++++++++--------------------\n upload-pack.h |   2 +\n 3 files changed, 86 insertions(+), 56 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex cce96dd5a81..1054d1459c9 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -112,6 +112,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"fetch\",\n+\t\t.startup_config = serve_upload_pack_startup_config,\n \t\t.advertise = upload_pack_advertise,\n \t\t.command = upload_pack_v2,\n \t},\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb43..6c0d566e9e0 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -52,6 +52,19 @@ enum allow_uor {\n \tALLOW_ANY_SHA1 = 0x07\n };\n \n+/*\n+ * \"Global\" configuration that won't be affected by the type of\n+ * request we're doing, or by other request data in \"struct\n+ * upload_pack_data\" below.\n+ */\n+static int v1_have_startup_config;\n+static int config_keepalive = 5;\n+static enum allow_uor config_allow_uor;\n+static unsigned config_advertise_sid;\n+static const char *config_pack_objects_hook;\n+static unsigned config_v2_allow_ref_in_want;\n+static unsigned config_v2_allow_sideband_all;\n+\n /*\n  * Please annotate, and if possible group together, fields used only\n  * for protocol v0 or only for protocol v2.\n@@ -70,7 +83,6 @@ struct upload_pack_data {\n \ttimestamp_t deepen_since;\n \tint deepen_rev_list;\n \tint deepen_relative;\n-\tint keepalive;\n \tint shallow_nr;\n \ttimestamp_t oldest_have;\n \n@@ -85,15 +97,12 @@ struct upload_pack_data {\n \tint use_sideband;\n \n \tstruct string_list uri_protocols;\n-\tenum allow_uor allow_uor;\n \n \tstruct list_objects_filter_options filter_options;\n \tstruct string_list allowed_filters;\n \n \tstruct packet_writer writer;\n \n-\tconst char *pack_objects_hook;\n-\n \tunsigned stateless_rpc : 1;\t\t\t\t/* v0 only */\n \tunsigned no_done : 1;\t\t\t\t\t/* v0 only */\n \tunsigned daemon_mode : 1;\t\t\t\t/* v0 only */\n@@ -109,9 +118,6 @@ struct upload_pack_data {\n \tunsigned long tree_filter_max_depth;\n \n \tunsigned done : 1;\t\t\t\t\t/* v2 only */\n-\tunsigned allow_ref_in_want : 1;\t\t\t\t/* v2 only */\n-\tunsigned allow_sideband_all : 1;\t\t\t/* v2 only */\n-\tunsigned advertise_sid : 1;\n };\n \n static void upload_pack_data_init(struct upload_pack_data *data)\n@@ -141,9 +147,6 @@ static void upload_pack_data_init(struct upload_pack_data *data)\n \tdata->allow_filter_fallback = 1;\n \tdata->tree_filter_max_depth = ULONG_MAX;\n \tpacket_writer_init(&data->writer, 1);\n-\n-\tdata->keepalive = 5;\n-\tdata->advertise_sid = 0;\n }\n \n static void upload_pack_data_clear(struct upload_pack_data *data)\n@@ -158,8 +161,6 @@ static void upload_pack_data_clear(struct upload_pack_data *data)\n \tobject_array_clear(&data->extra_edge_obj);\n \tlist_objects_filter_release(&data->filter_options);\n \tstring_list_clear(&data->allowed_filters, 0);\n-\n-\tfree((char *)data->pack_objects_hook);\n }\n \n static void reset_timeout(unsigned int timeout)\n@@ -277,10 +278,10 @@ static void create_pack_file(struct upload_pack_data *pack_data,\n \tint i;\n \tFILE *pipe_fd;\n \n-\tif (!pack_data->pack_objects_hook)\n+\tif (!config_pack_objects_hook)\n \t\tpack_objects.git_cmd = 1;\n \telse {\n-\t\tstrvec_push(&pack_objects.args, pack_data->pack_objects_hook);\n+\t\tstrvec_push(&pack_objects.args, config_pack_objects_hook);\n \t\tstrvec_push(&pack_objects.args, \"git\");\n \t\tpack_objects.use_shell = 1;\n \t}\n@@ -371,9 +372,9 @@ static void create_pack_file(struct upload_pack_data *pack_data,\n \t\tif (!pollsize)\n \t\t\tbreak;\n \n-\t\tpolltimeout = pack_data->keepalive < 0\n+\t\tpolltimeout = config_keepalive < 0\n \t\t\t? -1\n-\t\t\t: 1000 * pack_data->keepalive;\n+\t\t\t: 1000 * config_keepalive;\n \n \t\tret = poll(pfd, pollsize, polltimeout);\n \n@@ -581,9 +582,9 @@ static int get_common_commits(struct upload_pack_data *data,\n \t}\n }\n \n-static int is_our_ref(struct object *o, enum allow_uor allow_uor)\n+static int is_our_ref(struct object *o)\n {\n-\tint allow_hidden_ref = (allow_uor &\n+\tint allow_hidden_ref = (config_allow_uor &\n \t\t\t\t(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n@@ -593,8 +594,7 @@ static int is_our_ref(struct object *o, enum allow_uor allow_uor)\n  */\n static int do_reachable_revlist(struct child_process *cmd,\n \t\t\t\tstruct object_array *src,\n-\t\t\t\tstruct object_array *reachable,\n-\t\t\t\tenum allow_uor allow_uor)\n+\t\t\t\tstruct object_array *reachable)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n@@ -627,14 +627,14 @@ static int do_reachable_revlist(struct child_process *cmd,\n \t\t\tcontinue;\n \t\tif (reachable && o->type == OBJ_COMMIT)\n \t\t\to->flags &= ~TMP_MARK;\n-\t\tif (!is_our_ref(o, allow_uor))\n+\t\tif (!is_our_ref(o))\n \t\t\tcontinue;\n \t\tif (fprintf(cmd_in, \"^%s\\n\", oid_to_hex(&o->oid)) < 0)\n \t\t\tgoto error;\n \t}\n \tfor (i = 0; i < src->nr; i++) {\n \t\to = src->objects[i].item;\n-\t\tif (is_our_ref(o, allow_uor)) {\n+\t\tif (is_our_ref(o)) {\n \t\t\tif (reachable)\n \t\t\t\tadd_object_array(o, NULL, reachable);\n \t\t\tcontinue;\n@@ -671,8 +671,7 @@ static int get_reachable_list(struct upload_pack_data *data,\n \tchar namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */\n \tconst unsigned hexsz = the_hash_algo->hexsz;\n \n-\tif (do_reachable_revlist(&cmd, &data->shallows, reachable,\n-\t\t\t\t data->allow_uor) < 0)\n+\tif (do_reachable_revlist(&cmd, &data->shallows, reachable) < 0)\n \t\treturn -1;\n \n \twhile ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) {\n@@ -703,13 +702,13 @@ static int get_reachable_list(struct upload_pack_data *data,\n \treturn 0;\n }\n \n-static int has_unreachable(struct object_array *src, enum allow_uor allow_uor)\n+static int has_unreachable(struct object_array *src)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tchar buf[1];\n \tint i;\n \n-\tif (do_reachable_revlist(&cmd, src, NULL, allow_uor) < 0)\n+\tif (do_reachable_revlist(&cmd, src, NULL) < 0)\n \t\treturn 1;\n \n \t/*\n@@ -748,9 +747,9 @@ static void check_non_tip(struct upload_pack_data *data)\n \t * uploadpack.allowReachableSHA1InWant,\n \t * non-tip requests can never happen.\n \t */\n-\tif (!data->stateless_rpc && !(data->allow_uor & ALLOW_REACHABLE_SHA1))\n+\tif (!data->stateless_rpc && !(config_allow_uor & ALLOW_REACHABLE_SHA1))\n \t\tgoto error;\n-\tif (!has_unreachable(&data->want_obj, data->allow_uor))\n+\tif (!has_unreachable(&data->want_obj))\n \t\t/* All the non-tip ones are ancestors of what we advertised */\n \t\treturn;\n \n@@ -758,7 +757,7 @@ static void check_non_tip(struct upload_pack_data *data)\n \t/* Pick one of them (we know there at least is one) */\n \tfor (i = 0; i < data->want_obj.nr; i++) {\n \t\tstruct object *o = data->want_obj.objects[i].item;\n-\t\tif (!is_our_ref(o, data->allow_uor)) {\n+\t\tif (!is_our_ref(o)) {\n \t\t\tpacket_writer_error(&data->writer,\n \t\t\t\t\t    \"upload-pack: not our ref %s\",\n \t\t\t\t\t    oid_to_hex(&o->oid));\n@@ -1123,8 +1122,8 @@ static void receive_needs(struct upload_pack_data *data,\n \t\t}\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n-\t\t\tif (!((data->allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1\n-\t\t\t      || is_our_ref(o, data->allow_uor)))\n+\t\t\tif (!((config_allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1\n+\t\t\t      || is_our_ref(o)))\n \t\t\t\thas_non_tip = 1;\n \t\t\tadd_object_array(o, NULL, &data->want_obj);\n \t\t}\n@@ -1184,7 +1183,7 @@ static void format_symref_info(struct strbuf *buf, struct string_list *symref)\n }\n \n static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {\n-\tif (d->advertise_sid)\n+\tif (config_advertise_sid)\n \t\tstrbuf_addf(buf, \" session-id=%s\", trace2_session_id());\n }\n \n@@ -1210,9 +1209,9 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\tpacket_write_fmt(1, \"%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\\n\",\n \t\t\t     oid_to_hex(oid), refname_nons,\n \t\t\t     0, capabilities,\n-\t\t\t     (data->allow_uor & ALLOW_TIP_SHA1) ?\n+\t\t\t     (config_allow_uor & ALLOW_TIP_SHA1) ?\n \t\t\t\t     \" allow-tip-sha1-in-want\" : \"\",\n-\t\t\t     (data->allow_uor & ALLOW_REACHABLE_SHA1) ?\n+\t\t\t     (config_allow_uor & ALLOW_REACHABLE_SHA1) ?\n \t\t\t\t     \" allow-reachable-sha1-in-want\" : \"\",\n \t\t\t     data->stateless_rpc ? \" no-done\" : \"\",\n \t\t\t     symref_info.buf,\n@@ -1282,45 +1281,71 @@ static int parse_object_filter_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int upload_pack_config(const char *var, const char *value, void *cb_data)\n+static int upload_pack_startup_config(const char *var, const char *value,\n+\t\t\t\t      void *cb_data)\n {\n-\tstruct upload_pack_data *data = cb_data;\n-\n \tif (!strcmp(\"uploadpack.allowtipsha1inwant\", var)) {\n \t\tif (git_config_bool(var, value))\n-\t\t\tdata->allow_uor |= ALLOW_TIP_SHA1;\n+\t\t\tconfig_allow_uor |= ALLOW_TIP_SHA1;\n \t\telse\n-\t\t\tdata->allow_uor &= ~ALLOW_TIP_SHA1;\n+\t\t\tconfig_allow_uor &= ~ALLOW_TIP_SHA1;\n \t} else if (!strcmp(\"uploadpack.allowreachablesha1inwant\", var)) {\n \t\tif (git_config_bool(var, value))\n-\t\t\tdata->allow_uor |= ALLOW_REACHABLE_SHA1;\n+\t\t\tconfig_allow_uor |= ALLOW_REACHABLE_SHA1;\n \t\telse\n-\t\t\tdata->allow_uor &= ~ALLOW_REACHABLE_SHA1;\n+\t\t\tconfig_allow_uor &= ~ALLOW_REACHABLE_SHA1;\n \t} else if (!strcmp(\"uploadpack.allowanysha1inwant\", var)) {\n \t\tif (git_config_bool(var, value))\n-\t\t\tdata->allow_uor |= ALLOW_ANY_SHA1;\n+\t\t\tconfig_allow_uor |= ALLOW_ANY_SHA1;\n \t\telse\n-\t\t\tdata->allow_uor &= ~ALLOW_ANY_SHA1;\n+\t\t\tconfig_allow_uor &= ~ALLOW_ANY_SHA1;\n \t} else if (!strcmp(\"uploadpack.keepalive\", var)) {\n-\t\tdata->keepalive = git_config_int(var, value);\n-\t\tif (!data->keepalive)\n-\t\t\tdata->keepalive = -1;\n-\t} else if (!strcmp(\"uploadpack.allowfilter\", var)) {\n-\t\tdata->allow_filter = git_config_bool(var, value);\n-\t} else if (!strcmp(\"uploadpack.allowrefinwant\", var)) {\n-\t\tdata->allow_ref_in_want = git_config_bool(var, value);\n-\t} else if (!strcmp(\"uploadpack.allowsidebandall\", var)) {\n-\t\tdata->allow_sideband_all = git_config_bool(var, value);\n+\t\tconfig_keepalive = git_config_int(var, value);\n+\t\tif (!config_keepalive)\n+\t\t\tconfig_keepalive = -1;\n \t} else if (!strcmp(\"core.precomposeunicode\", var)) {\n \t\tprecomposed_unicode = git_config_bool(var, value);\n \t} else if (!strcmp(\"transfer.advertisesid\", var)) {\n-\t\tdata->advertise_sid = git_config_bool(var, value);\n+\t\tconfig_advertise_sid = git_config_bool(var, value);\n \t}\n \n \tif (current_config_scope() != CONFIG_SCOPE_LOCAL &&\n \t    current_config_scope() != CONFIG_SCOPE_WORKTREE) {\n \t\tif (!strcmp(\"uploadpack.packobjectshook\", var))\n-\t\t\treturn git_config_string(&data->pack_objects_hook, var, value);\n+\t\t\treturn git_config_string(&config_pack_objects_hook, var, value);\n+\t}\n+\treturn 0;\n+}\n+\n+static int upload_pack_startup_config_v2_only(const char *var,\n+\t\t\t\t\t      const char *value, void *cb_data)\n+{\n+\tif (!strcmp(\"uploadpack.allowrefinwant\", var))\n+\t\tconfig_v2_allow_ref_in_want = git_config_bool(var, value);\n+\telse if (!strcmp(\"uploadpack.allowsidebandall\", var))\n+\t\tconfig_v2_allow_sideband_all = git_config_bool(var, value);\n+\treturn 0;\n+}\n+\n+int serve_upload_pack_startup_config(const char *var, const char *value, void *data)\n+{\n+\tint ret;\n+\tret = upload_pack_startup_config(var, value, data);\n+\tif (ret)\n+\t\treturn ret;\n+\tret = upload_pack_startup_config_v2_only(var, value, data);\n+\tif (ret)\n+\t\treturn ret;\n+\treturn 0;\n+}\n+\n+static int upload_pack_config(const char *var, const char *value,\n+\t\t\t      void *cb_data)\n+{\n+\tstruct upload_pack_data *data = cb_data;\n+\n+\tif (!strcmp(\"uploadpack.allowfilter\", var)) {\n+\t\tdata->allow_filter = git_config_bool(var, value);\n \t}\n \n \tif (parse_object_filter_config(var, value, data) < 0)\n@@ -1336,6 +1361,8 @@ void upload_pack(struct upload_pack_options *options)\n \n \tupload_pack_data_init(&data);\n \n+\tif (!v1_have_startup_config++)\n+\t\tgit_config(upload_pack_startup_config, NULL);\n \tgit_config(upload_pack_config, &data);\n \n \tdata.stateless_rpc = options->stateless_rpc;\n@@ -1468,7 +1495,7 @@ static void process_args(struct packet_reader *request,\n \t\t/* process want */\n \t\tif (parse_want(&data->writer, arg, &data->want_obj))\n \t\t\tcontinue;\n-\t\tif (data->allow_ref_in_want &&\n+\t\tif (config_v2_allow_ref_in_want &&\n \t\t    parse_want_ref(&data->writer, arg, &data->wanted_refs,\n \t\t\t\t   &data->want_obj))\n \t\t\tcontinue;\n@@ -1526,7 +1553,7 @@ static void process_args(struct packet_reader *request,\n \t\t}\n \n \t\tif ((git_env_bool(\"GIT_TEST_SIDEBAND_ALL\", 0) ||\n-\t\t     data->allow_sideband_all) &&\n+\t\t     config_v2_allow_sideband_all) &&\n \t\t    !strcmp(arg, \"sideband-all\")) {\n \t\t\tdata->writer.use_sideband = 1;\n \t\t\tcontinue;\ndiff --git a/upload-pack.h b/upload-pack.h\nindex 27ddcdc6cb0..bd8b1ec5b3e 100644\n--- a/upload-pack.h\n+++ b/upload-pack.h\n@@ -19,5 +19,7 @@ int upload_pack_v2(struct repository *r, struct strvec *keys,\n struct strbuf;\n int upload_pack_advertise(struct repository *r,\n \t\t\t  struct strbuf *value);\n+int serve_upload_pack_startup_config(const char *var, const char *value,\n+\t\t\t\t     void *data);\n \n #endif /* UPLOAD_PACK_H */\n-- \n2.32.0.611.gd4a17395dfa\n\n"},{"id":"428969","messageId":"YN3tjEE8Ir7Zuvkq@coredump.intra.peff.net","threadId":"55944","inReplyTo":"patch-6.8-baeee6539ad-20210628T191634Z-avarab@gmail.com","subject":"Re: [PATCH v2 6/8] serve.c: add trace2 regions for advertise & command","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-07-01T16:30:04Z","receivedAt":"2021-07-01T16:30:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 28, 2021 at 09:19:23PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> Now that we've factored out \"call_{advertise,command}()\" in a\n> preceding commit it becomes easy to trace all these callbacks with\n> trace2. Let's do that. As the tests demonstrate there's no v2 push\n> protocol, which the tests assert.\n\nSeems reasonable. I haven't ever wanted these myself, but it seems like\na natural spot to mention when debugging server-side actions (especially\nbecause we may get multiple rounds of \"fetch\" for a single upload-pack\ninvocation).\n\n> diff --git a/serve.c b/serve.c\n> index 85cd3eab26e..6dbd05248b9 100644\n> --- a/serve.c\n> +++ b/serve.c\n> @@ -111,14 +111,34 @@ static struct protocol_capability capabilities[] = {\n>  static int call_advertise(struct protocol_capability *command,\n>  \t\t\t  struct repository *r, struct strbuf *value)\n>  {\n> -\treturn command->advertise(r, value);\n> +\tint ret;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tconst char *msg;\n> +\n> +\tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n> +\ttrace2_region_enter(\"serve\", sb.buf, r);\n> +\tret = command->advertise(r, value);\n> +\tmsg = ret ? \"advertised\" : \"hidden\";\n> +\ttrace2_region_leave_printf(\"serve\", sb.buf, r, \"%s\", msg);\n> +\tstrbuf_release(&sb);\n\nWe'll do these allocations even if trace2 isn't enabled. I guess that's\nprobably not that big a deal in practice. I think:\n\n  if (trace2_is_enabled())\n\tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n\nwould work (everything else is cheap and handles the unallocated state\nfine), but it might not be worth the readability hit (and it's probably\npremature optimization anyway).\n\n-Peff\n"},{"id":"428970","messageId":"YN3wvy6fhD4V+FA3@coredump.intra.peff.net","threadId":"55944","inReplyTo":"patch-7.8-0a4fb01ae38-20210628T191634Z-avarab@gmail.com","subject":"Re: [PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-07-01T16:43:43Z","receivedAt":"2021-07-01T16:43:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 28, 2021 at 09:19:24PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> So let's support this common pattern as a \"startup_config\" callback,\n> making use of our recently added \"call_{advertise,command}()\"\n> functions. This allows us to simplify e.g. the \"ensure_config_read()\"\n> function added in 59e1205d167 (ls-refs: report unborn targets of\n> symrefs, 2021-02-05).\n> \n> We could read all the config for all the protocol capabilities, but\n> let's do it one callback at a time in anticipation that some won't be\n> called at all, and that some might be more expensive than others in\n> the future.\n\nSadly I don't think this addresses my \"v2 receive-pack\" concern. The\nls_refs_startup_config() function will get called after we've received a\nrequest for \"ls-refs\", which is good. But:\n\n> +static void read_startup_config(struct protocol_capability *command)\n> +{\n> +\tif (!command->startup_config)\n> +\t\treturn;\n> +\tif (command->have_startup_config++)\n> +\t\treturn;\n> +\tgit_config(command->startup_config, NULL);\n> +}\n\n...we don't pass any context to the config callback here. I thought\npassing \"command\" might work, but looking at the ls_refs() function, it\nis the one who actually reads the pkt-lines that will tell us \"hey, I'm\ndoing an ls-refs for a push\".\n\nSo none of the serve() infrastructure can help us there; we need to read\npkt-lines and _then_ read config.\n\nI dunno. Maybe the solution is for ls_refs() to just do a separate\nconfig call to pick up the operation-specific bits, like:\n\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 88f6c3f60d..6ee70126aa 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -130,12 +130,13 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \n static int ls_refs_config(const char *var, const char *value, void *data)\n {\n+\tstruct ls_refs_data *d = data;\n \t/*\n \t * We only serve fetches over v2 for now, so respect only \"uploadpack\"\n \t * config. This may need to eventually be expanded to \"receive\", but we\n \t * don't yet know how that information will be passed to ls-refs.\n \t */\n-\treturn parse_hide_refs_config(var, value, \"uploadpack\");\n+\treturn parse_hide_refs_config(var, value, d->for_push ? \"receive\" : \"uploadpack\");\n }\n \n int ls_refs(struct repository *r, struct strvec *keys,\n@@ -147,7 +148,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n \tstrvec_init(&data.prefixes);\n \n \tensure_config_read();\n-\tgit_config(ls_refs_config, NULL);\n \n \twhile (packet_reader_read(request) == PACKET_READ_NORMAL) {\n \t\tconst char *arg = request->line;\n@@ -161,8 +161,12 @@ int ls_refs(struct repository *r, struct strvec *keys,\n \t\t\tstrvec_push(&data.prefixes, out);\n \t\telse if (!strcmp(\"unborn\", arg))\n \t\t\tdata.unborn = allow_unborn;\n+\t\telse if (!strcmp(\"for-push\", arg)) /* imagine this exists */\n+\t\t\tdata.for_push = 1;\n \t}\n \n+\tgit_config(ls_refs_config, &data);\n+\n \tif (request->status != PACKET_READ_FLUSH)\n \t\tdie(_(\"expected flush after ls-refs arguments\"));\n \n\nAnd then it is a separate thing entirely from your \"serve will read\nconfig\" code. It's arguably more confusing, because now config is read\nin two places, but that is already true because of this\nensure_config_read() thing.\n\nThe patch above obviously makes no sense until we're working on v2\nreceive-pack. But I think it illustrates that your patch here is not\ngetting in the way (though technically I think that would also be true\nof your v1, I do like this version better).\n\n-Peff\n"},{"id":"428971","messageId":"YN3xnjSN49HUzBSW@coredump.intra.peff.net","threadId":"55944","inReplyTo":"YN3wvy6fhD4V+FA3@coredump.intra.peff.net","subject":"Re: [PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-07-01T16:47:26Z","receivedAt":"2021-07-01T16:47:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 01, 2021 at 12:43:43PM -0400, Jeff King wrote:\n\n> I dunno. Maybe the solution is for ls_refs() to just do a separate\n> config call to pick up the operation-specific bits, like:\n\nBy the way, I think both currently and after the patch I showed,\nls_refs() has the same \"bug\" that we fixed for upload_pack_v2() a while\nago: in a v2 world, a client could request \"ls-refs\" over and over, and\neach time we'd load the hiderefs config, appending duplicate config to\nthe list each time.\n\nIn practice this doesn't happen because unlike \"fetch\", which clients\nmust do many rounds of, clients usually issue only a single ls-refs. So\nit may not be worth worrying too much about. I guess a malicious client\ncould convince us to very slowly allocate an arbitrary amount of memory.\n\n-Peff\n"},{"id":"429038","messageId":"87r1ggan9y.fsf@evledraar.gmail.com","threadId":"55944","inReplyTo":"YN3tjEE8Ir7Zuvkq@coredump.intra.peff.net","subject":"Re: [PATCH v2 6/8] serve.c: add trace2 regions for advertise & command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-02T12:54:56Z","receivedAt":"2021-07-02T12:55:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jul 01 2021, Jeff King wrote:\n\n> On Mon, Jun 28, 2021 at 09:19:23PM +0200, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Now that we've factored out \"call_{advertise,command}()\" in a\n>> preceding commit it becomes easy to trace all these callbacks with\n>> trace2. Let's do that. As the tests demonstrate there's no v2 push\n>> protocol, which the tests assert.\n>\n> Seems reasonable. I haven't ever wanted these myself, but it seems like\n> a natural spot to mention when debugging server-side actions (especially\n> because we may get multiple rounds of \"fetch\" for a single upload-pack\n> invocation).\n>\n>> diff --git a/serve.c b/serve.c\n>> index 85cd3eab26e..6dbd05248b9 100644\n>> --- a/serve.c\n>> +++ b/serve.c\n>> @@ -111,14 +111,34 @@ static struct protocol_capability capabilities[] = {\n>>  static int call_advertise(struct protocol_capability *command,\n>>  \t\t\t  struct repository *r, struct strbuf *value)\n>>  {\n>> -\treturn command->advertise(r, value);\n>> +\tint ret;\n>> +\tstruct strbuf sb = STRBUF_INIT;\n>> +\tconst char *msg;\n>> +\n>> +\tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n>> +\ttrace2_region_enter(\"serve\", sb.buf, r);\n>> +\tret = command->advertise(r, value);\n>> +\tmsg = ret ? \"advertised\" : \"hidden\";\n>> +\ttrace2_region_leave_printf(\"serve\", sb.buf, r, \"%s\", msg);\n>> +\tstrbuf_release(&sb);\n>\n> We'll do these allocations even if trace2 isn't enabled. I guess that's\n> probably not that big a deal in practice. I think:\n>\n>   if (trace2_is_enabled())\n> \tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n>\n> would work (everything else is cheap and handles the unallocated state\n> fine), but it might not be worth the readability hit (and it's probably\n> premature optimization anyway).\n\nWill change it to be conditional, pending further discussion...\n"},{"id":"429039","messageId":"87o8bkamxb.fsf@evledraar.gmail.com","threadId":"55944","inReplyTo":"YN3wvy6fhD4V+FA3@coredump.intra.peff.net","subject":"Re: [PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-02T12:55:38Z","receivedAt":"2021-07-02T13:02:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jul 01 2021, Jeff King wrote:\n\n> On Mon, Jun 28, 2021 at 09:19:24PM +0200, Ævar Arnfjörð Bjarmason wrote:\n>\n>> So let's support this common pattern as a \"startup_config\" callback,\n>> making use of our recently added \"call_{advertise,command}()\"\n>> functions. This allows us to simplify e.g. the \"ensure_config_read()\"\n>> function added in 59e1205d167 (ls-refs: report unborn targets of\n>> symrefs, 2021-02-05).\n>> \n>> We could read all the config for all the protocol capabilities, but\n>> let's do it one callback at a time in anticipation that some won't be\n>> called at all, and that some might be more expensive than others in\n>> the future.\n>\n> Sadly I don't think this addresses my \"v2 receive-pack\" concern. The\n> ls_refs_startup_config() function will get called after we've received a\n> request for \"ls-refs\", which is good. But:\n\nI think it does in that you rightly objected to us moving all config to\nsuch a callback, because for some of it we don't have the information\nneeded to look it up yet, we do that in the request handler.\n\nBut for a lot of our config it's fine to do it early, hence \"startup\"\nconfig.\n\nYes I've moved the ls-refs handling into the \"startup\" because /right\nnow/ it's only handling fetches, it'll need to be moved out if and when\nwe start handling pushes.\n\nBut isn't it going to be obvious that we'll need to do that then? Since\nwe'll have the example of upload-pack.c doing that exact thing?\n\nI.e. do you not want to have the \"startup config\" concept at all, or\nwould just prefer to have the ls-refs part of it pre-emotively moved out\nof it in anticipation of handling pushes some day, even if we can do\nthat on \"startup\" now?\n\n(More below)\n\n>> +static void read_startup_config(struct protocol_capability *command)\n>> +{\n>> +\tif (!command->startup_config)\n>> +\t\treturn;\n>> +\tif (command->have_startup_config++)\n>> +\t\treturn;\n>> +\tgit_config(command->startup_config, NULL);\n>> +}\n>\n> ...we don't pass any context to the config callback here. I thought\n> passing \"command\" might work, but looking at the ls_refs() function, it\n> is the one who actually reads the pkt-lines that will tell us \"hey, I'm\n> doing an ls-refs for a push\".\n>\n> So none of the serve() infrastructure can help us there; we need to read\n> pkt-lines and _then_ read config.\n>\n> I dunno. Maybe the solution is for ls_refs() to just do a separate\n> config call to pick up the operation-specific bits, like:\n>\n> diff --git a/ls-refs.c b/ls-refs.c\n> index 88f6c3f60d..6ee70126aa 100644\n> --- a/ls-refs.c\n> +++ b/ls-refs.c\n> @@ -130,12 +130,13 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n>  \n>  static int ls_refs_config(const char *var, const char *value, void *data)\n>  {\n> +\tstruct ls_refs_data *d = data;\n>  \t/*\n>  \t * We only serve fetches over v2 for now, so respect only \"uploadpack\"\n>  \t * config. This may need to eventually be expanded to \"receive\", but we\n>  \t * don't yet know how that information will be passed to ls-refs.\n>  \t */\n> -\treturn parse_hide_refs_config(var, value, \"uploadpack\");\n> +\treturn parse_hide_refs_config(var, value, d->for_push ? \"receive\" : \"uploadpack\");\n>  }\n>  \n>  int ls_refs(struct repository *r, struct strvec *keys,\n> @@ -147,7 +148,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n>  \tstrvec_init(&data.prefixes);\n>  \n>  \tensure_config_read();\n> -\tgit_config(ls_refs_config, NULL);\n>  \n>  \twhile (packet_reader_read(request) == PACKET_READ_NORMAL) {\n>  \t\tconst char *arg = request->line;\n> @@ -161,8 +161,12 @@ int ls_refs(struct repository *r, struct strvec *keys,\n>  \t\t\tstrvec_push(&data.prefixes, out);\n>  \t\telse if (!strcmp(\"unborn\", arg))\n>  \t\t\tdata.unborn = allow_unborn;\n> +\t\telse if (!strcmp(\"for-push\", arg)) /* imagine this exists */\n> +\t\t\tdata.for_push = 1;\n>  \t}\n>  \n> +\tgit_config(ls_refs_config, &data);\n> +\n>  \tif (request->status != PACKET_READ_FLUSH)\n>  \t\tdie(_(\"expected flush after ls-refs arguments\"));\n>  \n>\n> And then it is a separate thing entirely from your \"serve will read\n> config\" code. It's arguably more confusing, because now config is read\n> in two places, but that is already true because of this\n> ensure_config_read() thing.\n\nThis suggests to me you'd like to preemptively move it out of \"startup\",\ncorrect?\n\nAnyway, I can do that if that addresses your concern. I thought the v1\nobjection was mainly \"the config flow won't work that way in all cases\",\nwhich you're right that I incorrectly assumed.\n\nI just thought preemptively doing it for \"ls-refs\" wouldn't be needed,\nsince we'd notice in testing such a feature that \"do it once\" would\nbreak in obvious ways for multi-requests, especially with the comment\nfor the \"startup_config\" callback explicitly calling out that case.\n\n> The patch above obviously makes no sense until we're working on v2\n> receive-pack. But I think it illustrates that your patch here is not\n> getting in the way (though technically I think that would also be true\n> of your v1, I do like this version better).\n"},{"id":"429073","messageId":"YN+BdikYt+lH+L3I@coredump.intra.peff.net","threadId":"55944","inReplyTo":"87o8bkamxb.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-07-02T21:13:26Z","receivedAt":"2021-07-02T21:13:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 02, 2021 at 02:55:38PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> > Sadly I don't think this addresses my \"v2 receive-pack\" concern. The\n> > ls_refs_startup_config() function will get called after we've received a\n> > request for \"ls-refs\", which is good. But:\n> \n> I think it does in that you rightly objected to us moving all config to\n> such a callback, because for some of it we don't have the information\n> needed to look it up yet, we do that in the request handler.\n> \n> But for a lot of our config it's fine to do it early, hence \"startup\"\n> config.\n\nSo I think your patch is OK, but just to lay out my thinking, it was\nthis. The v2 serve code basically does:\n\n  1. Somebody calls serve().\n\n  2. It reads a capability/operation name, then dispatches to the\n     appropriate function.\n\n  3. That function reads operation-specific data, like options.\n\nYour first patch read config at step 1. This one reads at step 2.\n\nBut from past discussions, the hide-refs config could only be read (or\ninterpreted) after step 3, because that's when we'd see an option saying\n\"this is for pushing\".\n\nSo anything except step 3 is \"too late\". But that doesn't stop us from\nhaving some early config and some operation-specific late config.\n\n> Yes I've moved the ls-refs handling into the \"startup\" because /right\n> now/ it's only handling fetches, it'll need to be moved out if and when\n> we start handling pushes.\n> \n> But isn't it going to be obvious that we'll need to do that then? Since\n> we'll have the example of upload-pack.c doing that exact thing?\n>\n> I.e. do you not want to have the \"startup config\" concept at all, or\n> would just prefer to have the ls-refs part of it pre-emotively moved out\n> of it in anticipation of handling pushes some day, even if we can do\n> that on \"startup\" now?\n\nSorry to be so confusing. About halfway through writing my previous\nemail I had realized \"well, I guess we can have two phases of\nconfig-reading, and that's not so bad\". So I'm OK with this direction.\n\nI don't think anything else needs to happen on top of your patch here.\nThe \"late\" phase for ls-refs config reading already happens totally\nseparately (it's in the git_config(ls_refs_config) call, which is\nalready separate from the ensure_config_read() thing. And your patch\nleaves that line untouched.\n\n> > And then it is a separate thing entirely from your \"serve will read\n> > config\" code. It's arguably more confusing, because now config is read\n> > in two places, but that is already true because of this\n> > ensure_config_read() thing.\n> \n> This suggests to me you'd like to preemptively move it out of \"startup\",\n> correct?\n\nIt's already out of the startup, so I think we are OK.\n\nMy big question here was: is the concept of \"startup config\" and \"other\nconfig we read after seeing the options\" too confusing? But probably\nnot.\n\n> I just thought preemptively doing it for \"ls-refs\" wouldn't be needed,\n> since we'd notice in testing such a feature that \"do it once\" would\n> break in obvious ways for multi-requests, especially with the comment\n> for the \"startup_config\" callback explicitly calling out that case.\n\nYeah, we'd definitely notice. I was just wondering if we'd end up\nneeding to back out these changes to accommodate it. But the two-phase\nthing solves that.\n\n-Peff\n"},{"id":"429198","messageId":"CAFQ2z_Pbn2=0NuNNC9TnuLar91Nn2=CBZ7cs8ErM4guEi9BsEg@mail.gmail.com","threadId":"55944","inReplyTo":"patch-7.8-0a4fb01ae38-20210628T191634Z-avarab@gmail.com","subject":"Re: [PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Han-Wen Nienhuys","fromEmail":"hanwen@google.com","sentAt":"2021-07-05T12:23:20Z","receivedAt":"2021-07-05T12:23:36Z","isPatch":true,"sender":{"key":"hanwen@google.com","avatar":"https://avatars.githubusercontent.com/u/31547?v=4"},"body":"On Mon, Jun 28, 2021 at 9:19 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> So let's support this common pattern as a \"startup_config\" callback,\n> making use of our recently added \"call_{advertise,command}()\"\n\nI think this is an improvement over ensure_config_read(), but I don't\nunderstand the whole picture. If you want to control configuration and\nbe sure it is set early, isn't it more natural to put this in struct\nserve_options? With your patch, there are still two routes to\ninjecting configuration: 'struct serve_options' and the git config.\n\nIMO the former is more principled, and the control flow (serve_options\nis an argument to serve()) ensures that the options are read only\nonce.\n\n> +       /*\n> +        * A git_config() callback that'll be called only once for the\n> +        * lifetime of the process, possibly over many different\n\nPutting it in a struct supplied by the caller provides more\nflexibility for testing: you could test multiple behaviors from a\nsingle process, by calling serve multiple times supplying different\narguments.\n\n-- \nHan-Wen Nienhuys - Google Munich\nI work 80%. Don't expect answers from me on Fridays.\n--\nGoogle Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich\nRegistergericht und -nummer: Hamburg, HRB 86891\nSitz der Gesellschaft: Hamburg\nGeschäftsführer: Paul Manicle, Halimah DeLaine Prado\n"},{"id":"429199","messageId":"CAFQ2z_McVjV+DVMZJq21tBsxj2T3EZ5eFJuRWs8ozpNbJWzS1g@mail.gmail.com","threadId":"55944","inReplyTo":"patch-6.8-baeee6539ad-20210628T191634Z-avarab@gmail.com","subject":"Re: [PATCH v2 6/8] serve.c: add trace2 regions for advertise & command","fromName":"Han-Wen Nienhuys","fromEmail":"hanwen@google.com","sentAt":"2021-07-05T12:24:50Z","receivedAt":"2021-07-05T12:25:03Z","isPatch":true,"sender":{"key":"hanwen@google.com","avatar":"https://avatars.githubusercontent.com/u/31547?v=4"},"body":"On Mon, Jun 28, 2021 at 9:19 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Now that we've factored out \"call_{advertise,command}()\" in a\n> preceding commit it becomes easy to trace all these callbacks with\n> trace2. Let's do that. As the tests demonstrate there's no v2 push\n> protocol, which the tests assert.\n\ncode looks good, but confused about the objective. This isn't\nnecessary for the rest of the series, is it?\n\ncleanup changes leading up to here LGTM.\n\n-- \nHan-Wen Nienhuys - Google Munich\nI work 80%. Don't expect answers from me on Fridays.\n--\n\nGoogle Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich\n\nRegistergericht und -nummer: Hamburg, HRB 86891\n\nSitz der Gesellschaft: Hamburg\n\nGeschäftsführer: Paul Manicle, Halimah DeLaine Prado\n"},{"id":"429236","messageId":"CAFQ2z_NyGb8rju5CKzmo6KhZXD0Dp21u-BbyCb2aNxLEoSPRJw@mail.gmail.com","threadId":"55944","inReplyTo":"patch-7.8-0a4fb01ae38-20210628T191634Z-avarab@gmail.com","subject":"Re: [PATCH v2 7/8] serve: add support for a \"startup\" git_config() callback","fromName":"Han-Wen Nienhuys","fromEmail":"hanwen@google.com","sentAt":"2021-07-05T12:34:19Z","receivedAt":"2021-07-05T12:34:32Z","isPatch":true,"sender":{"key":"hanwen@google.com","avatar":"https://avatars.githubusercontent.com/u/31547?v=4"},"body":"On Mon, Jun 28, 2021 at 9:19 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>  /* Main serve loop for protocol version 2 */\n>  void serve(struct serve_options *options)\n\nside note: this is a very short name for a globally visible symbol.\nWould be nice if this were something like protocol_v2_serve_loop() .\nThen one could drop the comment.\n\n-- \nHan-Wen Nienhuys - Google Munich\nI work 80%. Don't expect answers from me on Fridays.\n--\n\nGoogle Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich\n\nRegistergericht und -nummer: Hamburg, HRB 86891\n\nSitz der Gesellschaft: Hamburg\n\nGeschäftsführer: Paul Manicle, Halimah DeLaine Prado\n"},{"id":"429239","messageId":"CAFQ2z_MmpisHcYqXd0=V8NCNxxsXn=MijX747V_oxnMV1bBFaA@mail.gmail.com","threadId":"55944","inReplyTo":"patch-8.8-9fd6138aa62-20210628T191634Z-avarab@gmail.com","subject":"Re: [PATCH v2 8/8] upload-pack.c: convert to new serve.c \"startup\" config cb","fromName":"Han-Wen Nienhuys","fromEmail":"hanwen@google.com","sentAt":"2021-07-05T14:00:20Z","receivedAt":"2021-07-05T14:00:34Z","isPatch":true,"sender":{"key":"hanwen@google.com","avatar":"https://avatars.githubusercontent.com/u/31547?v=4"},"body":"On Mon, Jun 28, 2021 at 9:19 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Convert the config reading code in upload-pack.c to use the new\n> \"startup_config\" callback when we're using the v2 protocol, and\n> lightly fake up the same when we're using v0 and v1.\n\n\nI'm not a fan creating more global variables rather, but I'll it leave\nto someone else to decide what's best. This change looks consistent\nwith its predecessor, and OK modulo a few nits below.\n\n> Thus it's clear that the \"allow_uor\" passed to functions like\n> \"is_our_ref()\" is constant after startup (usually it'll never change\n> for a given server's configuration, or change once).\n>\n> This requires a very light compatibly layer with the serve.c callback\n\ntypo.\n\n> +/*\n> + * \"Global\" configuration that won't be affected by the type of\n> + * request we're doing, or by other request data in \"struct\n> + * upload_pack_data\" below.\n> + */\n> +static int v1_have_startup_config;\n> +static int config_keepalive = 5;\n\ncould this include a unit? config_keepalive_secs , I think?\n\n> +       int ret;\n> +       ret = upload_pack_startup_config(var, value, data);\n> +       if (ret)\n> +               return ret;\n> +       ret = upload_pack_startup_config_v2_only(var, value, data);\n> +       if (ret)\n> +               return ret;\n\nNeither of these config readers ever return -1. Do you foresee this\never happening? Or could this return void?\n\n> +       if (!v1_have_startup_config++)\n> +               git_config(upload_pack_startup_config, NULL);\n\nSee comment in previous patch about ++ for boolean values.\n\n-- \nHan-Wen Nienhuys - Google Munich\nI work 80%. Don't expect answers from me on Fridays.\n--\nGoogle Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich\nRegistergericht und -nummer: Hamburg, HRB 86891\nSitz der Gesellschaft: Hamburg\nGeschäftsführer: Paul Manicle, Halimah DeLaine Prado\n"},{"id":"430833","messageId":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com","subject":"[PATCH v3 00/12] serve.[ch]: general API cleanup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:01Z","receivedAt":"2021-07-21T23:40:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"For v2, see:\nhttps://lore.kernel.org/git/cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com/\n\nJunio: Sorry about the late update, re:\nhttp://lore.kernel.org/git/xmqqsg07cz4g.fsf@gitster.g\n\nI think most people were on the fence about the need for this\n\"startup_config\" callback in v2. I think we could still do without it,\nbut as the new 08/12 shows a big part of the API complexity was due to\nserve() supporting the --advertise-refs mode directly. 08-10/12 is a\nnew late-series cleanup of that, which makes serve.[ch] a lot simpler.\n\nÆvar Arnfjörð Bjarmason (12):\n  serve: mark has_capability() as static\n  transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n  transport: use designated initializers\n  serve: use designated initializers\n  serve.c: add call_{advertise,command}() indirection\n  serve: add support for a \"startup\" git_config() callback\n  serve.c: move version line to advertise_capabilities()\n  serve.[ch]: remove \"serve_options\", split up --advertise-refs code\n  {upload,receive}-pack tests: add --advertise-refs tests\n  upload-pack: document and rename --advertise-refs\n  upload-pack.c: convert to new serve.c \"startup\" config cb\n  serve.[ch]: don't pass \"struct strvec *keys\" to commands\n\n Documentation/git-receive-pack.txt        |   5 +\n Documentation/git-upload-pack.txt         |  12 +-\n Documentation/technical/http-protocol.txt |   3 +\n Documentation/technical/protocol-v2.txt   |   3 +\n builtin/receive-pack.c                    |   3 +-\n builtin/upload-pack.c                     |  28 ++--\n http-backend.c                            |   2 +-\n ls-refs.c                                 |  45 ++----\n ls-refs.h                                 |   5 +-\n protocol-caps.c                           |   3 +-\n protocol-caps.h                           |   4 +-\n serve.c                                   | 146 ++++++++++++++------\n serve.h                                   |  12 +-\n t/helper/test-serve-v2.c                  |  14 +-\n t/t5555-http-smart-common.sh              | 159 ++++++++++++++++++++++\n transport-helper.c                        |  18 +--\n transport-internal.h                      |   2 +-\n transport.c                               |  32 ++---\n upload-pack.c                             | 154 ++++++++++++---------\n upload-pack.h                             |  16 +--\n 20 files changed, 450 insertions(+), 216 deletions(-)\n create mode 100755 t/t5555-http-smart-common.sh\n\nRange-diff against v2:\n 1:  fdb0c5f4df1 =  1:  192fb64ef82 serve: mark has_capability() as static\n 2:  7b8101dca71 =  2:  d716bd3c537 transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n 3:  766045e5f1d =  3:  d31690614af transport: use designated initializers\n 4:  bd920de1629 =  4:  13f1a8d8325 serve: use designated initializers\n 5:  d0b02aa0c7d =  5:  99eeff6f890 serve.c: add call_{advertise,command}() indirection\n 7:  0a4fb01ae38 !  6:  be719dc3dc1 serve: add support for a \"startup\" git_config() callback\n    @@ Commit message\n         upload-pack.c yet, that case is more complex since it deals with both\n         v1 and v2. It will be dealt with in a code a subsequent commit.\n     \n    +    As we'll see in subsequent commits, by moving the\n    +    transfer.advertisesid config reading out of serve() we can simplify\n    +    the codepath around advertising-only requests. See 6b5b6e422ee (serve:\n    +    advertise session ID in v2 capabilities, 2020-11-11)) for the commit\n    +    that added transfer.advertisesid.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## ls-refs.c ##\n    @@ serve.c: static struct protocol_capability capabilities[] = {\n      static int call_advertise(struct protocol_capability *command,\n      \t\t\t  struct repository *r, struct strbuf *value)\n      {\n    -@@ serve.c: static int call_advertise(struct protocol_capability *command,\n    - \tstruct strbuf sb = STRBUF_INIT;\n    - \tconst char *msg;\n    - \n     +\tread_startup_config(command);\n     +\n    - \tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n    - \ttrace2_region_enter(\"serve\", sb.buf, r);\n    - \tret = command->advertise(r, value);\n    -@@ serve.c: static int call_command(struct protocol_capability *command,\n    - \tint ret;\n    - \tstruct strbuf sb = STRBUF_INIT;\n    + \treturn command->advertise(r, value);\n    + }\n      \n    +@@ serve.c: static int call_command(struct protocol_capability *command,\n    + \t\t\tstruct repository *r, struct strvec *keys,\n    + \t\t\tstruct packet_reader *request)\n    + {\n    ++\n     +\tread_startup_config(command);\n     +\n    - \tstrbuf_addf(&sb, \"command/%s\", command->name);\n    - \ttrace2_region_enter(\"serve\", sb.buf, r);\n    - \tret = command->command(r, keys, request);\n    + \treturn command->command(r, keys, request);\n    + }\n    + \n     @@ serve.c: static int process_request(void)\n      /* Main serve loop for protocol version 2 */\n      void serve(struct serve_options *options)\n -:  ----------- >  7:  b7928ddbe9b serve.c: move version line to advertise_capabilities()\n 6:  baeee6539ad !  8:  fb80f152713 serve.c: add trace2 regions for advertise & command\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    serve.c: add trace2 regions for advertise & command\n    +    serve.[ch]: remove \"serve_options\", split up --advertise-refs code\n     \n    -    Now that we've factored out \"call_{advertise,command}()\" in a\n    -    preceding commit it becomes easy to trace all these callbacks with\n    -    trace2. Let's do that. As the tests demonstrate there's no v2 push\n    -    protocol, which the tests assert.\n    +    The \"advertise capabilities\" mode of serve.c added in\n    +    ed10cb952d3 (serve: introduce git-serve, 2018-03-15) is only used by\n    +    the http-backend.c to call {upload,receive}-pack with the\n    +    --advertise-refs parameter. See 42526b478e3 (Add stateless RPC options\n    +    to upload-pack, receive-pack, 2009-10-30).\n    +\n    +    Let's just make cmd_upload_pack() take the two (v2) or three (v2)\n    +    parameters the the v2/v1 servicing functions need directly, and pass\n    +    those in via the function signature. The logic of whether daemon mode\n    +    is implied by the timeout belongs in the v1 function (only used\n    +    there).\n    +\n    +    Once we split up the \"advertise v2 refs\" from \"serve v2 request\" it\n    +    becomes clear that v2 never cared about those in combination. The only\n    +    time it mattered was for v1 to emit its ref advertisement, in that\n    +    case we wanted to emit the smart-http-only \"no-done\" capability.\n    +\n    +    Since we only do that in the --advertise-refs codepath let's just have\n    +    it set \"do_done\" itself in v1's upload_pack() just before send_ref(),\n    +    at that point --advertise-refs and --stateless-rpc in combination are\n    +    redundant (the only user is get_info_refs() in http-backend.c), so we\n    +    can just pass in --advertise-refs only.\n    +\n    +    Since we need to touch all the serve() and advertise_capabilities()\n    +    codepaths let's rename them to less clever and obvious names, it's\n    +    been suggested numerous times, the latest of which is [1]'s suggestion\n    +    for protocol_v2_serve_loop(). Let's go with that.\n    +\n    +    1. https://lore.kernel.org/git/CAFQ2z_NyGb8rju5CKzmo6KhZXD0Dp21u-BbyCb2aNxLEoSPRJw@mail.gmail.com/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    + ## builtin/upload-pack.c ##\n    +@@ builtin/upload-pack.c: int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n    + {\n    + \tconst char *dir;\n    + \tint strict = 0;\n    +-\tstruct upload_pack_options opts = { 0 };\n    +-\tstruct serve_options serve_opts = SERVE_OPTIONS_INIT;\n    ++\tint advertise_refs = 0;\n    ++\tint stateless_rpc = 0;\n    ++\tint timeout = 0;\n    + \tstruct option options[] = {\n    +-\t\tOPT_BOOL(0, \"stateless-rpc\", &opts.stateless_rpc,\n    ++\t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n    + \t\t\t N_(\"quit after a single request/response exchange\")),\n    +-\t\tOPT_BOOL(0, \"advertise-refs\", &opts.advertise_refs,\n    ++\t\tOPT_BOOL(0, \"advertise-refs\", &advertise_refs,\n    + \t\t\t N_(\"exit immediately after initial ref advertisement\")),\n    + \t\tOPT_BOOL(0, \"strict\", &strict,\n    + \t\t\t N_(\"do not try <directory>/.git/ if <directory> is no Git directory\")),\n    +-\t\tOPT_INTEGER(0, \"timeout\", &opts.timeout,\n    ++\t\tOPT_INTEGER(0, \"timeout\", &timeout,\n    + \t\t\t    N_(\"interrupt transfer after <n> seconds of inactivity\")),\n    + \t\tOPT_END()\n    + \t};\n    +@@ builtin/upload-pack.c: int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n    + \tif (argc != 1)\n    + \t\tusage_with_options(upload_pack_usage, options);\n    + \n    +-\tif (opts.timeout)\n    +-\t\topts.daemon_mode = 1;\n    +-\n    + \tsetup_path();\n    + \n    + \tdir = argv[0];\n    +@@ builtin/upload-pack.c: int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n    + \n    + \tswitch (determine_protocol_version_server()) {\n    + \tcase protocol_v2:\n    +-\t\tserve_opts.advertise_capabilities = opts.advertise_refs;\n    +-\t\tserve_opts.stateless_rpc = opts.stateless_rpc;\n    +-\t\tserve(&serve_opts);\n    ++\t\tif (advertise_refs)\n    ++\t\t\tprotocol_v2_advertise_capabilities();\n    ++\t\telse\n    ++\t\t\tprotocol_v2_serve_loop(stateless_rpc);\n    + \t\tbreak;\n    + \tcase protocol_v1:\n    + \t\t/*\n    + \t\t * v1 is just the original protocol with a version string,\n    + \t\t * so just fall through after writing the version string.\n    + \t\t */\n    +-\t\tif (opts.advertise_refs || !opts.stateless_rpc)\n    ++\t\tif (advertise_refs || !stateless_rpc)\n    + \t\t\tpacket_write_fmt(1, \"version 1\\n\");\n    + \n    + \t\t/* fallthrough */\n    + \tcase protocol_v0:\n    +-\t\tupload_pack(&opts);\n    ++\t\tupload_pack(advertise_refs, stateless_rpc, timeout);\n    + \t\tbreak;\n    + \tcase protocol_unknown_version:\n    + \t\tBUG(\"unknown protocol version\");\n    +\n    + ## http-backend.c ##\n    +@@ http-backend.c: static void get_info_refs(struct strbuf *hdr, char *arg)\n    + \n    + \tif (service_name) {\n    + \t\tconst char *argv[] = {NULL /* service name */,\n    +-\t\t\t\"--stateless-rpc\", \"--advertise-refs\",\n    ++\t\t\t\"--advertise-refs\",\n    + \t\t\t\".\", NULL};\n    + \t\tstruct rpc_service *svc = select_service(hdr, service_name);\n    + \n    +\n      ## serve.c ##\n    -@@ serve.c: static struct protocol_capability capabilities[] = {\n    - static int call_advertise(struct protocol_capability *command,\n    - \t\t\t  struct repository *r, struct strbuf *value)\n    +@@ serve.c: static int call_command(struct protocol_capability *command,\n    + \treturn command->command(r, keys, request);\n    + }\n    + \n    +-static void advertise_capabilities(void)\n    ++void protocol_v2_advertise_capabilities(void)\n      {\n    --\treturn command->advertise(r, value);\n    -+\tint ret;\n    -+\tstruct strbuf sb = STRBUF_INIT;\n    -+\tconst char *msg;\n    -+\n    -+\tstrbuf_addf(&sb, \"advertise/%s\", command->name);\n    -+\ttrace2_region_enter(\"serve\", sb.buf, r);\n    -+\tret = command->advertise(r, value);\n    -+\tmsg = ret ? \"advertised\" : \"hidden\";\n    -+\ttrace2_region_leave_printf(\"serve\", sb.buf, r, \"%s\", msg);\n    -+\tstrbuf_release(&sb);\n    -+\n    -+\treturn ret;\n    + \tstruct strbuf capability = STRBUF_INIT;\n    + \tstruct strbuf value = STRBUF_INIT;\n    +@@ serve.c: static int process_request(void)\n    + \treturn 0;\n      }\n      \n    - static int call_command(struct protocol_capability *command,\n    - \t\t\tstruct repository *r, struct strvec *keys,\n    - \t\t\tstruct packet_reader *request)\n    +-/* Main serve loop for protocol version 2 */\n    +-void serve(struct serve_options *options)\n    ++void protocol_v2_serve_loop(int stateless_rpc)\n      {\n    --\treturn command->command(r, keys, request);\n    -+\tint ret;\n    -+\tstruct strbuf sb = STRBUF_INIT;\n    -+\n    -+\tstrbuf_addf(&sb, \"command/%s\", command->name);\n    -+\ttrace2_region_enter(\"serve\", sb.buf, r);\n    -+\tret = command->command(r, keys, request);\n    -+\ttrace2_region_leave(\"serve\", sb.buf, r);\n    -+\tstrbuf_release(&sb);\n    +-\tif (options->advertise_capabilities || !options->stateless_rpc) {\n    +-\t\tadvertise_capabilities();\n    +-\t\t/*\n    +-\t\t * If only the list of capabilities was requested exit\n    +-\t\t * immediately after advertising capabilities\n    +-\t\t */\n    +-\t\tif (options->advertise_capabilities)\n    +-\t\t\treturn;\n    +-\t}\n    ++\tif (!stateless_rpc)\n    ++\t\tprotocol_v2_advertise_capabilities();\n    + \n    + \t/*\n    + \t * If stateless-rpc was requested then exit after\n    + \t * a single request/response exchange\n    + \t */\n    +-\tif (options->stateless_rpc) {\n    ++\tif (stateless_rpc) {\n    + \t\tprocess_request();\n    + \t} else {\n    + \t\tfor (;;)\n    +\n    + ## serve.h ##\n    +@@\n    + #ifndef SERVE_H\n    + #define SERVE_H\n    + \n    +-struct serve_options {\n    +-\tunsigned advertise_capabilities;\n    +-\tunsigned stateless_rpc;\n    +-};\n    +-#define SERVE_OPTIONS_INIT { 0 }\n    +-void serve(struct serve_options *options);\n    ++void protocol_v2_advertise_capabilities(void);\n    ++void protocol_v2_serve_loop(int stateless_rpc);\n    + \n    + #endif /* SERVE_H */\n    +\n    + ## t/helper/test-serve-v2.c ##\n    +@@ t/helper/test-serve-v2.c: static char const * const serve_usage[] = {\n    + \n    + int cmd__serve_v2(int argc, const char **argv)\n    + {\n    +-\tstruct serve_options opts = SERVE_OPTIONS_INIT;\n    +-\n    ++\tint stateless_rpc = 0;\n    ++\tint advertise_capabilities = 0;\n    + \tstruct option options[] = {\n    +-\t\tOPT_BOOL(0, \"stateless-rpc\", &opts.stateless_rpc,\n    ++\t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n    + \t\t\t N_(\"quit after a single request/response exchange\")),\n    +-\t\tOPT_BOOL(0, \"advertise-capabilities\", &opts.advertise_capabilities,\n    ++\t\tOPT_BOOL(0, \"advertise-capabilities\", &advertise_capabilities,\n    + \t\t\t N_(\"exit immediately after advertising capabilities\")),\n    + \t\tOPT_END()\n    + \t};\n    +@@ t/helper/test-serve-v2.c: int cmd__serve_v2(int argc, const char **argv)\n    + \targc = parse_options(argc, argv, prefix, options, serve_usage,\n    + \t\t\t     PARSE_OPT_KEEP_DASHDASH |\n    + \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n    +-\tserve(&opts);\n     +\n    -+\treturn ret;\n    ++\tif (advertise_capabilities)\n    ++\t\tprotocol_v2_advertise_capabilities();\n    ++\telse\n    ++\t\tprotocol_v2_serve_loop(stateless_rpc);\n    + \n    + \treturn 0;\n    + }\n    +\n    + ## upload-pack.c ##\n    +@@ upload-pack.c: static int send_ref(const char *refname, const struct object_id *oid,\n    + \t\t\t\t     \" allow-tip-sha1-in-want\" : \"\",\n    + \t\t\t     (data->allow_uor & ALLOW_REACHABLE_SHA1) ?\n    + \t\t\t\t     \" allow-reachable-sha1-in-want\" : \"\",\n    +-\t\t\t     data->stateless_rpc ? \" no-done\" : \"\",\n    ++\t\t\t     data->no_done ? \" no-done\" : \"\",\n    + \t\t\t     symref_info.buf,\n    + \t\t\t     data->allow_filter ? \" filter\" : \"\",\n    + \t\t\t     session_id.buf,\n    +@@ upload-pack.c: static int upload_pack_config(const char *var, const char *value, void *cb_data)\n    + \treturn parse_hide_refs_config(var, value, \"uploadpack\");\n      }\n      \n    - static void advertise_capabilities(void)\n    +-void upload_pack(struct upload_pack_options *options)\n    ++void upload_pack(const int advertise_refs, const int stateless_rpc,\n    ++\t\t const int timeout)\n    + {\n    + \tstruct packet_reader reader;\n    + \tstruct upload_pack_data data;\n    +@@ upload-pack.c: void upload_pack(struct upload_pack_options *options)\n    + \n    + \tgit_config(upload_pack_config, &data);\n    + \n    +-\tdata.stateless_rpc = options->stateless_rpc;\n    +-\tdata.daemon_mode = options->daemon_mode;\n    +-\tdata.timeout = options->timeout;\n    ++\tdata.stateless_rpc = stateless_rpc;\n    ++\tdata.timeout = timeout;\n    ++\tif (data.timeout)\n    ++\t\tdata.daemon_mode = 1;\n    + \n    + \thead_ref_namespaced(find_symref, &data.symref);\n    + \n    +-\tif (options->advertise_refs || !data.stateless_rpc) {\n    ++\tif (advertise_refs || !data.stateless_rpc) {\n    + \t\treset_timeout(data.timeout);\n    ++\t\tif (advertise_refs)\n    ++\t\t\tdata.no_done = 1;\n    + \t\thead_ref_namespaced(send_ref, &data);\n    + \t\tfor_each_namespaced_ref(send_ref, &data);\n    + \t\tadvertise_shallow_grafts(1);\n    +@@ upload-pack.c: void upload_pack(struct upload_pack_options *options)\n    + \t\tfor_each_namespaced_ref(check_ref, NULL);\n    + \t}\n    + \n    +-\tif (!options->advertise_refs) {\n    ++\tif (!advertise_refs) {\n    + \t\tpacket_reader_init(&reader, 0, NULL, 0,\n    + \t\t\t\t   PACKET_READ_CHOMP_NEWLINE |\n    + \t\t\t\t   PACKET_READ_DIE_ON_ERR_PACKET);\n     \n    - ## t/t5705-session-id-in-capabilities.sh ##\n    -@@ t/t5705-session-id-in-capabilities.sh: do\n    - \t\t\t--upload-pack \"GIT_TRACE2_EVENT=\\\"$(pwd)/tr2-server-events\\\" git-upload-pack\" \\\n    - \t\t\torigin &&\n    - \t\tgrep \\\"key\\\":\\\"server-sid\\\" tr2-client-events &&\n    --\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events\n    -+\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events &&\n    -+\n    -+\t\tif test \"$PROTO\" = 2\n    -+\t\tthen\n    -+\t\t\tgrep \\\"event\\\":\\\"region_enter\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events &&\n    -+\t\t\tgrep \\\"event\\\":\\\"region_leave\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events\n    -+\t\tfi\n    - \t'\n    - \n    - \ttest_expect_success \"session IDs advertised (push v${PROTO})\" '\n    -@@ t/t5705-session-id-in-capabilities.sh: do\n    - \t\t\t--receive-pack \"GIT_TRACE2_EVENT=\\\"$(pwd)/tr2-server-events\\\" git-receive-pack\" \\\n    - \t\t\torigin HEAD:new-branch &&\n    - \t\tgrep \\\"key\\\":\\\"server-sid\\\" tr2-client-events &&\n    --\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events\n    -+\t\tgrep \\\"key\\\":\\\"client-sid\\\" tr2-server-events &&\n    -+\n    -+\t\tif test \"$PROTO\" = 2\n    -+\t\tthen\n    -+\t\t\t! grep \\\"event\\\":\\\"region_enter\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events &&\n    -+\t\t\t! grep \\\"event\\\":\\\"region_leave\\\".*\\\"category\\\":\\\"serve\\\" tr2-server-events\n    -+\t\tfi\n    - \t'\n    - done\n    + ## upload-pack.h ##\n    +@@\n    + #ifndef UPLOAD_PACK_H\n    + #define UPLOAD_PACK_H\n    + \n    +-struct upload_pack_options {\n    +-\tint stateless_rpc;\n    +-\tint advertise_refs;\n    +-\tunsigned int timeout;\n    +-\tint daemon_mode;\n    +-};\n    +-\n    +-void upload_pack(struct upload_pack_options *options);\n    ++void upload_pack(const int advertise_refs, const int stateless_rpc,\n    ++\t\t const int timeout);\n      \n    + struct repository;\n    + struct strvec;\n -:  ----------- >  9:  beafe9811c1 {upload,receive}-pack tests: add --advertise-refs tests\n -:  ----------- > 10:  c6870b5f18a upload-pack: document and rename --advertise-refs\n 8:  9fd6138aa62 ! 11:  2d4c3d0d463 upload-pack.c: convert to new serve.c \"startup\" config cb\n    @@ Commit message\n         \"is_our_ref()\" is constant after startup (usually it'll never change\n         for a given server's configuration, or change once).\n     \n    -    This requires a very light compatibly layer with the serve.c callback\n    -    mechanism in the form of \"upload_pack\" for the v0 and v1 protocols.\n    +    This requires a very light compatibility layer with the serve.c\n    +    callback mechanism in the form of \"upload_pack\" for the v0 and v1\n    +    protocols.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ upload-pack.c: enum allow_uor {\n     + * upload_pack_data\" below.\n     + */\n     +static int v1_have_startup_config;\n    -+static int config_keepalive = 5;\n    ++static int config_keepalive_secs = 5;\n     +static enum allow_uor config_allow_uor;\n     +static unsigned config_advertise_sid;\n     +static const char *config_pack_objects_hook;\n    @@ upload-pack.c: static void create_pack_file(struct upload_pack_data *pack_data,\n      \t\t\tbreak;\n      \n     -\t\tpolltimeout = pack_data->keepalive < 0\n    -+\t\tpolltimeout = config_keepalive < 0\n    ++\t\tpolltimeout = config_keepalive_secs < 0\n      \t\t\t? -1\n     -\t\t\t: 1000 * pack_data->keepalive;\n    -+\t\t\t: 1000 * config_keepalive;\n    ++\t\t\t: 1000 * config_keepalive_secs;\n      \n      \t\tret = poll(pfd, pollsize, polltimeout);\n      \n    @@ upload-pack.c: static int send_ref(const char *refname, const struct object_id *\n     -\t\t\t     (data->allow_uor & ALLOW_REACHABLE_SHA1) ?\n     +\t\t\t     (config_allow_uor & ALLOW_REACHABLE_SHA1) ?\n      \t\t\t\t     \" allow-reachable-sha1-in-want\" : \"\",\n    - \t\t\t     data->stateless_rpc ? \" no-done\" : \"\",\n    + \t\t\t     data->no_done ? \" no-done\" : \"\",\n      \t\t\t     symref_info.buf,\n     @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char *value,\n      \treturn 0;\n    @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char\n     -\t\tdata->allow_ref_in_want = git_config_bool(var, value);\n     -\t} else if (!strcmp(\"uploadpack.allowsidebandall\", var)) {\n     -\t\tdata->allow_sideband_all = git_config_bool(var, value);\n    -+\t\tconfig_keepalive = git_config_int(var, value);\n    -+\t\tif (!config_keepalive)\n    -+\t\t\tconfig_keepalive = -1;\n    ++\t\tconfig_keepalive_secs = git_config_int(var, value);\n    ++\t\tif (!config_keepalive_secs)\n    ++\t\t\tconfig_keepalive_secs = -1;\n      \t} else if (!strcmp(\"core.precomposeunicode\", var)) {\n      \t\tprecomposed_unicode = git_config_bool(var, value);\n      \t} else if (!strcmp(\"transfer.advertisesid\", var)) {\n    @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char\n      \t\tif (!strcmp(\"uploadpack.packobjectshook\", var))\n     -\t\t\treturn git_config_string(&data->pack_objects_hook, var, value);\n     +\t\t\treturn git_config_string(&config_pack_objects_hook, var, value);\n    -+\t}\n    + \t}\n     +\treturn 0;\n     +}\n     +\n    @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char\n     +\n     +int serve_upload_pack_startup_config(const char *var, const char *value, void *data)\n     +{\n    -+\tint ret;\n    -+\tret = upload_pack_startup_config(var, value, data);\n    -+\tif (ret)\n    -+\t\treturn ret;\n    -+\tret = upload_pack_startup_config_v2_only(var, value, data);\n    -+\tif (ret)\n    -+\t\treturn ret;\n    ++\tupload_pack_startup_config(var, value, data);\n    ++\tupload_pack_startup_config_v2_only(var, value, data);\n     +\treturn 0;\n     +}\n     +\n    @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char\n     +{\n     +\tstruct upload_pack_data *data = cb_data;\n     +\n    -+\tif (!strcmp(\"uploadpack.allowfilter\", var)) {\n    ++\tif (!strcmp(\"uploadpack.allowfilter\", var))\n     +\t\tdata->allow_filter = git_config_bool(var, value);\n    - \t}\n      \n      \tif (parse_object_filter_config(var, value, data) < 0)\n    -@@ upload-pack.c: void upload_pack(struct upload_pack_options *options)\n    + \t\treturn -1;\n    +@@ upload-pack.c: void upload_pack(const int advertise_refs, const int stateless_rpc,\n      \n      \tupload_pack_data_init(&data);\n      \n    @@ upload-pack.c: void upload_pack(struct upload_pack_options *options)\n     +\t\tgit_config(upload_pack_startup_config, NULL);\n      \tgit_config(upload_pack_config, &data);\n      \n    - \tdata.stateless_rpc = options->stateless_rpc;\n    + \tdata.stateless_rpc = stateless_rpc;\n     @@ upload-pack.c: static void process_args(struct packet_reader *request,\n      \t\t/* process want */\n      \t\tif (parse_want(&data->writer, arg, &data->want_obj))\n -:  ----------- > 12:  e4eb31b5b8e serve.[ch]: don't pass \"struct strvec *keys\" to commands\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430834","messageId":"patch-01.12-192fb64ef82-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 01/12] serve: mark has_capability() as static","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:02Z","receivedAt":"2021-07-21T23:40:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The has_capability() function introduced in ed10cb952d3 (serve:\nintroduce git-serve, 2018-03-15) has never been used anywhere except\nserve.c, so let's mark it as static.\n\nIt was later changed from \"extern\" in 554544276a6 (*.[ch]: remove\nextern from function declarations using spatch, 2019-04-29), but we\ncould have simply marked it as \"static\" instead.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 4 ++--\n serve.h | 4 ----\n 2 files changed, 2 insertions(+), 6 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex aa8209f147e..6748c590b74 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -156,8 +156,8 @@ static int is_command(const char *key, struct protocol_capability **command)\n \treturn 0;\n }\n \n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value)\n+static int has_capability(const struct strvec *keys, const char *capability,\n+\t\t\t  const char **value)\n {\n \tint i;\n \tfor (i = 0; i < keys->nr; i++) {\ndiff --git a/serve.h b/serve.h\nindex fc2683e24d3..56da77a87af 100644\n--- a/serve.h\n+++ b/serve.h\n@@ -1,10 +1,6 @@\n #ifndef SERVE_H\n #define SERVE_H\n \n-struct strvec;\n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value);\n-\n struct serve_options {\n \tunsigned advertise_capabilities;\n \tunsigned stateless_rpc;\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430835","messageId":"patch-02.12-d716bd3c537-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 02/12] transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:03Z","receivedAt":"2021-07-21T23:40:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"fetch\" member of the transport_vtable to \"fetch_refs\" for\nconsistency with the existing \"push_refs\". Neither of them just push\n\"refs\" but refs and objects, but having the two match makes the code\nmore readable than having it be inconsistent, especially since\n\"fetch_refs\" is a lot easier to grep for than \"fetch\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c   | 8 ++++----\n transport-internal.h | 2 +-\n transport.c          | 2 +-\n 3 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 4be035edb8b..8d445a8f3ee 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -671,8 +671,8 @@ static int connect_helper(struct transport *transport, const char *name,\n static struct ref *get_refs_list_using_list(struct transport *transport,\n \t\t\t\t\t    int for_push);\n \n-static int fetch(struct transport *transport,\n-\t\t int nr_heads, struct ref **to_fetch)\n+static int fetch_refs(struct transport *transport,\n+\t\t      int nr_heads, struct ref **to_fetch)\n {\n \tstruct helper_data *data = transport->data;\n \tint i, count;\n@@ -681,7 +681,7 @@ static int fetch(struct transport *transport,\n \n \tif (process_connect(transport, 0)) {\n \t\tdo_take_over(transport);\n-\t\treturn transport->vtable->fetch(transport, nr_heads, to_fetch);\n+\t\treturn transport->vtable->fetch_refs(transport, nr_heads, to_fetch);\n \t}\n \n \t/*\n@@ -1263,7 +1263,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n static struct transport_vtable vtable = {\n \tset_helper_option,\n \tget_refs_list,\n-\tfetch,\n+\tfetch_refs,\n \tpush_refs,\n \tconnect_helper,\n \trelease_helper\ndiff --git a/transport-internal.h b/transport-internal.h\nindex b60f1ba9077..c4ca0b733ac 100644\n--- a/transport-internal.h\n+++ b/transport-internal.h\n@@ -34,7 +34,7 @@ struct transport_vtable {\n \t * get_refs_list(), it should set the old_sha1 fields in the\n \t * provided refs now.\n \t **/\n-\tint (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);\n+\tint (*fetch_refs)(struct transport *transport, int refs_nr, struct ref **refs);\n \n \t/**\n \t * Push the objects and refs. Send the necessary objects, and\ndiff --git a/transport.c b/transport.c\nindex 17e9629710a..3e8a27b0321 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1453,7 +1453,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)\n \t\t\theads[nr_heads++] = rm;\n \t}\n \n-\trc = transport->vtable->fetch(transport, nr_heads, heads);\n+\trc = transport->vtable->fetch_refs(transport, nr_heads, heads);\n \n \tfree(heads);\n \treturn rc;\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430836","messageId":"patch-03.12-d31690614af-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 03/12] transport: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:04Z","receivedAt":"2021-07-21T23:40:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the assignments to the various transport_vtables to use\ndesignated initializers, this makes the code easier to read and\nmaintain.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c | 12 ++++++------\n transport.c        | 30 ++++++++++++------------------\n 2 files changed, 18 insertions(+), 24 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 8d445a8f3ee..e8dbdd11530 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -1261,12 +1261,12 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n }\n \n static struct transport_vtable vtable = {\n-\tset_helper_option,\n-\tget_refs_list,\n-\tfetch_refs,\n-\tpush_refs,\n-\tconnect_helper,\n-\trelease_helper\n+\t.set_option\t= set_helper_option,\n+\t.get_refs_list\t= get_refs_list,\n+\t.fetch_refs\t= fetch_refs,\n+\t.push_refs\t= push_refs,\n+\t.connect\t= connect_helper,\n+\t.disconnect\t= release_helper\n };\n \n int transport_helper_init(struct transport *transport, const char *name)\ndiff --git a/transport.c b/transport.c\nindex 3e8a27b0321..f9400b9b0bd 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -883,12 +883,10 @@ static int disconnect_git(struct transport *transport)\n }\n \n static struct transport_vtable taken_over_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tNULL,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.disconnect\t= disconnect_git\n };\n \n void transport_take_over(struct transport *transport,\n@@ -1032,21 +1030,17 @@ void transport_check_allowed(const char *type)\n }\n \n static struct transport_vtable bundle_vtable = {\n-\tNULL,\n-\tget_refs_from_bundle,\n-\tfetch_refs_from_bundle,\n-\tNULL,\n-\tNULL,\n-\tclose_bundle\n+\t.get_refs_list\t= get_refs_from_bundle,\n+\t.fetch_refs\t= fetch_refs_from_bundle,\n+\t.disconnect\t= close_bundle\n };\n \n static struct transport_vtable builtin_smart_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tconnect_git,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.connect\t= connect_git,\n+\t.disconnect\t= disconnect_git\n };\n \n struct transport *transport_get(struct remote *remote, const char *url)\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430837","messageId":"patch-04.12-13f1a8d8325-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 04/12] serve: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:05Z","receivedAt":"2021-07-21T23:40:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the declaration of the protocol_capability struct to use\ndesignated initializers, this makes this more verbose now, but a\nfollow-up commit will add a new field. At that point these lines would\nbe too dense to be on one line comfortably.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 38 +++++++++++++++++++++++++++++++-------\n 1 file changed, 31 insertions(+), 7 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 6748c590b74..49ea9fc8fd5 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -73,13 +73,37 @@ struct protocol_capability {\n };\n \n static struct protocol_capability capabilities[] = {\n-\t{ \"agent\", agent_advertise, NULL },\n-\t{ \"ls-refs\", ls_refs_advertise, ls_refs },\n-\t{ \"fetch\", upload_pack_advertise, upload_pack_v2 },\n-\t{ \"server-option\", always_advertise, NULL },\n-\t{ \"object-format\", object_format_advertise, NULL },\n-\t{ \"session-id\", session_id_advertise, NULL },\n-\t{ \"object-info\", always_advertise, cap_object_info },\n+\t{\n+\t\t.name = \"agent\",\n+\t\t.advertise = agent_advertise,\n+\t},\n+\t{\n+\t\t.name = \"ls-refs\",\n+\t\t.advertise = ls_refs_advertise,\n+\t\t.command = ls_refs,\n+\t},\n+\t{\n+\t\t.name = \"fetch\",\n+\t\t.advertise = upload_pack_advertise,\n+\t\t.command = upload_pack_v2,\n+\t},\n+\t{\n+\t\t.name = \"server-option\",\n+\t\t.advertise = always_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-format\",\n+\t\t.advertise = object_format_advertise,\n+\t},\n+\t{\n+\t\t.name = \"session-id\",\n+\t\t.advertise = session_id_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-info\",\n+\t\t.advertise = always_advertise,\n+\t\t.command = cap_object_info,\n+\t},\n };\n \n static void advertise_capabilities(void)\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430838","messageId":"patch-05.12-99eeff6f890-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 05/12] serve.c: add call_{advertise,command}() indirection","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:06Z","receivedAt":"2021-07-21T23:40:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Instead of directly calling the callbacks defined in the \"struct\nprotocol_capability\", let's call them via new\n\"call_{advertise,command}()\" functions. There's no use in this\nindirection now, but it'll be used in a subsequent commit to ensure\nthat config is read before they're called.\n\nSee ed10cb952d3 (serve: introduce git-serve, 2018-03-15) for the\nintroduction of the code being changed here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 33 ++++++++++++++++++++++++---------\n 1 file changed, 24 insertions(+), 9 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 49ea9fc8fd5..85cd3eab26e 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -42,6 +42,10 @@ static int session_id_advertise(struct repository *r, struct strbuf *value)\n \treturn 1;\n }\n \n+typedef int (*advertise_fn_t)(struct repository *r, struct strbuf *value);\n+typedef int (*command_fn_t)(struct repository *r, struct strvec *keys,\n+\t\t\t    struct packet_reader *request);\n+\n struct protocol_capability {\n \t/*\n \t * The name of the capability.  The server uses this name when\n@@ -56,7 +60,7 @@ struct protocol_capability {\n \t * If a value is added to 'value', the server will advertise this\n \t * capability as \"<name>=<value>\" instead of \"<name>\".\n \t */\n-\tint (*advertise)(struct repository *r, struct strbuf *value);\n+\tadvertise_fn_t advertise;\n \n \t/*\n \t * Function called when a client requests the capability as a command.\n@@ -67,9 +71,7 @@ struct protocol_capability {\n \t *\n \t * This field should be NULL for capabilities which are not commands.\n \t */\n-\tint (*command)(struct repository *r,\n-\t\t       struct strvec *keys,\n-\t\t       struct packet_reader *request);\n+\tcommand_fn_t command;\n };\n \n static struct protocol_capability capabilities[] = {\n@@ -106,6 +108,19 @@ static struct protocol_capability capabilities[] = {\n \t},\n };\n \n+static int call_advertise(struct protocol_capability *command,\n+\t\t\t  struct repository *r, struct strbuf *value)\n+{\n+\treturn command->advertise(r, value);\n+}\n+\n+static int call_command(struct protocol_capability *command,\n+\t\t\tstruct repository *r, struct strvec *keys,\n+\t\t\tstruct packet_reader *request)\n+{\n+\treturn command->command(r, keys, request);\n+}\n+\n static void advertise_capabilities(void)\n {\n \tstruct strbuf capability = STRBUF_INIT;\n@@ -115,7 +130,7 @@ static void advertise_capabilities(void)\n \tfor (i = 0; i < ARRAY_SIZE(capabilities); i++) {\n \t\tstruct protocol_capability *c = &capabilities[i];\n \n-\t\tif (c->advertise(the_repository, &value)) {\n+\t\tif (call_advertise(c, the_repository, &value)) {\n \t\t\tstrbuf_addstr(&capability, c->name);\n \n \t\t\tif (value.len) {\n@@ -155,9 +170,9 @@ static struct protocol_capability *get_capability(const char *key)\n \n static int is_valid_capability(const char *key)\n {\n-\tconst struct protocol_capability *c = get_capability(key);\n+\tstruct protocol_capability *c = get_capability(key);\n \n-\treturn c && c->advertise(the_repository, NULL);\n+\treturn c && call_advertise(c, the_repository, NULL);\n }\n \n static int is_command(const char *key, struct protocol_capability **command)\n@@ -170,7 +185,7 @@ static int is_command(const char *key, struct protocol_capability **command)\n \t\tif (*command)\n \t\t\tdie(\"command '%s' requested after already requesting command '%s'\",\n \t\t\t    out, (*command)->name);\n-\t\tif (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)\n+\t\tif (!cmd || !call_advertise(cmd, the_repository, NULL) || !cmd->command)\n \t\t\tdie(\"invalid command '%s'\", out);\n \n \t\t*command = cmd;\n@@ -294,7 +309,7 @@ static int process_request(void)\n \tif (has_capability(&keys, \"session-id\", &client_sid))\n \t\ttrace2_data_string(\"transfer\", NULL, \"client-sid\", client_sid);\n \n-\tcommand->command(the_repository, &keys, &reader);\n+\tcall_command(command, the_repository, &keys, &reader);\n \n \tstrvec_clear(&keys);\n \treturn 0;\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430839","messageId":"patch-06.12-be719dc3dc1-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 06/12] serve: add support for a \"startup\" git_config() callback","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:07Z","receivedAt":"2021-07-21T23:40:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the introduction of serve.c we've added git_config() callbacks\nand other config reading for capabilities in the following commits:\n\n- e20b4192a37 (upload-pack: support hidden refs with protocol v2, 2018-12-18)\n- 08450ef7918 (upload-pack: clear filter_options for each v2 fetch command, 2020-05-08)\n- 6b5b6e422ee (serve: advertise session ID in v2 capabilities, 2020-11-11)\n- 59e1205d167 (ls-refs: report unborn targets of symrefs, 2021-02-05)\n\nOf these 08450ef7918 fixed code that needed to read config on a\nper-request basis, whereas most of the config reading just wants to\ncheck if we've enabled one semi-static config variable or other. We'd\nlike to re-read that value eventually, but from request-to-request\nit's OK if we retain the old one, and it isn't impacted by other\nrequest data.\n\nSo let's support this common pattern as a \"startup_config\" callback,\nmaking use of our recently added \"call_{advertise,command}()\"\nfunctions. This allows us to simplify e.g. the \"ensure_config_read()\"\nfunction added in 59e1205d167 (ls-refs: report unborn targets of\nsymrefs, 2021-02-05).\n\nWe could read all the config for all the protocol capabilities, but\nlet's do it one callback at a time in anticipation that some won't be\ncalled at all, and that some might be more expensive than others in\nthe future.\n\nI'm not migrating over the code in the upload_pack_v2 function in\nupload-pack.c yet, that case is more complex since it deals with both\nv1 and v2. It will be dealt with in a code a subsequent commit.\n\nAs we'll see in subsequent commits, by moving the\ntransfer.advertisesid config reading out of serve() we can simplify\nthe codepath around advertising-only requests. See 6b5b6e422ee (serve:\nadvertise session ID in v2 capabilities, 2020-11-11)) for the commit\nthat added transfer.advertisesid.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n ls-refs.c | 42 ++++++++++++++----------------------------\n ls-refs.h |  1 +\n serve.c   | 43 +++++++++++++++++++++++++++++++++++++++++--\n 3 files changed, 56 insertions(+), 30 deletions(-)\n\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 88f6c3f60d8..02fbcfd9bde 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -7,37 +7,26 @@\n #include \"pkt-line.h\"\n #include \"config.h\"\n \n-static int config_read;\n-static int advertise_unborn;\n-static int allow_unborn;\n+/* \"unborn\" is on by default if there's no lsrefs.unborn config */\n+static int advertise_unborn = 1;\n+static int allow_unborn = 1;\n \n-static void ensure_config_read(void)\n+int ls_refs_startup_config(const char *var, const char *value, void *data)\n {\n-\tconst char *str = NULL;\n-\n-\tif (config_read)\n-\t\treturn;\n-\n-\tif (repo_config_get_string_tmp(the_repository, \"lsrefs.unborn\", &str)) {\n-\t\t/*\n-\t\t * If there is no such config, advertise and allow it by\n-\t\t * default.\n-\t\t */\n-\t\tadvertise_unborn = 1;\n-\t\tallow_unborn = 1;\n-\t} else {\n-\t\tif (!strcmp(str, \"advertise\")) {\n-\t\t\tadvertise_unborn = 1;\n+\tif (!strcmp(var, \"lsrefs.unborn\")) {\n+\t\tif (!strcmp(value, \"advertise\")) {\n+\t\t\t/* Allowed and advertised by default */\n+\t\t} else if (!strcmp(value, \"allow\")) {\n+\t\t\tadvertise_unborn = 0;\n \t\t\tallow_unborn = 1;\n-\t\t} else if (!strcmp(str, \"allow\")) {\n-\t\t\tallow_unborn = 1;\n-\t\t} else if (!strcmp(str, \"ignore\")) {\n-\t\t\t/* do nothing */\n+\t\t} else if (!strcmp(value, \"ignore\")) {\n+\t\t\tadvertise_unborn = 0;\n+\t\t\tallow_unborn = 0;\n \t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n+\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), value);\n \t\t}\n \t}\n-\tconfig_read = 1;\n+\treturn 0;\n }\n \n /*\n@@ -145,8 +134,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n \n \tmemset(&data, 0, sizeof(data));\n \tstrvec_init(&data.prefixes);\n-\n-\tensure_config_read();\n \tgit_config(ls_refs_config, NULL);\n \n \twhile (packet_reader_read(request) == PACKET_READ_NORMAL) {\n@@ -179,7 +166,6 @@ int ls_refs(struct repository *r, struct strvec *keys,\n int ls_refs_advertise(struct repository *r, struct strbuf *value)\n {\n \tif (value) {\n-\t\tensure_config_read();\n \t\tif (advertise_unborn)\n \t\t\tstrbuf_addstr(value, \"unborn\");\n \t}\ndiff --git a/ls-refs.h b/ls-refs.h\nindex a99e4be0bdd..5fcab0d1dca 100644\n--- a/ls-refs.h\n+++ b/ls-refs.h\n@@ -6,6 +6,7 @@ struct strvec;\n struct packet_reader;\n int ls_refs(struct repository *r, struct strvec *keys,\n \t    struct packet_reader *request);\n+int ls_refs_startup_config(const char *var, const char *value, void *data);\n int ls_refs_advertise(struct repository *r, struct strbuf *value);\n \n #endif /* LS_REFS_H */\ndiff --git a/serve.c b/serve.c\nindex 85cd3eab26e..3c7c9329a14 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -33,6 +33,13 @@ static int object_format_advertise(struct repository *r,\n \treturn 1;\n }\n \n+static int session_id_startup_config(const char *var, const char *value, void *data)\n+{\n+\tif (!strcmp(var, \"transfer.advertisesid\"))\n+\t\tadvertise_sid = git_config_bool(var, value);\n+\treturn 0;\n+}\n+\n static int session_id_advertise(struct repository *r, struct strbuf *value)\n {\n \tif (!advertise_sid)\n@@ -54,6 +61,24 @@ struct protocol_capability {\n \t */\n \tconst char *name;\n \n+\t/*\n+\t * A git_config() callback that'll be called only once for the\n+\t * lifetime of the process, possibly over many different\n+\t * requests. Used for reading config that's expected to be\n+\t * static.\n+\t *\n+\t * The \"command\" or \"advertise\" callbacks themselves are\n+\t * expected to read config that needs to be more current than\n+\t * that, or which is dependent on request data.\n+\t */\n+\tint (*startup_config)(const char *var, const char *value, void *data);\n+\n+\t/*\n+\t * A boolean to check if we've called our \"startup_config\"\n+\t * callback.\n+\t */\n+\tint have_startup_config;\n+\n \t/*\n \t * Function queried to see if a capability should be advertised.\n \t * Optionally a value can be specified by adding it to 'value'.\n@@ -81,6 +106,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"ls-refs\",\n+\t\t.startup_config = ls_refs_startup_config,\n \t\t.advertise = ls_refs_advertise,\n \t\t.command = ls_refs,\n \t},\n@@ -99,6 +125,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"session-id\",\n+\t\t.startup_config = session_id_startup_config,\n \t\t.advertise = session_id_advertise,\n \t},\n \t{\n@@ -108,9 +135,20 @@ static struct protocol_capability capabilities[] = {\n \t},\n };\n \n+static void read_startup_config(struct protocol_capability *command)\n+{\n+\tif (!command->startup_config)\n+\t\treturn;\n+\tif (command->have_startup_config++)\n+\t\treturn;\n+\tgit_config(command->startup_config, NULL);\n+}\n+\n static int call_advertise(struct protocol_capability *command,\n \t\t\t  struct repository *r, struct strbuf *value)\n {\n+\tread_startup_config(command);\n+\n \treturn command->advertise(r, value);\n }\n \n@@ -118,6 +156,9 @@ static int call_command(struct protocol_capability *command,\n \t\t\tstruct repository *r, struct strvec *keys,\n \t\t\tstruct packet_reader *request)\n {\n+\n+\tread_startup_config(command);\n+\n \treturn command->command(r, keys, request);\n }\n \n@@ -318,8 +359,6 @@ static int process_request(void)\n /* Main serve loop for protocol version 2 */\n void serve(struct serve_options *options)\n {\n-\tgit_config_get_bool(\"transfer.advertisesid\", &advertise_sid);\n-\n \tif (options->advertise_capabilities || !options->stateless_rpc) {\n \t\t/* serve by default supports v2 */\n \t\tpacket_write_fmt(1, \"version 2\\n\");\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430840","messageId":"patch-07.12-b7928ddbe9b-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 07/12] serve.c: move version line to advertise_capabilities()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:08Z","receivedAt":"2021-07-21T23:40:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The advertise_capabilities() is only called from serve() and we always\nemit this version line before it, it makes more sense to consider the\ncapabilities part of a \"header\" that has the version, so let's move\nthe writing of the version there.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 3c7c9329a14..10209ab237d 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -168,6 +168,9 @@ static void advertise_capabilities(void)\n \tstruct strbuf value = STRBUF_INIT;\n \tint i;\n \n+\t/* serve by default supports v2 */\n+\tpacket_write_fmt(1, \"version 2\\n\");\n+\n \tfor (i = 0; i < ARRAY_SIZE(capabilities); i++) {\n \t\tstruct protocol_capability *c = &capabilities[i];\n \n@@ -360,9 +363,6 @@ static int process_request(void)\n void serve(struct serve_options *options)\n {\n \tif (options->advertise_capabilities || !options->stateless_rpc) {\n-\t\t/* serve by default supports v2 */\n-\t\tpacket_write_fmt(1, \"version 2\\n\");\n-\n \t\tadvertise_capabilities();\n \t\t/*\n \t\t * If only the list of capabilities was requested exit\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430841","messageId":"patch-08.12-fb80f152713-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 08/12] serve.[ch]: remove \"serve_options\", split up --advertise-refs code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:09Z","receivedAt":"2021-07-21T23:40:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"advertise capabilities\" mode of serve.c added in\ned10cb952d3 (serve: introduce git-serve, 2018-03-15) is only used by\nthe http-backend.c to call {upload,receive}-pack with the\n--advertise-refs parameter. See 42526b478e3 (Add stateless RPC options\nto upload-pack, receive-pack, 2009-10-30).\n\nLet's just make cmd_upload_pack() take the two (v2) or three (v2)\nparameters the the v2/v1 servicing functions need directly, and pass\nthose in via the function signature. The logic of whether daemon mode\nis implied by the timeout belongs in the v1 function (only used\nthere).\n\nOnce we split up the \"advertise v2 refs\" from \"serve v2 request\" it\nbecomes clear that v2 never cared about those in combination. The only\ntime it mattered was for v1 to emit its ref advertisement, in that\ncase we wanted to emit the smart-http-only \"no-done\" capability.\n\nSince we only do that in the --advertise-refs codepath let's just have\nit set \"do_done\" itself in v1's upload_pack() just before send_ref(),\nat that point --advertise-refs and --stateless-rpc in combination are\nredundant (the only user is get_info_refs() in http-backend.c), so we\ncan just pass in --advertise-refs only.\n\nSince we need to touch all the serve() and advertise_capabilities()\ncodepaths let's rename them to less clever and obvious names, it's\nbeen suggested numerous times, the latest of which is [1]'s suggestion\nfor protocol_v2_serve_loop(). Let's go with that.\n\n1. https://lore.kernel.org/git/CAFQ2z_NyGb8rju5CKzmo6KhZXD0Dp21u-BbyCb2aNxLEoSPRJw@mail.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/upload-pack.c    | 25 ++++++++++++-------------\n http-backend.c           |  2 +-\n serve.c                  | 18 +++++-------------\n serve.h                  |  8 ++------\n t/helper/test-serve-v2.c | 14 +++++++++-----\n upload-pack.c            | 18 +++++++++++-------\n upload-pack.h            | 10 ++--------\n 7 files changed, 42 insertions(+), 53 deletions(-)\n\ndiff --git a/builtin/upload-pack.c b/builtin/upload-pack.c\nindex 6da8fa2607c..8506030a648 100644\n--- a/builtin/upload-pack.c\n+++ b/builtin/upload-pack.c\n@@ -16,16 +16,17 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n {\n \tconst char *dir;\n \tint strict = 0;\n-\tstruct upload_pack_options opts = { 0 };\n-\tstruct serve_options serve_opts = SERVE_OPTIONS_INIT;\n+\tint advertise_refs = 0;\n+\tint stateless_rpc = 0;\n+\tint timeout = 0;\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"stateless-rpc\", &opts.stateless_rpc,\n+\t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n \t\t\t N_(\"quit after a single request/response exchange\")),\n-\t\tOPT_BOOL(0, \"advertise-refs\", &opts.advertise_refs,\n+\t\tOPT_BOOL(0, \"advertise-refs\", &advertise_refs,\n \t\t\t N_(\"exit immediately after initial ref advertisement\")),\n \t\tOPT_BOOL(0, \"strict\", &strict,\n \t\t\t N_(\"do not try <directory>/.git/ if <directory> is no Git directory\")),\n-\t\tOPT_INTEGER(0, \"timeout\", &opts.timeout,\n+\t\tOPT_INTEGER(0, \"timeout\", &timeout,\n \t\t\t    N_(\"interrupt transfer after <n> seconds of inactivity\")),\n \t\tOPT_END()\n \t};\n@@ -38,9 +39,6 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage_with_options(upload_pack_usage, options);\n \n-\tif (opts.timeout)\n-\t\topts.daemon_mode = 1;\n-\n \tsetup_path();\n \n \tdir = argv[0];\n@@ -50,21 +48,22 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n \n \tswitch (determine_protocol_version_server()) {\n \tcase protocol_v2:\n-\t\tserve_opts.advertise_capabilities = opts.advertise_refs;\n-\t\tserve_opts.stateless_rpc = opts.stateless_rpc;\n-\t\tserve(&serve_opts);\n+\t\tif (advertise_refs)\n+\t\t\tprotocol_v2_advertise_capabilities();\n+\t\telse\n+\t\t\tprotocol_v2_serve_loop(stateless_rpc);\n \t\tbreak;\n \tcase protocol_v1:\n \t\t/*\n \t\t * v1 is just the original protocol with a version string,\n \t\t * so just fall through after writing the version string.\n \t\t */\n-\t\tif (opts.advertise_refs || !opts.stateless_rpc)\n+\t\tif (advertise_refs || !stateless_rpc)\n \t\t\tpacket_write_fmt(1, \"version 1\\n\");\n \n \t\t/* fallthrough */\n \tcase protocol_v0:\n-\t\tupload_pack(&opts);\n+\t\tupload_pack(advertise_refs, stateless_rpc, timeout);\n \t\tbreak;\n \tcase protocol_unknown_version:\n \t\tBUG(\"unknown protocol version\");\ndiff --git a/http-backend.c b/http-backend.c\nindex b329bf63f09..d37463cec8b 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -534,7 +534,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg)\n \n \tif (service_name) {\n \t\tconst char *argv[] = {NULL /* service name */,\n-\t\t\t\"--stateless-rpc\", \"--advertise-refs\",\n+\t\t\t\"--advertise-refs\",\n \t\t\t\".\", NULL};\n \t\tstruct rpc_service *svc = select_service(hdr, service_name);\n \ndiff --git a/serve.c b/serve.c\nindex 10209ab237d..cacb0bc4e0e 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -162,7 +162,7 @@ static int call_command(struct protocol_capability *command,\n \treturn command->command(r, keys, request);\n }\n \n-static void advertise_capabilities(void)\n+void protocol_v2_advertise_capabilities(void)\n {\n \tstruct strbuf capability = STRBUF_INIT;\n \tstruct strbuf value = STRBUF_INIT;\n@@ -359,24 +359,16 @@ static int process_request(void)\n \treturn 0;\n }\n \n-/* Main serve loop for protocol version 2 */\n-void serve(struct serve_options *options)\n+void protocol_v2_serve_loop(int stateless_rpc)\n {\n-\tif (options->advertise_capabilities || !options->stateless_rpc) {\n-\t\tadvertise_capabilities();\n-\t\t/*\n-\t\t * If only the list of capabilities was requested exit\n-\t\t * immediately after advertising capabilities\n-\t\t */\n-\t\tif (options->advertise_capabilities)\n-\t\t\treturn;\n-\t}\n+\tif (!stateless_rpc)\n+\t\tprotocol_v2_advertise_capabilities();\n \n \t/*\n \t * If stateless-rpc was requested then exit after\n \t * a single request/response exchange\n \t */\n-\tif (options->stateless_rpc) {\n+\tif (stateless_rpc) {\n \t\tprocess_request();\n \t} else {\n \t\tfor (;;)\ndiff --git a/serve.h b/serve.h\nindex 56da77a87af..f946cf904a2 100644\n--- a/serve.h\n+++ b/serve.h\n@@ -1,11 +1,7 @@\n #ifndef SERVE_H\n #define SERVE_H\n \n-struct serve_options {\n-\tunsigned advertise_capabilities;\n-\tunsigned stateless_rpc;\n-};\n-#define SERVE_OPTIONS_INIT { 0 }\n-void serve(struct serve_options *options);\n+void protocol_v2_advertise_capabilities(void);\n+void protocol_v2_serve_loop(int stateless_rpc);\n \n #endif /* SERVE_H */\ndiff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c\nindex aee35e5aef4..28e905afc36 100644\n--- a/t/helper/test-serve-v2.c\n+++ b/t/helper/test-serve-v2.c\n@@ -10,12 +10,12 @@ static char const * const serve_usage[] = {\n \n int cmd__serve_v2(int argc, const char **argv)\n {\n-\tstruct serve_options opts = SERVE_OPTIONS_INIT;\n-\n+\tint stateless_rpc = 0;\n+\tint advertise_capabilities = 0;\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"stateless-rpc\", &opts.stateless_rpc,\n+\t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n \t\t\t N_(\"quit after a single request/response exchange\")),\n-\t\tOPT_BOOL(0, \"advertise-capabilities\", &opts.advertise_capabilities,\n+\t\tOPT_BOOL(0, \"advertise-capabilities\", &advertise_capabilities,\n \t\t\t N_(\"exit immediately after advertising capabilities\")),\n \t\tOPT_END()\n \t};\n@@ -25,7 +25,11 @@ int cmd__serve_v2(int argc, const char **argv)\n \targc = parse_options(argc, argv, prefix, options, serve_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH |\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n-\tserve(&opts);\n+\n+\tif (advertise_capabilities)\n+\t\tprotocol_v2_advertise_capabilities();\n+\telse\n+\t\tprotocol_v2_serve_loop(stateless_rpc);\n \n \treturn 0;\n }\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb43..48e8bf5694c 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1214,7 +1214,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t\t\t     \" allow-tip-sha1-in-want\" : \"\",\n \t\t\t     (data->allow_uor & ALLOW_REACHABLE_SHA1) ?\n \t\t\t\t     \" allow-reachable-sha1-in-want\" : \"\",\n-\t\t\t     data->stateless_rpc ? \" no-done\" : \"\",\n+\t\t\t     data->no_done ? \" no-done\" : \"\",\n \t\t\t     symref_info.buf,\n \t\t\t     data->allow_filter ? \" filter\" : \"\",\n \t\t\t     session_id.buf,\n@@ -1329,7 +1329,8 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)\n \treturn parse_hide_refs_config(var, value, \"uploadpack\");\n }\n \n-void upload_pack(struct upload_pack_options *options)\n+void upload_pack(const int advertise_refs, const int stateless_rpc,\n+\t\t const int timeout)\n {\n \tstruct packet_reader reader;\n \tstruct upload_pack_data data;\n@@ -1338,14 +1339,17 @@ void upload_pack(struct upload_pack_options *options)\n \n \tgit_config(upload_pack_config, &data);\n \n-\tdata.stateless_rpc = options->stateless_rpc;\n-\tdata.daemon_mode = options->daemon_mode;\n-\tdata.timeout = options->timeout;\n+\tdata.stateless_rpc = stateless_rpc;\n+\tdata.timeout = timeout;\n+\tif (data.timeout)\n+\t\tdata.daemon_mode = 1;\n \n \thead_ref_namespaced(find_symref, &data.symref);\n \n-\tif (options->advertise_refs || !data.stateless_rpc) {\n+\tif (advertise_refs || !data.stateless_rpc) {\n \t\treset_timeout(data.timeout);\n+\t\tif (advertise_refs)\n+\t\t\tdata.no_done = 1;\n \t\thead_ref_namespaced(send_ref, &data);\n \t\tfor_each_namespaced_ref(send_ref, &data);\n \t\tadvertise_shallow_grafts(1);\n@@ -1355,7 +1359,7 @@ void upload_pack(struct upload_pack_options *options)\n \t\tfor_each_namespaced_ref(check_ref, NULL);\n \t}\n \n-\tif (!options->advertise_refs) {\n+\tif (!advertise_refs) {\n \t\tpacket_reader_init(&reader, 0, NULL, 0,\n \t\t\t\t   PACKET_READ_CHOMP_NEWLINE |\n \t\t\t\t   PACKET_READ_DIE_ON_ERR_PACKET);\ndiff --git a/upload-pack.h b/upload-pack.h\nindex 27ddcdc6cb0..a06498bbea7 100644\n--- a/upload-pack.h\n+++ b/upload-pack.h\n@@ -1,14 +1,8 @@\n #ifndef UPLOAD_PACK_H\n #define UPLOAD_PACK_H\n \n-struct upload_pack_options {\n-\tint stateless_rpc;\n-\tint advertise_refs;\n-\tunsigned int timeout;\n-\tint daemon_mode;\n-};\n-\n-void upload_pack(struct upload_pack_options *options);\n+void upload_pack(const int advertise_refs, const int stateless_rpc,\n+\t\t const int timeout);\n \n struct repository;\n struct strvec;\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430843","messageId":"patch-10.12-c6870b5f18a-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 10/12] upload-pack: document and rename --advertise-refs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:11Z","receivedAt":"2021-07-21T23:40:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The --advertise-refs documentation in git-upload-pack added in\n9812f2136b3 (upload-pack.c: use parse-options API, 2016-05-31) hasn't\nbeen entirely true ever since v2 support was implemented in\ne52449b6722 (connect: request remote refs using v2, 2018-03-15). Under\nv2 we don't advertise the refs at all, but rather dump the\ncapabilities header.\n\nThis option has always been an obscure internal implementation detail,\nit wasn't even documented for git-receive-pack. Since it has exactly\none user let's rename it to --http-backend-info-refs, which is more\naccurate and points the reader in the right direction. Let's also\ncross-link this from the protocol v1 and v2 documentation.\n\nI'm retaining a hidden --advertise-refs alias in case there's any\nexternal users of this, and making both options hidden to the bash\ncompletion (as with most other internal-only options).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-receive-pack.txt        |  5 +++++\n Documentation/git-upload-pack.txt         | 12 ++++++++----\n Documentation/technical/http-protocol.txt |  3 +++\n Documentation/technical/protocol-v2.txt   |  3 +++\n builtin/receive-pack.c                    |  3 ++-\n builtin/upload-pack.c                     |  5 +++--\n http-backend.c                            |  2 +-\n t/t5555-http-smart-common.sh              | 14 ++++++++++++++\n 8 files changed, 39 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt\nindex 25702ed7307..014a78409b9 100644\n--- a/Documentation/git-receive-pack.txt\n+++ b/Documentation/git-receive-pack.txt\n@@ -41,6 +41,11 @@ OPTIONS\n <directory>::\n \tThe repository to sync into.\n \n+--http-backend-info-refs::\n+\tUsed by linkgit:git-http-backend[1] to serve up\n+\t`$GIT_URL/info/refs?service=git-receive-pack` requests. See\n+\t`--http-backend-info-refs` in linkgit:git-upload-pack[1].\n+\n PRE-RECEIVE HOOK\n ----------------\n Before any ref is updated, if $GIT_DIR/hooks/pre-receive file exists\ndiff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt\nindex 9822c1eb1ad..739416ec83d 100644\n--- a/Documentation/git-upload-pack.txt\n+++ b/Documentation/git-upload-pack.txt\n@@ -36,10 +36,14 @@ OPTIONS\n \tThis fits with the HTTP POST request processing model where\n \ta program may read the request, write a response, and must exit.\n \n---advertise-refs::\n-\tOnly the initial ref advertisement is output, and the program exits\n-\timmediately. This fits with the HTTP GET request model, where\n-\tno request content is received but a response must be produced.\n+--http-backend-info-refs::\n+\tUsed by linkgit:git-http-backend[1] to serve up\n+\t`$GIT_URL/info/refs?service=git-upload-pack` requests. See\n+\t\"Smart Clients\" in link:technical/http-protocol.html[the HTTP\n+\ttransfer protocols] documentation and \"HTTP Transport\" in\n+\tlink:technical/protocol-v2.html[the Git Wire Protocol, Version\n+\t2] documentation. Also understood by\n+\tlinkgit:git-receive-pack[1].\n \n <directory>::\n \tThe repository to sync from.\ndiff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt\nindex 96d89ea9b22..cc5126cfeda 100644\n--- a/Documentation/technical/http-protocol.txt\n+++ b/Documentation/technical/http-protocol.txt\n@@ -225,6 +225,9 @@ The client may send Extra Parameters (see\n Documentation/technical/pack-protocol.txt) as a colon-separated string\n in the Git-Protocol HTTP header.\n \n+Uses the `--http-backend-info-refs` option to\n+linkgit:git-upload-pack[1].\n+\n Dumb Server Response\n ^^^^^^^^^^^^^^^^^^^^\n Dumb servers MUST respond with the dumb server reply format.\ndiff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt\nindex 1040d853198..213538f1d0e 100644\n--- a/Documentation/technical/protocol-v2.txt\n+++ b/Documentation/technical/protocol-v2.txt\n@@ -81,6 +81,9 @@ A v2 server would reply:\n Subsequent requests are then made directly to the service\n `$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack).\n \n+Uses the `--http-backend-info-refs` option to\n+linkgit:git-upload-pack[1].\n+\n Capability Advertisement\n ------------------------\n \ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex a34742513ac..b12820dbd6c 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -2478,7 +2478,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"quiet\")),\n \t\tOPT_HIDDEN_BOOL(0, \"stateless-rpc\", &stateless_rpc, NULL),\n-\t\tOPT_HIDDEN_BOOL(0, \"advertise-refs\", &advertise_refs, NULL),\n+\t\tOPT_HIDDEN_BOOL(0, \"http-backend-info-refs\", &advertise_refs, NULL),\n+\t\tOPT_ALIAS(0, \"advertise-refs\", \"http-backend-info-refs\"),\n \t\tOPT_HIDDEN_BOOL(0, \"reject-thin-pack-for-testing\", &reject_thin, NULL),\n \t\tOPT_END()\n \t};\ndiff --git a/builtin/upload-pack.c b/builtin/upload-pack.c\nindex 8506030a648..125af53885f 100644\n--- a/builtin/upload-pack.c\n+++ b/builtin/upload-pack.c\n@@ -22,8 +22,9 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n \t\t\t N_(\"quit after a single request/response exchange\")),\n-\t\tOPT_BOOL(0, \"advertise-refs\", &advertise_refs,\n-\t\t\t N_(\"exit immediately after initial ref advertisement\")),\n+\t\tOPT_HIDDEN_BOOL(0, \"http-backend-info-refs\", &advertise_refs,\n+\t\t\t\tN_(\"serve up the info/refs for git-http-backend\")),\n+\t\tOPT_ALIAS(0, \"advertise-refs\", \"http-backend-info-refs\"),\n \t\tOPT_BOOL(0, \"strict\", &strict,\n \t\t\t N_(\"do not try <directory>/.git/ if <directory> is no Git directory\")),\n \t\tOPT_INTEGER(0, \"timeout\", &timeout,\ndiff --git a/http-backend.c b/http-backend.c\nindex d37463cec8b..838374edb91 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -534,7 +534,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg)\n \n \tif (service_name) {\n \t\tconst char *argv[] = {NULL /* service name */,\n-\t\t\t\"--advertise-refs\",\n+\t\t\t\"--http-backend-info-refs\",\n \t\t\t\".\", NULL};\n \t\tstruct rpc_service *svc = select_service(hdr, service_name);\n \ndiff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh\nindex 389ee96987b..8d9d6a556f0 100755\n--- a/t/t5555-http-smart-common.sh\n+++ b/t/t5555-http-smart-common.sh\n@@ -6,6 +6,20 @@ test_expect_success 'setup' '\n \ttest_commit --no-tag initial\n '\n \n+test_expect_success 'git upload-pack --http-backend-info-refs and --advertise-refs are aliased' '\n+\tgit upload-pack --http-backend-info-refs . >expected 2>err.expected &&\n+\tgit upload-pack --advertise-refs . >actual 2>err.actual &&\n+\ttest_cmp err.expected err.actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git receive-pack --http-backend-info-refs and --advertise-refs are aliased' '\n+\tgit receive-pack --http-backend-info-refs . >expected 2>err.expected &&\n+\tgit receive-pack --advertise-refs . >actual 2>err.actual &&\n+\ttest_cmp err.expected err.actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'git upload-pack --advertise-refs' '\n \tcat >expect <<-EOF &&\n \t$(git rev-parse HEAD) HEAD\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430842","messageId":"patch-09.12-beafe9811c1-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 09/12] {upload,receive}-pack tests: add --advertise-refs tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:10Z","receivedAt":"2021-07-21T23:40:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The --advertise-refs option had no explicit tests of its own, only\nother http tests that would fail at a distance if it it was\nbroken. Let's test its behavior explicitly.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5555-http-smart-common.sh | 145 +++++++++++++++++++++++++++++++++++\n 1 file changed, 145 insertions(+)\n create mode 100755 t/t5555-http-smart-common.sh\n\ndiff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh\nnew file mode 100755\nindex 00000000000..389ee96987b\n--- /dev/null\n+++ b/t/t5555-http-smart-common.sh\n@@ -0,0 +1,145 @@\n+test_description='test functionality common to smart fetch & push'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit --no-tag initial\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs' '\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) HEAD\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\t# We only care about GIT_PROTOCOL, not GIT_TEST_PROTOCOL_VERSION\n+\tsane_unset GIT_PROTOCOL &&\n+\tGIT_TEST_PROTOCOL_VERSION=2 \\\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect &&\n+\n+\t# The --advertise-refs alias works\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs: v0' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) HEAD\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect &&\n+\n+\t# With explicit v0\n+\tGIT_PROTOCOL=version=0 \\\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+\n+'\n+\n+test_expect_success 'git receive-pack --advertise-refs: v0' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tgit receive-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect &&\n+\n+\t# With explicit v0\n+\tGIT_PROTOCOL=version=0 \\\n+\tgit receive-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs: v1' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\tversion 1\n+\t$(git rev-parse HEAD) HEAD\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=1 \\\n+\tgit upload-pack --advertise-refs . >out &&\n+\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git receive-pack --advertise-refs: v1' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\tversion 1\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=1 \\\n+\tgit receive-pack --advertise-refs . >out &&\n+\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs: v2' '\n+\tcat >expect <<-EOF &&\n+\tversion 2\n+\tagent=FAKE\n+\tls-refs=unborn\n+\tfetch=shallow wait-for-done\n+\tserver-option\n+\tobject-format=$(test_oid algo)\n+\tobject-info\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=2 \\\n+\tGIT_USER_AGENT=FAKE \\\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git receive-pack --advertise-refs: v2' '\n+\t# There is no v2 yet for receive-pack, implicit v0\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=2 \\\n+\tgit receive-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_done\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430845","messageId":"patch-11.12-2d4c3d0d463-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 11/12] upload-pack.c: convert to new serve.c \"startup\" config cb","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:12Z","receivedAt":"2021-07-21T23:40:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Convert the config reading code in upload-pack.c to use the new\n\"startup_config\" callback when we're using the v2 protocol, and\nlightly fake up the same when we're using v0 and v1.\n\nBefore the series that fixed 08450ef7918 (upload-pack: clear\nfilter_options for each v2 fetch command, 2020-05-08) landed, most of\nwhat's now \"struct upload_pack_data\" used to be static variables at\nthe top of this file.\n\nThis moves some of them back. See f203a88cf14 (upload-pack: move\nkeepalive to upload_pack_data, 2020-06-04), f1514c6aad0 (upload-pack:\nmove allow_unadvertised_object_request to upload_pack_data,\n2020-06-11) etc.\n\nI think this makes it easier to understand the this code, as we're now\nclearly separating data and config that changes on a\nrequest-to-request basis (see 08450ef7918), and the sort of config\nthat serve.c's \"startup_config\" is aimed at.\n\nThus it's clear that the \"allow_uor\" passed to functions like\n\"is_our_ref()\" is constant after startup (usually it'll never change\nfor a given server's configuration, or change once).\n\nThis requires a very light compatibility layer with the serve.c\ncallback mechanism in the form of \"upload_pack\" for the v0 and v1\nprotocols.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c       |   1 +\n upload-pack.c | 133 +++++++++++++++++++++++++++++---------------------\n upload-pack.h |   2 +\n 3 files changed, 80 insertions(+), 56 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex cacb0bc4e0e..162e8cea550 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -112,6 +112,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n \t{\n \t\t.name = \"fetch\",\n+\t\t.startup_config = serve_upload_pack_startup_config,\n \t\t.advertise = upload_pack_advertise,\n \t\t.command = upload_pack_v2,\n \t},\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 48e8bf5694c..b9cfc09795b 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -52,6 +52,19 @@ enum allow_uor {\n \tALLOW_ANY_SHA1 = 0x07\n };\n \n+/*\n+ * \"Global\" configuration that won't be affected by the type of\n+ * request we're doing, or by other request data in \"struct\n+ * upload_pack_data\" below.\n+ */\n+static int v1_have_startup_config;\n+static int config_keepalive_secs = 5;\n+static enum allow_uor config_allow_uor;\n+static unsigned config_advertise_sid;\n+static const char *config_pack_objects_hook;\n+static unsigned config_v2_allow_ref_in_want;\n+static unsigned config_v2_allow_sideband_all;\n+\n /*\n  * Please annotate, and if possible group together, fields used only\n  * for protocol v0 or only for protocol v2.\n@@ -70,7 +83,6 @@ struct upload_pack_data {\n \ttimestamp_t deepen_since;\n \tint deepen_rev_list;\n \tint deepen_relative;\n-\tint keepalive;\n \tint shallow_nr;\n \ttimestamp_t oldest_have;\n \n@@ -85,15 +97,12 @@ struct upload_pack_data {\n \tint use_sideband;\n \n \tstruct string_list uri_protocols;\n-\tenum allow_uor allow_uor;\n \n \tstruct list_objects_filter_options filter_options;\n \tstruct string_list allowed_filters;\n \n \tstruct packet_writer writer;\n \n-\tconst char *pack_objects_hook;\n-\n \tunsigned stateless_rpc : 1;\t\t\t\t/* v0 only */\n \tunsigned no_done : 1;\t\t\t\t\t/* v0 only */\n \tunsigned daemon_mode : 1;\t\t\t\t/* v0 only */\n@@ -109,9 +118,6 @@ struct upload_pack_data {\n \tunsigned long tree_filter_max_depth;\n \n \tunsigned done : 1;\t\t\t\t\t/* v2 only */\n-\tunsigned allow_ref_in_want : 1;\t\t\t\t/* v2 only */\n-\tunsigned allow_sideband_all : 1;\t\t\t/* v2 only */\n-\tunsigned advertise_sid : 1;\n };\n \n static void upload_pack_data_init(struct upload_pack_data *data)\n@@ -141,9 +147,6 @@ static void upload_pack_data_init(struct upload_pack_data *data)\n \tdata->allow_filter_fallback = 1;\n \tdata->tree_filter_max_depth = ULONG_MAX;\n \tpacket_writer_init(&data->writer, 1);\n-\n-\tdata->keepalive = 5;\n-\tdata->advertise_sid = 0;\n }\n \n static void upload_pack_data_clear(struct upload_pack_data *data)\n@@ -158,8 +161,6 @@ static void upload_pack_data_clear(struct upload_pack_data *data)\n \tobject_array_clear(&data->extra_edge_obj);\n \tlist_objects_filter_release(&data->filter_options);\n \tstring_list_clear(&data->allowed_filters, 0);\n-\n-\tfree((char *)data->pack_objects_hook);\n }\n \n static void reset_timeout(unsigned int timeout)\n@@ -277,10 +278,10 @@ static void create_pack_file(struct upload_pack_data *pack_data,\n \tint i;\n \tFILE *pipe_fd;\n \n-\tif (!pack_data->pack_objects_hook)\n+\tif (!config_pack_objects_hook)\n \t\tpack_objects.git_cmd = 1;\n \telse {\n-\t\tstrvec_push(&pack_objects.args, pack_data->pack_objects_hook);\n+\t\tstrvec_push(&pack_objects.args, config_pack_objects_hook);\n \t\tstrvec_push(&pack_objects.args, \"git\");\n \t\tpack_objects.use_shell = 1;\n \t}\n@@ -371,9 +372,9 @@ static void create_pack_file(struct upload_pack_data *pack_data,\n \t\tif (!pollsize)\n \t\t\tbreak;\n \n-\t\tpolltimeout = pack_data->keepalive < 0\n+\t\tpolltimeout = config_keepalive_secs < 0\n \t\t\t? -1\n-\t\t\t: 1000 * pack_data->keepalive;\n+\t\t\t: 1000 * config_keepalive_secs;\n \n \t\tret = poll(pfd, pollsize, polltimeout);\n \n@@ -581,9 +582,9 @@ static int get_common_commits(struct upload_pack_data *data,\n \t}\n }\n \n-static int is_our_ref(struct object *o, enum allow_uor allow_uor)\n+static int is_our_ref(struct object *o)\n {\n-\tint allow_hidden_ref = (allow_uor &\n+\tint allow_hidden_ref = (config_allow_uor &\n \t\t\t\t(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n@@ -593,8 +594,7 @@ static int is_our_ref(struct object *o, enum allow_uor allow_uor)\n  */\n static int do_reachable_revlist(struct child_process *cmd,\n \t\t\t\tstruct object_array *src,\n-\t\t\t\tstruct object_array *reachable,\n-\t\t\t\tenum allow_uor allow_uor)\n+\t\t\t\tstruct object_array *reachable)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n@@ -627,14 +627,14 @@ static int do_reachable_revlist(struct child_process *cmd,\n \t\t\tcontinue;\n \t\tif (reachable && o->type == OBJ_COMMIT)\n \t\t\to->flags &= ~TMP_MARK;\n-\t\tif (!is_our_ref(o, allow_uor))\n+\t\tif (!is_our_ref(o))\n \t\t\tcontinue;\n \t\tif (fprintf(cmd_in, \"^%s\\n\", oid_to_hex(&o->oid)) < 0)\n \t\t\tgoto error;\n \t}\n \tfor (i = 0; i < src->nr; i++) {\n \t\to = src->objects[i].item;\n-\t\tif (is_our_ref(o, allow_uor)) {\n+\t\tif (is_our_ref(o)) {\n \t\t\tif (reachable)\n \t\t\t\tadd_object_array(o, NULL, reachable);\n \t\t\tcontinue;\n@@ -671,8 +671,7 @@ static int get_reachable_list(struct upload_pack_data *data,\n \tchar namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */\n \tconst unsigned hexsz = the_hash_algo->hexsz;\n \n-\tif (do_reachable_revlist(&cmd, &data->shallows, reachable,\n-\t\t\t\t data->allow_uor) < 0)\n+\tif (do_reachable_revlist(&cmd, &data->shallows, reachable) < 0)\n \t\treturn -1;\n \n \twhile ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) {\n@@ -703,13 +702,13 @@ static int get_reachable_list(struct upload_pack_data *data,\n \treturn 0;\n }\n \n-static int has_unreachable(struct object_array *src, enum allow_uor allow_uor)\n+static int has_unreachable(struct object_array *src)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tchar buf[1];\n \tint i;\n \n-\tif (do_reachable_revlist(&cmd, src, NULL, allow_uor) < 0)\n+\tif (do_reachable_revlist(&cmd, src, NULL) < 0)\n \t\treturn 1;\n \n \t/*\n@@ -748,9 +747,9 @@ static void check_non_tip(struct upload_pack_data *data)\n \t * uploadpack.allowReachableSHA1InWant,\n \t * non-tip requests can never happen.\n \t */\n-\tif (!data->stateless_rpc && !(data->allow_uor & ALLOW_REACHABLE_SHA1))\n+\tif (!data->stateless_rpc && !(config_allow_uor & ALLOW_REACHABLE_SHA1))\n \t\tgoto error;\n-\tif (!has_unreachable(&data->want_obj, data->allow_uor))\n+\tif (!has_unreachable(&data->want_obj))\n \t\t/* All the non-tip ones are ancestors of what we advertised */\n \t\treturn;\n \n@@ -758,7 +757,7 @@ static void check_non_tip(struct upload_pack_data *data)\n \t/* Pick one of them (we know there at least is one) */\n \tfor (i = 0; i < data->want_obj.nr; i++) {\n \t\tstruct object *o = data->want_obj.objects[i].item;\n-\t\tif (!is_our_ref(o, data->allow_uor)) {\n+\t\tif (!is_our_ref(o)) {\n \t\t\tpacket_writer_error(&data->writer,\n \t\t\t\t\t    \"upload-pack: not our ref %s\",\n \t\t\t\t\t    oid_to_hex(&o->oid));\n@@ -1123,8 +1122,8 @@ static void receive_needs(struct upload_pack_data *data,\n \t\t}\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n-\t\t\tif (!((data->allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1\n-\t\t\t      || is_our_ref(o, data->allow_uor)))\n+\t\t\tif (!((config_allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1\n+\t\t\t      || is_our_ref(o)))\n \t\t\t\thas_non_tip = 1;\n \t\t\tadd_object_array(o, NULL, &data->want_obj);\n \t\t}\n@@ -1184,7 +1183,7 @@ static void format_symref_info(struct strbuf *buf, struct string_list *symref)\n }\n \n static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {\n-\tif (d->advertise_sid)\n+\tif (config_advertise_sid)\n \t\tstrbuf_addf(buf, \" session-id=%s\", trace2_session_id());\n }\n \n@@ -1210,9 +1209,9 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\tpacket_write_fmt(1, \"%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\\n\",\n \t\t\t     oid_to_hex(oid), refname_nons,\n \t\t\t     0, capabilities,\n-\t\t\t     (data->allow_uor & ALLOW_TIP_SHA1) ?\n+\t\t\t     (config_allow_uor & ALLOW_TIP_SHA1) ?\n \t\t\t\t     \" allow-tip-sha1-in-want\" : \"\",\n-\t\t\t     (data->allow_uor & ALLOW_REACHABLE_SHA1) ?\n+\t\t\t     (config_allow_uor & ALLOW_REACHABLE_SHA1) ?\n \t\t\t\t     \" allow-reachable-sha1-in-want\" : \"\",\n \t\t\t     data->no_done ? \" no-done\" : \"\",\n \t\t\t     symref_info.buf,\n@@ -1282,46 +1281,66 @@ static int parse_object_filter_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int upload_pack_config(const char *var, const char *value, void *cb_data)\n+static int upload_pack_startup_config(const char *var, const char *value,\n+\t\t\t\t      void *cb_data)\n {\n-\tstruct upload_pack_data *data = cb_data;\n-\n \tif (!strcmp(\"uploadpack.allowtipsha1inwant\", var)) {\n \t\tif (git_config_bool(var, value))\n-\t\t\tdata->allow_uor |= ALLOW_TIP_SHA1;\n+\t\t\tconfig_allow_uor |= ALLOW_TIP_SHA1;\n \t\telse\n-\t\t\tdata->allow_uor &= ~ALLOW_TIP_SHA1;\n+\t\t\tconfig_allow_uor &= ~ALLOW_TIP_SHA1;\n \t} else if (!strcmp(\"uploadpack.allowreachablesha1inwant\", var)) {\n \t\tif (git_config_bool(var, value))\n-\t\t\tdata->allow_uor |= ALLOW_REACHABLE_SHA1;\n+\t\t\tconfig_allow_uor |= ALLOW_REACHABLE_SHA1;\n \t\telse\n-\t\t\tdata->allow_uor &= ~ALLOW_REACHABLE_SHA1;\n+\t\t\tconfig_allow_uor &= ~ALLOW_REACHABLE_SHA1;\n \t} else if (!strcmp(\"uploadpack.allowanysha1inwant\", var)) {\n \t\tif (git_config_bool(var, value))\n-\t\t\tdata->allow_uor |= ALLOW_ANY_SHA1;\n+\t\t\tconfig_allow_uor |= ALLOW_ANY_SHA1;\n \t\telse\n-\t\t\tdata->allow_uor &= ~ALLOW_ANY_SHA1;\n+\t\t\tconfig_allow_uor &= ~ALLOW_ANY_SHA1;\n \t} else if (!strcmp(\"uploadpack.keepalive\", var)) {\n-\t\tdata->keepalive = git_config_int(var, value);\n-\t\tif (!data->keepalive)\n-\t\t\tdata->keepalive = -1;\n-\t} else if (!strcmp(\"uploadpack.allowfilter\", var)) {\n-\t\tdata->allow_filter = git_config_bool(var, value);\n-\t} else if (!strcmp(\"uploadpack.allowrefinwant\", var)) {\n-\t\tdata->allow_ref_in_want = git_config_bool(var, value);\n-\t} else if (!strcmp(\"uploadpack.allowsidebandall\", var)) {\n-\t\tdata->allow_sideband_all = git_config_bool(var, value);\n+\t\tconfig_keepalive_secs = git_config_int(var, value);\n+\t\tif (!config_keepalive_secs)\n+\t\t\tconfig_keepalive_secs = -1;\n \t} else if (!strcmp(\"core.precomposeunicode\", var)) {\n \t\tprecomposed_unicode = git_config_bool(var, value);\n \t} else if (!strcmp(\"transfer.advertisesid\", var)) {\n-\t\tdata->advertise_sid = git_config_bool(var, value);\n+\t\tconfig_advertise_sid = git_config_bool(var, value);\n \t}\n \n \tif (current_config_scope() != CONFIG_SCOPE_LOCAL &&\n \t    current_config_scope() != CONFIG_SCOPE_WORKTREE) {\n \t\tif (!strcmp(\"uploadpack.packobjectshook\", var))\n-\t\t\treturn git_config_string(&data->pack_objects_hook, var, value);\n+\t\t\treturn git_config_string(&config_pack_objects_hook, var, value);\n \t}\n+\treturn 0;\n+}\n+\n+static int upload_pack_startup_config_v2_only(const char *var,\n+\t\t\t\t\t      const char *value, void *cb_data)\n+{\n+\tif (!strcmp(\"uploadpack.allowrefinwant\", var))\n+\t\tconfig_v2_allow_ref_in_want = git_config_bool(var, value);\n+\telse if (!strcmp(\"uploadpack.allowsidebandall\", var))\n+\t\tconfig_v2_allow_sideband_all = git_config_bool(var, value);\n+\treturn 0;\n+}\n+\n+int serve_upload_pack_startup_config(const char *var, const char *value, void *data)\n+{\n+\tupload_pack_startup_config(var, value, data);\n+\tupload_pack_startup_config_v2_only(var, value, data);\n+\treturn 0;\n+}\n+\n+static int upload_pack_config(const char *var, const char *value,\n+\t\t\t      void *cb_data)\n+{\n+\tstruct upload_pack_data *data = cb_data;\n+\n+\tif (!strcmp(\"uploadpack.allowfilter\", var))\n+\t\tdata->allow_filter = git_config_bool(var, value);\n \n \tif (parse_object_filter_config(var, value, data) < 0)\n \t\treturn -1;\n@@ -1337,6 +1356,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,\n \n \tupload_pack_data_init(&data);\n \n+\tif (!v1_have_startup_config++)\n+\t\tgit_config(upload_pack_startup_config, NULL);\n \tgit_config(upload_pack_config, &data);\n \n \tdata.stateless_rpc = stateless_rpc;\n@@ -1472,7 +1493,7 @@ static void process_args(struct packet_reader *request,\n \t\t/* process want */\n \t\tif (parse_want(&data->writer, arg, &data->want_obj))\n \t\t\tcontinue;\n-\t\tif (data->allow_ref_in_want &&\n+\t\tif (config_v2_allow_ref_in_want &&\n \t\t    parse_want_ref(&data->writer, arg, &data->wanted_refs,\n \t\t\t\t   &data->want_obj))\n \t\t\tcontinue;\n@@ -1530,7 +1551,7 @@ static void process_args(struct packet_reader *request,\n \t\t}\n \n \t\tif ((git_env_bool(\"GIT_TEST_SIDEBAND_ALL\", 0) ||\n-\t\t     data->allow_sideband_all) &&\n+\t\t     config_v2_allow_sideband_all) &&\n \t\t    !strcmp(arg, \"sideband-all\")) {\n \t\t\tdata->writer.use_sideband = 1;\n \t\t\tcontinue;\ndiff --git a/upload-pack.h b/upload-pack.h\nindex a06498bbea7..52011d9ecff 100644\n--- a/upload-pack.h\n+++ b/upload-pack.h\n@@ -13,5 +13,7 @@ int upload_pack_v2(struct repository *r, struct strvec *keys,\n struct strbuf;\n int upload_pack_advertise(struct repository *r,\n \t\t\t  struct strbuf *value);\n+int serve_upload_pack_startup_config(const char *var, const char *value,\n+\t\t\t\t     void *data);\n \n #endif /* UPLOAD_PACK_H */\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"430844","messageId":"patch-12.12-e4eb31b5b8e-20210721T233307Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v3 12/12] serve.[ch]: don't pass \"struct strvec *keys\" to commands","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-07-21T23:40:13Z","receivedAt":"2021-07-21T23:40:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The serve.c API added in ed10cb952d3 (serve: introduce git-serve,\n2018-03-15) was passing in the raw capabilities \"keys\", but nothing\ndownstream of it ever used them.\n\nLet's remove that code because it's not needed, and because if and\nwhen we need to pass data about the advertisement (I have some WIP\npatches for that), it makes much more sense to have the serve.c parse\nthe capabilities, and then pass specific information we need down than\nexpecting its API users to re-do their own parsing of the raw data.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n ls-refs.c       | 3 +--\n ls-refs.h       | 4 +---\n protocol-caps.c | 3 +--\n protocol-caps.h | 4 +---\n serve.c         | 7 +++----\n upload-pack.c   | 3 +--\n upload-pack.h   | 4 +---\n 7 files changed, 9 insertions(+), 19 deletions(-)\n\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 02fbcfd9bde..787e3849d97 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -127,8 +127,7 @@ static int ls_refs_config(const char *var, const char *value, void *data)\n \treturn parse_hide_refs_config(var, value, \"uploadpack\");\n }\n \n-int ls_refs(struct repository *r, struct strvec *keys,\n-\t    struct packet_reader *request)\n+int ls_refs(struct repository *r, struct packet_reader *request)\n {\n \tstruct ls_refs_data data;\n \ndiff --git a/ls-refs.h b/ls-refs.h\nindex 5fcab0d1dca..32f9e0eddd0 100644\n--- a/ls-refs.h\n+++ b/ls-refs.h\n@@ -2,10 +2,8 @@\n #define LS_REFS_H\n \n struct repository;\n-struct strvec;\n struct packet_reader;\n-int ls_refs(struct repository *r, struct strvec *keys,\n-\t    struct packet_reader *request);\n+int ls_refs(struct repository *r, struct packet_reader *request);\n int ls_refs_startup_config(const char *var, const char *value, void *data);\n int ls_refs_advertise(struct repository *r, struct strbuf *value);\n \ndiff --git a/protocol-caps.c b/protocol-caps.c\nindex 901b6795e42..bbde91810ac 100644\n--- a/protocol-caps.c\n+++ b/protocol-caps.c\n@@ -75,8 +75,7 @@ static void send_info(struct repository *r, struct packet_writer *writer,\n \tstrbuf_release(&send_buffer);\n }\n \n-int cap_object_info(struct repository *r, struct strvec *keys,\n-\t\t    struct packet_reader *request)\n+int cap_object_info(struct repository *r, struct packet_reader *request)\n {\n \tstruct requested_info info;\n \tstruct packet_writer writer;\ndiff --git a/protocol-caps.h b/protocol-caps.h\nindex 0a9f49df115..15c4550360c 100644\n--- a/protocol-caps.h\n+++ b/protocol-caps.h\n@@ -2,9 +2,7 @@\n #define PROTOCOL_CAPS_H\n \n struct repository;\n-struct strvec;\n struct packet_reader;\n-int cap_object_info(struct repository *r, struct strvec *keys,\n-\t\t    struct packet_reader *request);\n+int cap_object_info(struct repository *r, struct packet_reader *request);\n \n #endif /* PROTOCOL_CAPS_H */\ndiff --git a/serve.c b/serve.c\nindex 162e8cea550..3e82178c0f7 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -50,7 +50,7 @@ static int session_id_advertise(struct repository *r, struct strbuf *value)\n }\n \n typedef int (*advertise_fn_t)(struct repository *r, struct strbuf *value);\n-typedef int (*command_fn_t)(struct repository *r, struct strvec *keys,\n+typedef int (*command_fn_t)(struct repository *r,\n \t\t\t    struct packet_reader *request);\n \n struct protocol_capability {\n@@ -89,8 +89,7 @@ struct protocol_capability {\n \n \t/*\n \t * Function called when a client requests the capability as a command.\n-\t * The function will be provided the capabilities requested via 'keys'\n-\t * as well as a struct packet_reader 'request' which the command should\n+\t * Will be provided a struct packet_reader 'request' which it should\n \t * use to read the command specific part of the request.  Every command\n \t * MUST read until a flush packet is seen before sending a response.\n \t *\n@@ -160,7 +159,7 @@ static int call_command(struct protocol_capability *command,\n \n \tread_startup_config(command);\n \n-\treturn command->command(r, keys, request);\n+\treturn command->command(r, request);\n }\n \n void protocol_v2_advertise_capabilities(void)\ndiff --git a/upload-pack.c b/upload-pack.c\nindex b9cfc09795b..097b982c010 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1680,8 +1680,7 @@ enum fetch_state {\n \tFETCH_DONE,\n };\n \n-int upload_pack_v2(struct repository *r, struct strvec *keys,\n-\t\t   struct packet_reader *request)\n+int upload_pack_v2(struct repository *r, struct packet_reader *request)\n {\n \tenum fetch_state state = FETCH_PROCESS_ARGS;\n \tstruct upload_pack_data data;\ndiff --git a/upload-pack.h b/upload-pack.h\nindex 52011d9ecff..0ae817c1d0d 100644\n--- a/upload-pack.h\n+++ b/upload-pack.h\n@@ -5,10 +5,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,\n \t\t const int timeout);\n \n struct repository;\n-struct strvec;\n struct packet_reader;\n-int upload_pack_v2(struct repository *r, struct strvec *keys,\n-\t\t   struct packet_reader *request);\n+int upload_pack_v2(struct repository *r, struct packet_reader *request);\n \n struct strbuf;\n int upload_pack_advertise(struct repository *r,\n-- \n2.32.0.955.ge7c5360f7e7\n\n"},{"id":"431761","messageId":"YQhenAYAOvdCAXge@google.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"Re: [PATCH v3 00/12] serve.[ch]: general API cleanup","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2021-08-02T21:07:40Z","receivedAt":"2021-08-02T21:07:49Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2021.07.22 01:40, Ævar Arnfjörð Bjarmason wrote:\n> For v2, see:\n> https://lore.kernel.org/git/cover-0.8-00000000000-20210628T191634Z-avarab@gmail.com/\n> \n> Junio: Sorry about the late update, re:\n> http://lore.kernel.org/git/xmqqsg07cz4g.fsf@gitster.g\n> \n> I think most people were on the fence about the need for this\n> \"startup_config\" callback in v2. I think we could still do without it,\n> but as the new 08/12 shows a big part of the API complexity was due to\n> serve() supporting the --advertise-refs mode directly. 08-10/12 is a\n> new late-series cleanup of that, which makes serve.[ch] a lot simpler.\n> \n> Ævar Arnfjörð Bjarmason (12):\n>   serve: mark has_capability() as static\n>   transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n>   transport: use designated initializers\n>   serve: use designated initializers\n>   serve.c: add call_{advertise,command}() indirection\n>   serve: add support for a \"startup\" git_config() callback\n>   serve.c: move version line to advertise_capabilities()\n>   serve.[ch]: remove \"serve_options\", split up --advertise-refs code\n>   {upload,receive}-pack tests: add --advertise-refs tests\n>   upload-pack: document and rename --advertise-refs\n>   upload-pack.c: convert to new serve.c \"startup\" config cb\n>   serve.[ch]: don't pass \"struct strvec *keys\" to commands\n\nSorry for the delayed response on this series; I've been away from work\nfor the past several weeks.\n\nI'm in favor of the cleanups in patches 1-4. I'm afraid I am not\nfamiliar enough with the server code to have useful feedback on the rest\nof the patches, at least not at first glance.\n"},{"id":"431801","messageId":"20210803060005.18151-1-carenas@gmail.com","threadId":"55944","inReplyTo":"patch-09.12-beafe9811c1-20210721T233307Z-avarab@gmail.com","subject":"[PATCH] fixup! {upload,receive}-pack tests: add --advertise-refs tests","fromName":"Carlo Marcelo Arenas Belón","fromEmail":"carenas@gmail.com","sentAt":"2021-08-03T06:00:05Z","receivedAt":"2021-08-03T06:00:24Z","isPatch":true,"sender":{"key":"carenas@gmail.com","avatar":"https://avatars.githubusercontent.com/u/76036?v=4"},"body":"avoids CI windows run failures showing:\n\n  non-executable tests: t5555-http-smart-common.sh\n  make: *** [Makefile:90: test-lint-executable] Error 1\n  make: *** Waiting for unfinished jobs....\n  Error: Process completed with exit code 2.\n\nSigned-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>\n---\n t/t5555-http-smart-common.sh | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh\nindex 8d9d6a556f..49faf5e283 100755\n--- a/t/t5555-http-smart-common.sh\n+++ b/t/t5555-http-smart-common.sh\n@@ -1,3 +1,5 @@\n+#!/bin/sh\n+\n test_description='test functionality common to smart fetch & push'\n \n . ./test-lib.sh\n-- \n2.33.0.rc0.433.g9a510e7e11\n\n"},{"id":"432028","messageId":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com","subject":"[PATCH v4 00/10] serve.[ch]: general API cleanup + --advertise-refs cleanup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:33Z","receivedAt":"2021-08-05T01:25:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This is a general API cleanup and simplifiction of of serve.[ch] and\nits interaction with \"upload-pack --advertise-refs\".\n\nPrevious rounds of this series had a way to define \"startup config\"\nfor commands hanging off the v2 server. That's now entirely gone,\nalong with prep work required for it.\n\nFor v3, see :\nhttps://lore.kernel.org/git/cover-00.12-00000000000-20210721T233307Z-avarab@gmail.com/\n\nThe fixup for the missing test shebang has also been squashed in,\nthanks Carlo!:\nhttps://lore.kernel.org/git/20210803060005.18151-1-carenas@gmail.com/\n\nDespite the overall diffstat this is a net deletion of code in any\nmeaningful sense of reducing complexity, i.e. it goes into the plus on\nthe diffstat due to the addition of previously missing tests, and the\nuse of designated initializers.\n\nÆvar Arnfjörð Bjarmason (10):\n  serve: mark has_capability() as static\n  transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n  transport: use designated initializers\n  serve: use designated initializers\n  serve.[ch]: don't pass \"struct strvec *keys\" to commands\n  serve: move transfer.advertiseSID check into session_id_advertise()\n  serve.c: move version line to advertise_capabilities()\n  {upload,receive}-pack tests: add --advertise-refs tests\n  serve.[ch]: remove \"serve_options\", split up --advertise-refs code\n  upload-pack: document and rename --advertise-refs\n\n Documentation/git-receive-pack.txt        |   5 +\n Documentation/git-upload-pack.txt         |  12 +-\n Documentation/technical/http-protocol.txt |   3 +\n Documentation/technical/protocol-v2.txt   |   3 +\n builtin/receive-pack.c                    |   3 +-\n builtin/upload-pack.c                     |  28 ++--\n http-backend.c                            |   2 +-\n ls-refs.c                                 |   3 +-\n ls-refs.h                                 |   4 +-\n protocol-caps.c                           |   3 +-\n protocol-caps.h                           |   4 +-\n serve.c                                   |  82 ++++++-----\n serve.h                                   |  12 +-\n t/helper/test-serve-v2.c                  |  14 +-\n t/t5555-http-smart-common.sh              | 161 ++++++++++++++++++++++\n transport-helper.c                        |  18 +--\n transport-internal.h                      |   2 +-\n transport.c                               |  32 ++---\n upload-pack.c                             |  21 +--\n upload-pack.h                             |  14 +-\n 20 files changed, 298 insertions(+), 128 deletions(-)\n create mode 100755 t/t5555-http-smart-common.sh\n\nRange-diff against v3:\n 1:  192fb64ef82 =  1:  13bb1a06078 serve: mark has_capability() as static\n 2:  d716bd3c537 =  2:  162f717436f transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"\n 3:  d31690614af =  3:  d3d5818adda transport: use designated initializers\n 4:  13f1a8d8325 =  4:  8da15c373cc serve: use designated initializers\n 5:  99eeff6f890 <  -:  ----------- serve.c: add call_{advertise,command}() indirection\n 6:  be719dc3dc1 <  -:  ----------- serve: add support for a \"startup\" git_config() callback\n12:  e4eb31b5b8e !  5:  7714f81d62c serve.[ch]: don't pass \"struct strvec *keys\" to commands\n    @@ Commit message\n         2018-03-15) was passing in the raw capabilities \"keys\", but nothing\n         downstream of it ever used them.\n     \n    -    Let's remove that code because it's not needed, and because if and\n    -    when we need to pass data about the advertisement (I have some WIP\n    -    patches for that), it makes much more sense to have the serve.c parse\n    -    the capabilities, and then pass specific information we need down than\n    -    expecting its API users to re-do their own parsing of the raw data.\n    +    Let's remove that code because it's not needed. If we do end up\n    +    needing to pass information about the advertisement in the future\n    +    it'll make more sense to have serve.c parse the capabilities keys and\n    +    pass the result of its parsing, rather than expecting expecting its\n    +    API users to parse the same keys again.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ ls-refs.h\n     -int ls_refs(struct repository *r, struct strvec *keys,\n     -\t    struct packet_reader *request);\n     +int ls_refs(struct repository *r, struct packet_reader *request);\n    - int ls_refs_startup_config(const char *var, const char *value, void *data);\n      int ls_refs_advertise(struct repository *r, struct strbuf *value);\n      \n    + #endif /* LS_REFS_H */\n     \n      ## protocol-caps.c ##\n     @@ protocol-caps.c: static void send_info(struct repository *r, struct packet_writer *writer,\n    - \tstrbuf_release(&send_buffer);\n    + \t}\n      }\n      \n     -int cap_object_info(struct repository *r, struct strvec *keys,\n    @@ protocol-caps.h\n      #endif /* PROTOCOL_CAPS_H */\n     \n      ## serve.c ##\n    -@@ serve.c: static int session_id_advertise(struct repository *r, struct strbuf *value)\n    - }\n    - \n    - typedef int (*advertise_fn_t)(struct repository *r, struct strbuf *value);\n    --typedef int (*command_fn_t)(struct repository *r, struct strvec *keys,\n    -+typedef int (*command_fn_t)(struct repository *r,\n    - \t\t\t    struct packet_reader *request);\n    - \n    - struct protocol_capability {\n     @@ serve.c: struct protocol_capability {\n      \n      \t/*\n    @@ serve.c: struct protocol_capability {\n      \t * use to read the command specific part of the request.  Every command\n      \t * MUST read until a flush packet is seen before sending a response.\n      \t *\n    -@@ serve.c: static int call_command(struct protocol_capability *command,\n    + \t * This field should be NULL for capabilities which are not commands.\n    + \t */\n    +-\tint (*command)(struct repository *r,\n    +-\t\t       struct strvec *keys,\n    +-\t\t       struct packet_reader *request);\n    ++\tint (*command)(struct repository *r, struct packet_reader *request);\n    + };\n      \n    - \tread_startup_config(command);\n    + static struct protocol_capability capabilities[] = {\n    +@@ serve.c: static int process_request(void)\n    + \tif (has_capability(&keys, \"session-id\", &client_sid))\n    + \t\ttrace2_data_string(\"transfer\", NULL, \"client-sid\", client_sid);\n      \n    --\treturn command->command(r, keys, request);\n    -+\treturn command->command(r, request);\n    - }\n    +-\tcommand->command(the_repository, &keys, &reader);\n    ++\tcommand->command(the_repository, &reader);\n      \n    - void protocol_v2_advertise_capabilities(void)\n    + \tstrvec_clear(&keys);\n    + \treturn 0;\n     \n      ## upload-pack.c ##\n     @@ upload-pack.c: enum fetch_state {\n    @@ upload-pack.c: enum fetch_state {\n      \tstruct upload_pack_data data;\n     \n      ## upload-pack.h ##\n    -@@ upload-pack.h: void upload_pack(const int advertise_refs, const int stateless_rpc,\n    - \t\t const int timeout);\n    +@@ upload-pack.h: struct upload_pack_options {\n    + void upload_pack(struct upload_pack_options *options);\n      \n      struct repository;\n     -struct strvec;\n -:  ----------- >  6:  33db6b7cf53 serve: move transfer.advertiseSID check into session_id_advertise()\n 7:  b7928ddbe9b !  7:  9828e08cbad serve.c: move version line to advertise_capabilities()\n    @@ Commit message\n         serve.c: move version line to advertise_capabilities()\n     \n         The advertise_capabilities() is only called from serve() and we always\n    -    emit this version line before it, it makes more sense to consider the\n    -    capabilities part of a \"header\" that has the version, so let's move\n    -    the writing of the version there.\n    +    emit this version line before it. In a subsequent commit I'll make\n    +    builtin/upload-pack.c sometimes call advertise_capabilities()\n    +    directly, so it'll make sense to have this line emitted by\n    +    advertise_capabilities(), not serve() itself.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n 9:  beafe9811c1 !  8:  fd58abb7420 {upload,receive}-pack tests: add --advertise-refs tests\n    @@ Commit message\n     \n      ## t/t5555-http-smart-common.sh (new) ##\n     @@\n    ++#!/bin/sh\n    ++\n     +test_description='test functionality common to smart fetch & push'\n     +\n     +. ./test-lib.sh\n 8:  fb80f152713 !  9:  c9a35868933 serve.[ch]: remove \"serve_options\", split up --advertise-refs code\n    @@ http-backend.c: static void get_info_refs(struct strbuf *hdr, char *arg)\n      \n     \n      ## serve.c ##\n    -@@ serve.c: static int call_command(struct protocol_capability *command,\n    - \treturn command->command(r, keys, request);\n    - }\n    +@@ serve.c: static struct protocol_capability capabilities[] = {\n    + \t},\n    + };\n      \n     -static void advertise_capabilities(void)\n     +void protocol_v2_advertise_capabilities(void)\n    @@ upload-pack.h\n     +\t\t const int timeout);\n      \n      struct repository;\n    - struct strvec;\n    + struct packet_reader;\n10:  c6870b5f18a = 10:  2006cb234b6 upload-pack: document and rename --advertise-refs\n11:  2d4c3d0d463 <  -:  ----------- upload-pack.c: convert to new serve.c \"startup\" config cb\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432029","messageId":"patch-v4-01.10-13bb1a06078-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 01/10] serve: mark has_capability() as static","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:34Z","receivedAt":"2021-08-05T01:25:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The has_capability() function introduced in ed10cb952d3 (serve:\nintroduce git-serve, 2018-03-15) has never been used anywhere except\nserve.c, so let's mark it as static.\n\nIt was later changed from \"extern\" in 554544276a6 (*.[ch]: remove\nextern from function declarations using spatch, 2019-04-29), but we\ncould have simply marked it as \"static\" instead.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 4 ++--\n serve.h | 4 ----\n 2 files changed, 2 insertions(+), 6 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex f11c0e07c45..7bf5f23ea6a 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -156,8 +156,8 @@ static int is_command(const char *key, struct protocol_capability **command)\n \treturn 0;\n }\n \n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value)\n+static int has_capability(const struct strvec *keys, const char *capability,\n+\t\t\t  const char **value)\n {\n \tint i;\n \tfor (i = 0; i < keys->nr; i++) {\ndiff --git a/serve.h b/serve.h\nindex fc2683e24d3..56da77a87af 100644\n--- a/serve.h\n+++ b/serve.h\n@@ -1,10 +1,6 @@\n #ifndef SERVE_H\n #define SERVE_H\n \n-struct strvec;\n-int has_capability(const struct strvec *keys, const char *capability,\n-\t\t   const char **value);\n-\n struct serve_options {\n \tunsigned advertise_capabilities;\n \tunsigned stateless_rpc;\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432030","messageId":"patch-v4-02.10-162f717436f-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 02/10] transport: rename \"fetch\" in transport_vtable to \"fetch_refs\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:35Z","receivedAt":"2021-08-05T01:25:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"fetch\" member of the transport_vtable to \"fetch_refs\" for\nconsistency with the existing \"push_refs\". Neither of them just push\n\"refs\" but refs and objects, but having the two match makes the code\nmore readable than having it be inconsistent, especially since\n\"fetch_refs\" is a lot easier to grep for than \"fetch\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c   | 8 ++++----\n transport-internal.h | 2 +-\n transport.c          | 2 +-\n 3 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 4be035edb8b..8d445a8f3ee 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -671,8 +671,8 @@ static int connect_helper(struct transport *transport, const char *name,\n static struct ref *get_refs_list_using_list(struct transport *transport,\n \t\t\t\t\t    int for_push);\n \n-static int fetch(struct transport *transport,\n-\t\t int nr_heads, struct ref **to_fetch)\n+static int fetch_refs(struct transport *transport,\n+\t\t      int nr_heads, struct ref **to_fetch)\n {\n \tstruct helper_data *data = transport->data;\n \tint i, count;\n@@ -681,7 +681,7 @@ static int fetch(struct transport *transport,\n \n \tif (process_connect(transport, 0)) {\n \t\tdo_take_over(transport);\n-\t\treturn transport->vtable->fetch(transport, nr_heads, to_fetch);\n+\t\treturn transport->vtable->fetch_refs(transport, nr_heads, to_fetch);\n \t}\n \n \t/*\n@@ -1263,7 +1263,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n static struct transport_vtable vtable = {\n \tset_helper_option,\n \tget_refs_list,\n-\tfetch,\n+\tfetch_refs,\n \tpush_refs,\n \tconnect_helper,\n \trelease_helper\ndiff --git a/transport-internal.h b/transport-internal.h\nindex b60f1ba9077..c4ca0b733ac 100644\n--- a/transport-internal.h\n+++ b/transport-internal.h\n@@ -34,7 +34,7 @@ struct transport_vtable {\n \t * get_refs_list(), it should set the old_sha1 fields in the\n \t * provided refs now.\n \t **/\n-\tint (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);\n+\tint (*fetch_refs)(struct transport *transport, int refs_nr, struct ref **refs);\n \n \t/**\n \t * Push the objects and refs. Send the necessary objects, and\ndiff --git a/transport.c b/transport.c\nindex 17e9629710a..3e8a27b0321 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1453,7 +1453,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)\n \t\t\theads[nr_heads++] = rm;\n \t}\n \n-\trc = transport->vtable->fetch(transport, nr_heads, heads);\n+\trc = transport->vtable->fetch_refs(transport, nr_heads, heads);\n \n \tfree(heads);\n \treturn rc;\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432031","messageId":"patch-v4-03.10-d3d5818adda-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 03/10] transport: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:36Z","receivedAt":"2021-08-05T01:25:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the assignments to the various transport_vtables to use\ndesignated initializers, this makes the code easier to read and\nmaintain.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n transport-helper.c | 12 ++++++------\n transport.c        | 30 ++++++++++++------------------\n 2 files changed, 18 insertions(+), 24 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 8d445a8f3ee..e8dbdd11530 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -1261,12 +1261,12 @@ static struct ref *get_refs_list_using_list(struct transport *transport,\n }\n \n static struct transport_vtable vtable = {\n-\tset_helper_option,\n-\tget_refs_list,\n-\tfetch_refs,\n-\tpush_refs,\n-\tconnect_helper,\n-\trelease_helper\n+\t.set_option\t= set_helper_option,\n+\t.get_refs_list\t= get_refs_list,\n+\t.fetch_refs\t= fetch_refs,\n+\t.push_refs\t= push_refs,\n+\t.connect\t= connect_helper,\n+\t.disconnect\t= release_helper\n };\n \n int transport_helper_init(struct transport *transport, const char *name)\ndiff --git a/transport.c b/transport.c\nindex 3e8a27b0321..f9400b9b0bd 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -883,12 +883,10 @@ static int disconnect_git(struct transport *transport)\n }\n \n static struct transport_vtable taken_over_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tNULL,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.disconnect\t= disconnect_git\n };\n \n void transport_take_over(struct transport *transport,\n@@ -1032,21 +1030,17 @@ void transport_check_allowed(const char *type)\n }\n \n static struct transport_vtable bundle_vtable = {\n-\tNULL,\n-\tget_refs_from_bundle,\n-\tfetch_refs_from_bundle,\n-\tNULL,\n-\tNULL,\n-\tclose_bundle\n+\t.get_refs_list\t= get_refs_from_bundle,\n+\t.fetch_refs\t= fetch_refs_from_bundle,\n+\t.disconnect\t= close_bundle\n };\n \n static struct transport_vtable builtin_smart_vtable = {\n-\tNULL,\n-\tget_refs_via_connect,\n-\tfetch_refs_via_pack,\n-\tgit_transport_push,\n-\tconnect_git,\n-\tdisconnect_git\n+\t.get_refs_list\t= get_refs_via_connect,\n+\t.fetch_refs\t= fetch_refs_via_pack,\n+\t.push_refs\t= git_transport_push,\n+\t.connect\t= connect_git,\n+\t.disconnect\t= disconnect_git\n };\n \n struct transport *transport_get(struct remote *remote, const char *url)\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432032","messageId":"patch-v4-04.10-8da15c373cc-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 04/10] serve: use designated initializers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:37Z","receivedAt":"2021-08-05T01:25:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the declaration of the protocol_capability struct to use\ndesignated initializers, this makes this more verbose now, but a\nfollow-up commit will add a new field. At that point these lines would\nbe too dense to be on one line comfortably.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 38 +++++++++++++++++++++++++++++++-------\n 1 file changed, 31 insertions(+), 7 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 7bf5f23ea6a..33d00c8c634 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -73,13 +73,37 @@ struct protocol_capability {\n };\n \n static struct protocol_capability capabilities[] = {\n-\t{ \"agent\", agent_advertise, NULL },\n-\t{ \"ls-refs\", ls_refs_advertise, ls_refs },\n-\t{ \"fetch\", upload_pack_advertise, upload_pack_v2 },\n-\t{ \"server-option\", always_advertise, NULL },\n-\t{ \"object-format\", object_format_advertise, NULL },\n-\t{ \"session-id\", session_id_advertise, NULL },\n-\t{ \"object-info\", always_advertise, cap_object_info },\n+\t{\n+\t\t.name = \"agent\",\n+\t\t.advertise = agent_advertise,\n+\t},\n+\t{\n+\t\t.name = \"ls-refs\",\n+\t\t.advertise = ls_refs_advertise,\n+\t\t.command = ls_refs,\n+\t},\n+\t{\n+\t\t.name = \"fetch\",\n+\t\t.advertise = upload_pack_advertise,\n+\t\t.command = upload_pack_v2,\n+\t},\n+\t{\n+\t\t.name = \"server-option\",\n+\t\t.advertise = always_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-format\",\n+\t\t.advertise = object_format_advertise,\n+\t},\n+\t{\n+\t\t.name = \"session-id\",\n+\t\t.advertise = session_id_advertise,\n+\t},\n+\t{\n+\t\t.name = \"object-info\",\n+\t\t.advertise = always_advertise,\n+\t\t.command = cap_object_info,\n+\t},\n };\n \n static void advertise_capabilities(void)\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432033","messageId":"patch-v4-05.10-7714f81d62c-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 05/10] serve.[ch]: don't pass \"struct strvec *keys\" to commands","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:38Z","receivedAt":"2021-08-05T01:25:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The serve.c API added in ed10cb952d3 (serve: introduce git-serve,\n2018-03-15) was passing in the raw capabilities \"keys\", but nothing\ndownstream of it ever used them.\n\nLet's remove that code because it's not needed. If we do end up\nneeding to pass information about the advertisement in the future\nit'll make more sense to have serve.c parse the capabilities keys and\npass the result of its parsing, rather than expecting expecting its\nAPI users to parse the same keys again.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n ls-refs.c       | 3 +--\n ls-refs.h       | 4 +---\n protocol-caps.c | 3 +--\n protocol-caps.h | 4 +---\n serve.c         | 9 +++------\n upload-pack.c   | 3 +--\n upload-pack.h   | 4 +---\n 7 files changed, 9 insertions(+), 21 deletions(-)\n\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 88f6c3f60d8..a1a0250607f 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -138,8 +138,7 @@ static int ls_refs_config(const char *var, const char *value, void *data)\n \treturn parse_hide_refs_config(var, value, \"uploadpack\");\n }\n \n-int ls_refs(struct repository *r, struct strvec *keys,\n-\t    struct packet_reader *request)\n+int ls_refs(struct repository *r, struct packet_reader *request)\n {\n \tstruct ls_refs_data data;\n \ndiff --git a/ls-refs.h b/ls-refs.h\nindex a99e4be0bdd..e2243a547bb 100644\n--- a/ls-refs.h\n+++ b/ls-refs.h\n@@ -2,10 +2,8 @@\n #define LS_REFS_H\n \n struct repository;\n-struct strvec;\n struct packet_reader;\n-int ls_refs(struct repository *r, struct strvec *keys,\n-\t    struct packet_reader *request);\n+int ls_refs(struct repository *r, struct packet_reader *request);\n int ls_refs_advertise(struct repository *r, struct strbuf *value);\n \n #endif /* LS_REFS_H */\ndiff --git a/protocol-caps.c b/protocol-caps.c\nindex 13a9e63a04a..4b345c55046 100644\n--- a/protocol-caps.c\n+++ b/protocol-caps.c\n@@ -74,8 +74,7 @@ static void send_info(struct repository *r, struct packet_writer *writer,\n \t}\n }\n \n-int cap_object_info(struct repository *r, struct strvec *keys,\n-\t\t    struct packet_reader *request)\n+int cap_object_info(struct repository *r, struct packet_reader *request)\n {\n \tstruct requested_info info;\n \tstruct packet_writer writer;\ndiff --git a/protocol-caps.h b/protocol-caps.h\nindex 0a9f49df115..15c4550360c 100644\n--- a/protocol-caps.h\n+++ b/protocol-caps.h\n@@ -2,9 +2,7 @@\n #define PROTOCOL_CAPS_H\n \n struct repository;\n-struct strvec;\n struct packet_reader;\n-int cap_object_info(struct repository *r, struct strvec *keys,\n-\t\t    struct packet_reader *request);\n+int cap_object_info(struct repository *r, struct packet_reader *request);\n \n #endif /* PROTOCOL_CAPS_H */\ndiff --git a/serve.c b/serve.c\nindex 33d00c8c634..967bf17d623 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -60,16 +60,13 @@ struct protocol_capability {\n \n \t/*\n \t * Function called when a client requests the capability as a command.\n-\t * The function will be provided the capabilities requested via 'keys'\n-\t * as well as a struct packet_reader 'request' which the command should\n+\t * Will be provided a struct packet_reader 'request' which it should\n \t * use to read the command specific part of the request.  Every command\n \t * MUST read until a flush packet is seen before sending a response.\n \t *\n \t * This field should be NULL for capabilities which are not commands.\n \t */\n-\tint (*command)(struct repository *r,\n-\t\t       struct strvec *keys,\n-\t\t       struct packet_reader *request);\n+\tint (*command)(struct repository *r, struct packet_reader *request);\n };\n \n static struct protocol_capability capabilities[] = {\n@@ -294,7 +291,7 @@ static int process_request(void)\n \tif (has_capability(&keys, \"session-id\", &client_sid))\n \t\ttrace2_data_string(\"transfer\", NULL, \"client-sid\", client_sid);\n \n-\tcommand->command(the_repository, &keys, &reader);\n+\tcommand->command(the_repository, &reader);\n \n \tstrvec_clear(&keys);\n \treturn 0;\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb43..ed60a9abd60 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1655,8 +1655,7 @@ enum fetch_state {\n \tFETCH_DONE,\n };\n \n-int upload_pack_v2(struct repository *r, struct strvec *keys,\n-\t\t   struct packet_reader *request)\n+int upload_pack_v2(struct repository *r, struct packet_reader *request)\n {\n \tenum fetch_state state = FETCH_PROCESS_ARGS;\n \tstruct upload_pack_data data;\ndiff --git a/upload-pack.h b/upload-pack.h\nindex 27ddcdc6cb0..63e3252c98d 100644\n--- a/upload-pack.h\n+++ b/upload-pack.h\n@@ -11,10 +11,8 @@ struct upload_pack_options {\n void upload_pack(struct upload_pack_options *options);\n \n struct repository;\n-struct strvec;\n struct packet_reader;\n-int upload_pack_v2(struct repository *r, struct strvec *keys,\n-\t\t   struct packet_reader *request);\n+int upload_pack_v2(struct repository *r, struct packet_reader *request);\n \n struct strbuf;\n int upload_pack_advertise(struct repository *r,\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432034","messageId":"patch-v4-06.10-33db6b7cf53-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 06/10] serve: move transfer.advertiseSID check into session_id_advertise()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:39Z","receivedAt":"2021-08-05T01:25:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In 6b5b6e422ee (serve: advertise session ID in v2 capabilities,\n2020-11-11) the check for transfer.advertiseSID was added to the\nbeginning of the main serve() loop. Thus on startup of the server we'd\npopulate it.\n\nLet's instead use an explicit lazy initialization pattern in\nsession_id_advertise() itself, we'll still look the config up only\nonce per-process, but by moving it out of serve() itself the further\nchanging of that routine becomes easier.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 7 ++++---\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 967bf17d623..26829991333 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -9,7 +9,7 @@\n #include \"serve.h\"\n #include \"upload-pack.h\"\n \n-static int advertise_sid;\n+static int advertise_sid = -1;\n \n static int always_advertise(struct repository *r,\n \t\t\t    struct strbuf *value)\n@@ -35,6 +35,9 @@ static int object_format_advertise(struct repository *r,\n \n static int session_id_advertise(struct repository *r, struct strbuf *value)\n {\n+\tif (advertise_sid == -1 &&\n+\t    git_config_get_bool(\"transfer.advertisesid\", &advertise_sid))\n+\t\tadvertise_sid = 0;\n \tif (!advertise_sid)\n \t\treturn 0;\n \tif (value)\n@@ -300,8 +303,6 @@ static int process_request(void)\n /* Main serve loop for protocol version 2 */\n void serve(struct serve_options *options)\n {\n-\tgit_config_get_bool(\"transfer.advertisesid\", &advertise_sid);\n-\n \tif (options->advertise_capabilities || !options->stateless_rpc) {\n \t\t/* serve by default supports v2 */\n \t\tpacket_write_fmt(1, \"version 2\\n\");\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432035","messageId":"patch-v4-07.10-9828e08cbad-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 07/10] serve.c: move version line to advertise_capabilities()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:40Z","receivedAt":"2021-08-05T01:26:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The advertise_capabilities() is only called from serve() and we always\nemit this version line before it. In a subsequent commit I'll make\nbuiltin/upload-pack.c sometimes call advertise_capabilities()\ndirectly, so it'll make sense to have this line emitted by\nadvertise_capabilities(), not serve() itself.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n serve.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/serve.c b/serve.c\nindex 26829991333..412c2cd1317 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -112,6 +112,9 @@ static void advertise_capabilities(void)\n \tstruct strbuf value = STRBUF_INIT;\n \tint i;\n \n+\t/* serve by default supports v2 */\n+\tpacket_write_fmt(1, \"version 2\\n\");\n+\n \tfor (i = 0; i < ARRAY_SIZE(capabilities); i++) {\n \t\tstruct protocol_capability *c = &capabilities[i];\n \n@@ -304,9 +307,6 @@ static int process_request(void)\n void serve(struct serve_options *options)\n {\n \tif (options->advertise_capabilities || !options->stateless_rpc) {\n-\t\t/* serve by default supports v2 */\n-\t\tpacket_write_fmt(1, \"version 2\\n\");\n-\n \t\tadvertise_capabilities();\n \t\t/*\n \t\t * If only the list of capabilities was requested exit\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432036","messageId":"patch-v4-08.10-fd58abb7420-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 08/10] {upload,receive}-pack tests: add --advertise-refs tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:41Z","receivedAt":"2021-08-05T01:26:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The --advertise-refs option had no explicit tests of its own, only\nother http tests that would fail at a distance if it it was\nbroken. Let's test its behavior explicitly.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5555-http-smart-common.sh | 147 +++++++++++++++++++++++++++++++++++\n 1 file changed, 147 insertions(+)\n create mode 100755 t/t5555-http-smart-common.sh\n\ndiff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh\nnew file mode 100755\nindex 00000000000..b813099ed2c\n--- /dev/null\n+++ b/t/t5555-http-smart-common.sh\n@@ -0,0 +1,147 @@\n+#!/bin/sh\n+\n+test_description='test functionality common to smart fetch & push'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit --no-tag initial\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs' '\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) HEAD\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\t# We only care about GIT_PROTOCOL, not GIT_TEST_PROTOCOL_VERSION\n+\tsane_unset GIT_PROTOCOL &&\n+\tGIT_TEST_PROTOCOL_VERSION=2 \\\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect &&\n+\n+\t# The --advertise-refs alias works\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs: v0' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) HEAD\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect &&\n+\n+\t# With explicit v0\n+\tGIT_PROTOCOL=version=0 \\\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+\n+'\n+\n+test_expect_success 'git receive-pack --advertise-refs: v0' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tgit receive-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect &&\n+\n+\t# With explicit v0\n+\tGIT_PROTOCOL=version=0 \\\n+\tgit receive-pack --advertise-refs . >out 2>err &&\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs: v1' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\tversion 1\n+\t$(git rev-parse HEAD) HEAD\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=1 \\\n+\tgit upload-pack --advertise-refs . >out &&\n+\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git receive-pack --advertise-refs: v1' '\n+\t# With no specified protocol\n+\tcat >expect <<-EOF &&\n+\tversion 1\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=1 \\\n+\tgit receive-pack --advertise-refs . >out &&\n+\n+\ttest-tool pkt-line unpack <out >actual 2>err &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git upload-pack --advertise-refs: v2' '\n+\tcat >expect <<-EOF &&\n+\tversion 2\n+\tagent=FAKE\n+\tls-refs=unborn\n+\tfetch=shallow wait-for-done\n+\tserver-option\n+\tobject-format=$(test_oid algo)\n+\tobject-info\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=2 \\\n+\tGIT_USER_AGENT=FAKE \\\n+\tgit upload-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_expect_success 'git receive-pack --advertise-refs: v2' '\n+\t# There is no v2 yet for receive-pack, implicit v0\n+\tcat >expect <<-EOF &&\n+\t$(git rev-parse HEAD) $(git symbolic-ref HEAD)\n+\t0000\n+\tEOF\n+\n+\tGIT_PROTOCOL=version=2 \\\n+\tgit receive-pack --advertise-refs . >out 2>err &&\n+\n+\ttest-tool pkt-line unpack <out >actual &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp actual expect\n+'\n+\n+test_done\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432037","messageId":"patch-v4-09.10-c9a35868933-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 09/10] serve.[ch]: remove \"serve_options\", split up --advertise-refs code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:42Z","receivedAt":"2021-08-05T01:26:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"advertise capabilities\" mode of serve.c added in\ned10cb952d3 (serve: introduce git-serve, 2018-03-15) is only used by\nthe http-backend.c to call {upload,receive}-pack with the\n--advertise-refs parameter. See 42526b478e3 (Add stateless RPC options\nto upload-pack, receive-pack, 2009-10-30).\n\nLet's just make cmd_upload_pack() take the two (v2) or three (v2)\nparameters the the v2/v1 servicing functions need directly, and pass\nthose in via the function signature. The logic of whether daemon mode\nis implied by the timeout belongs in the v1 function (only used\nthere).\n\nOnce we split up the \"advertise v2 refs\" from \"serve v2 request\" it\nbecomes clear that v2 never cared about those in combination. The only\ntime it mattered was for v1 to emit its ref advertisement, in that\ncase we wanted to emit the smart-http-only \"no-done\" capability.\n\nSince we only do that in the --advertise-refs codepath let's just have\nit set \"do_done\" itself in v1's upload_pack() just before send_ref(),\nat that point --advertise-refs and --stateless-rpc in combination are\nredundant (the only user is get_info_refs() in http-backend.c), so we\ncan just pass in --advertise-refs only.\n\nSince we need to touch all the serve() and advertise_capabilities()\ncodepaths let's rename them to less clever and obvious names, it's\nbeen suggested numerous times, the latest of which is [1]'s suggestion\nfor protocol_v2_serve_loop(). Let's go with that.\n\n1. https://lore.kernel.org/git/CAFQ2z_NyGb8rju5CKzmo6KhZXD0Dp21u-BbyCb2aNxLEoSPRJw@mail.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/upload-pack.c    | 25 ++++++++++++-------------\n http-backend.c           |  2 +-\n serve.c                  | 18 +++++-------------\n serve.h                  |  8 ++------\n t/helper/test-serve-v2.c | 14 +++++++++-----\n upload-pack.c            | 18 +++++++++++-------\n upload-pack.h            | 10 ++--------\n 7 files changed, 42 insertions(+), 53 deletions(-)\n\ndiff --git a/builtin/upload-pack.c b/builtin/upload-pack.c\nindex 6da8fa2607c..8506030a648 100644\n--- a/builtin/upload-pack.c\n+++ b/builtin/upload-pack.c\n@@ -16,16 +16,17 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n {\n \tconst char *dir;\n \tint strict = 0;\n-\tstruct upload_pack_options opts = { 0 };\n-\tstruct serve_options serve_opts = SERVE_OPTIONS_INIT;\n+\tint advertise_refs = 0;\n+\tint stateless_rpc = 0;\n+\tint timeout = 0;\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"stateless-rpc\", &opts.stateless_rpc,\n+\t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n \t\t\t N_(\"quit after a single request/response exchange\")),\n-\t\tOPT_BOOL(0, \"advertise-refs\", &opts.advertise_refs,\n+\t\tOPT_BOOL(0, \"advertise-refs\", &advertise_refs,\n \t\t\t N_(\"exit immediately after initial ref advertisement\")),\n \t\tOPT_BOOL(0, \"strict\", &strict,\n \t\t\t N_(\"do not try <directory>/.git/ if <directory> is no Git directory\")),\n-\t\tOPT_INTEGER(0, \"timeout\", &opts.timeout,\n+\t\tOPT_INTEGER(0, \"timeout\", &timeout,\n \t\t\t    N_(\"interrupt transfer after <n> seconds of inactivity\")),\n \t\tOPT_END()\n \t};\n@@ -38,9 +39,6 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage_with_options(upload_pack_usage, options);\n \n-\tif (opts.timeout)\n-\t\topts.daemon_mode = 1;\n-\n \tsetup_path();\n \n \tdir = argv[0];\n@@ -50,21 +48,22 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n \n \tswitch (determine_protocol_version_server()) {\n \tcase protocol_v2:\n-\t\tserve_opts.advertise_capabilities = opts.advertise_refs;\n-\t\tserve_opts.stateless_rpc = opts.stateless_rpc;\n-\t\tserve(&serve_opts);\n+\t\tif (advertise_refs)\n+\t\t\tprotocol_v2_advertise_capabilities();\n+\t\telse\n+\t\t\tprotocol_v2_serve_loop(stateless_rpc);\n \t\tbreak;\n \tcase protocol_v1:\n \t\t/*\n \t\t * v1 is just the original protocol with a version string,\n \t\t * so just fall through after writing the version string.\n \t\t */\n-\t\tif (opts.advertise_refs || !opts.stateless_rpc)\n+\t\tif (advertise_refs || !stateless_rpc)\n \t\t\tpacket_write_fmt(1, \"version 1\\n\");\n \n \t\t/* fallthrough */\n \tcase protocol_v0:\n-\t\tupload_pack(&opts);\n+\t\tupload_pack(advertise_refs, stateless_rpc, timeout);\n \t\tbreak;\n \tcase protocol_unknown_version:\n \t\tBUG(\"unknown protocol version\");\ndiff --git a/http-backend.c b/http-backend.c\nindex b329bf63f09..d37463cec8b 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -534,7 +534,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg)\n \n \tif (service_name) {\n \t\tconst char *argv[] = {NULL /* service name */,\n-\t\t\t\"--stateless-rpc\", \"--advertise-refs\",\n+\t\t\t\"--advertise-refs\",\n \t\t\t\".\", NULL};\n \t\tstruct rpc_service *svc = select_service(hdr, service_name);\n \ndiff --git a/serve.c b/serve.c\nindex 412c2cd1317..1817edc7f57 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -106,7 +106,7 @@ static struct protocol_capability capabilities[] = {\n \t},\n };\n \n-static void advertise_capabilities(void)\n+void protocol_v2_advertise_capabilities(void)\n {\n \tstruct strbuf capability = STRBUF_INIT;\n \tstruct strbuf value = STRBUF_INIT;\n@@ -303,24 +303,16 @@ static int process_request(void)\n \treturn 0;\n }\n \n-/* Main serve loop for protocol version 2 */\n-void serve(struct serve_options *options)\n+void protocol_v2_serve_loop(int stateless_rpc)\n {\n-\tif (options->advertise_capabilities || !options->stateless_rpc) {\n-\t\tadvertise_capabilities();\n-\t\t/*\n-\t\t * If only the list of capabilities was requested exit\n-\t\t * immediately after advertising capabilities\n-\t\t */\n-\t\tif (options->advertise_capabilities)\n-\t\t\treturn;\n-\t}\n+\tif (!stateless_rpc)\n+\t\tprotocol_v2_advertise_capabilities();\n \n \t/*\n \t * If stateless-rpc was requested then exit after\n \t * a single request/response exchange\n \t */\n-\tif (options->stateless_rpc) {\n+\tif (stateless_rpc) {\n \t\tprocess_request();\n \t} else {\n \t\tfor (;;)\ndiff --git a/serve.h b/serve.h\nindex 56da77a87af..f946cf904a2 100644\n--- a/serve.h\n+++ b/serve.h\n@@ -1,11 +1,7 @@\n #ifndef SERVE_H\n #define SERVE_H\n \n-struct serve_options {\n-\tunsigned advertise_capabilities;\n-\tunsigned stateless_rpc;\n-};\n-#define SERVE_OPTIONS_INIT { 0 }\n-void serve(struct serve_options *options);\n+void protocol_v2_advertise_capabilities(void);\n+void protocol_v2_serve_loop(int stateless_rpc);\n \n #endif /* SERVE_H */\ndiff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c\nindex aee35e5aef4..28e905afc36 100644\n--- a/t/helper/test-serve-v2.c\n+++ b/t/helper/test-serve-v2.c\n@@ -10,12 +10,12 @@ static char const * const serve_usage[] = {\n \n int cmd__serve_v2(int argc, const char **argv)\n {\n-\tstruct serve_options opts = SERVE_OPTIONS_INIT;\n-\n+\tint stateless_rpc = 0;\n+\tint advertise_capabilities = 0;\n \tstruct option options[] = {\n-\t\tOPT_BOOL(0, \"stateless-rpc\", &opts.stateless_rpc,\n+\t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n \t\t\t N_(\"quit after a single request/response exchange\")),\n-\t\tOPT_BOOL(0, \"advertise-capabilities\", &opts.advertise_capabilities,\n+\t\tOPT_BOOL(0, \"advertise-capabilities\", &advertise_capabilities,\n \t\t\t N_(\"exit immediately after advertising capabilities\")),\n \t\tOPT_END()\n \t};\n@@ -25,7 +25,11 @@ int cmd__serve_v2(int argc, const char **argv)\n \targc = parse_options(argc, argv, prefix, options, serve_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH |\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n-\tserve(&opts);\n+\n+\tif (advertise_capabilities)\n+\t\tprotocol_v2_advertise_capabilities();\n+\telse\n+\t\tprotocol_v2_serve_loop(stateless_rpc);\n \n \treturn 0;\n }\ndiff --git a/upload-pack.c b/upload-pack.c\nindex ed60a9abd60..fc38f04d983 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1214,7 +1214,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t\t\t     \" allow-tip-sha1-in-want\" : \"\",\n \t\t\t     (data->allow_uor & ALLOW_REACHABLE_SHA1) ?\n \t\t\t\t     \" allow-reachable-sha1-in-want\" : \"\",\n-\t\t\t     data->stateless_rpc ? \" no-done\" : \"\",\n+\t\t\t     data->no_done ? \" no-done\" : \"\",\n \t\t\t     symref_info.buf,\n \t\t\t     data->allow_filter ? \" filter\" : \"\",\n \t\t\t     session_id.buf,\n@@ -1329,7 +1329,8 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)\n \treturn parse_hide_refs_config(var, value, \"uploadpack\");\n }\n \n-void upload_pack(struct upload_pack_options *options)\n+void upload_pack(const int advertise_refs, const int stateless_rpc,\n+\t\t const int timeout)\n {\n \tstruct packet_reader reader;\n \tstruct upload_pack_data data;\n@@ -1338,14 +1339,17 @@ void upload_pack(struct upload_pack_options *options)\n \n \tgit_config(upload_pack_config, &data);\n \n-\tdata.stateless_rpc = options->stateless_rpc;\n-\tdata.daemon_mode = options->daemon_mode;\n-\tdata.timeout = options->timeout;\n+\tdata.stateless_rpc = stateless_rpc;\n+\tdata.timeout = timeout;\n+\tif (data.timeout)\n+\t\tdata.daemon_mode = 1;\n \n \thead_ref_namespaced(find_symref, &data.symref);\n \n-\tif (options->advertise_refs || !data.stateless_rpc) {\n+\tif (advertise_refs || !data.stateless_rpc) {\n \t\treset_timeout(data.timeout);\n+\t\tif (advertise_refs)\n+\t\t\tdata.no_done = 1;\n \t\thead_ref_namespaced(send_ref, &data);\n \t\tfor_each_namespaced_ref(send_ref, &data);\n \t\tadvertise_shallow_grafts(1);\n@@ -1355,7 +1359,7 @@ void upload_pack(struct upload_pack_options *options)\n \t\tfor_each_namespaced_ref(check_ref, NULL);\n \t}\n \n-\tif (!options->advertise_refs) {\n+\tif (!advertise_refs) {\n \t\tpacket_reader_init(&reader, 0, NULL, 0,\n \t\t\t\t   PACKET_READ_CHOMP_NEWLINE |\n \t\t\t\t   PACKET_READ_DIE_ON_ERR_PACKET);\ndiff --git a/upload-pack.h b/upload-pack.h\nindex 63e3252c98d..d6ee25ea98e 100644\n--- a/upload-pack.h\n+++ b/upload-pack.h\n@@ -1,14 +1,8 @@\n #ifndef UPLOAD_PACK_H\n #define UPLOAD_PACK_H\n \n-struct upload_pack_options {\n-\tint stateless_rpc;\n-\tint advertise_refs;\n-\tunsigned int timeout;\n-\tint daemon_mode;\n-};\n-\n-void upload_pack(struct upload_pack_options *options);\n+void upload_pack(const int advertise_refs, const int stateless_rpc,\n+\t\t const int timeout);\n \n struct repository;\n struct packet_reader;\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"432038","messageId":"patch-v4-10.10-2006cb234b6-20210805T011823Z-avarab@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"[PATCH v4 10/10] upload-pack: document and rename --advertise-refs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-08-05T01:25:43Z","receivedAt":"2021-08-05T01:26:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The --advertise-refs documentation in git-upload-pack added in\n9812f2136b3 (upload-pack.c: use parse-options API, 2016-05-31) hasn't\nbeen entirely true ever since v2 support was implemented in\ne52449b6722 (connect: request remote refs using v2, 2018-03-15). Under\nv2 we don't advertise the refs at all, but rather dump the\ncapabilities header.\n\nThis option has always been an obscure internal implementation detail,\nit wasn't even documented for git-receive-pack. Since it has exactly\none user let's rename it to --http-backend-info-refs, which is more\naccurate and points the reader in the right direction. Let's also\ncross-link this from the protocol v1 and v2 documentation.\n\nI'm retaining a hidden --advertise-refs alias in case there's any\nexternal users of this, and making both options hidden to the bash\ncompletion (as with most other internal-only options).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-receive-pack.txt        |  5 +++++\n Documentation/git-upload-pack.txt         | 12 ++++++++----\n Documentation/technical/http-protocol.txt |  3 +++\n Documentation/technical/protocol-v2.txt   |  3 +++\n builtin/receive-pack.c                    |  3 ++-\n builtin/upload-pack.c                     |  5 +++--\n http-backend.c                            |  2 +-\n t/t5555-http-smart-common.sh              | 14 ++++++++++++++\n 8 files changed, 39 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt\nindex 25702ed7307..014a78409b9 100644\n--- a/Documentation/git-receive-pack.txt\n+++ b/Documentation/git-receive-pack.txt\n@@ -41,6 +41,11 @@ OPTIONS\n <directory>::\n \tThe repository to sync into.\n \n+--http-backend-info-refs::\n+\tUsed by linkgit:git-http-backend[1] to serve up\n+\t`$GIT_URL/info/refs?service=git-receive-pack` requests. See\n+\t`--http-backend-info-refs` in linkgit:git-upload-pack[1].\n+\n PRE-RECEIVE HOOK\n ----------------\n Before any ref is updated, if $GIT_DIR/hooks/pre-receive file exists\ndiff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt\nindex 9822c1eb1ad..739416ec83d 100644\n--- a/Documentation/git-upload-pack.txt\n+++ b/Documentation/git-upload-pack.txt\n@@ -36,10 +36,14 @@ OPTIONS\n \tThis fits with the HTTP POST request processing model where\n \ta program may read the request, write a response, and must exit.\n \n---advertise-refs::\n-\tOnly the initial ref advertisement is output, and the program exits\n-\timmediately. This fits with the HTTP GET request model, where\n-\tno request content is received but a response must be produced.\n+--http-backend-info-refs::\n+\tUsed by linkgit:git-http-backend[1] to serve up\n+\t`$GIT_URL/info/refs?service=git-upload-pack` requests. See\n+\t\"Smart Clients\" in link:technical/http-protocol.html[the HTTP\n+\ttransfer protocols] documentation and \"HTTP Transport\" in\n+\tlink:technical/protocol-v2.html[the Git Wire Protocol, Version\n+\t2] documentation. Also understood by\n+\tlinkgit:git-receive-pack[1].\n \n <directory>::\n \tThe repository to sync from.\ndiff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt\nindex 96d89ea9b22..cc5126cfeda 100644\n--- a/Documentation/technical/http-protocol.txt\n+++ b/Documentation/technical/http-protocol.txt\n@@ -225,6 +225,9 @@ The client may send Extra Parameters (see\n Documentation/technical/pack-protocol.txt) as a colon-separated string\n in the Git-Protocol HTTP header.\n \n+Uses the `--http-backend-info-refs` option to\n+linkgit:git-upload-pack[1].\n+\n Dumb Server Response\n ^^^^^^^^^^^^^^^^^^^^\n Dumb servers MUST respond with the dumb server reply format.\ndiff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt\nindex 1040d853198..213538f1d0e 100644\n--- a/Documentation/technical/protocol-v2.txt\n+++ b/Documentation/technical/protocol-v2.txt\n@@ -81,6 +81,9 @@ A v2 server would reply:\n Subsequent requests are then made directly to the service\n `$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack).\n \n+Uses the `--http-backend-info-refs` option to\n+linkgit:git-upload-pack[1].\n+\n Capability Advertisement\n ------------------------\n \ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 2d1f97e1ca7..7ffb45d7062 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -2477,7 +2477,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"quiet\")),\n \t\tOPT_HIDDEN_BOOL(0, \"stateless-rpc\", &stateless_rpc, NULL),\n-\t\tOPT_HIDDEN_BOOL(0, \"advertise-refs\", &advertise_refs, NULL),\n+\t\tOPT_HIDDEN_BOOL(0, \"http-backend-info-refs\", &advertise_refs, NULL),\n+\t\tOPT_ALIAS(0, \"advertise-refs\", \"http-backend-info-refs\"),\n \t\tOPT_HIDDEN_BOOL(0, \"reject-thin-pack-for-testing\", &reject_thin, NULL),\n \t\tOPT_END()\n \t};\ndiff --git a/builtin/upload-pack.c b/builtin/upload-pack.c\nindex 8506030a648..125af53885f 100644\n--- a/builtin/upload-pack.c\n+++ b/builtin/upload-pack.c\n@@ -22,8 +22,9 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL(0, \"stateless-rpc\", &stateless_rpc,\n \t\t\t N_(\"quit after a single request/response exchange\")),\n-\t\tOPT_BOOL(0, \"advertise-refs\", &advertise_refs,\n-\t\t\t N_(\"exit immediately after initial ref advertisement\")),\n+\t\tOPT_HIDDEN_BOOL(0, \"http-backend-info-refs\", &advertise_refs,\n+\t\t\t\tN_(\"serve up the info/refs for git-http-backend\")),\n+\t\tOPT_ALIAS(0, \"advertise-refs\", \"http-backend-info-refs\"),\n \t\tOPT_BOOL(0, \"strict\", &strict,\n \t\t\t N_(\"do not try <directory>/.git/ if <directory> is no Git directory\")),\n \t\tOPT_INTEGER(0, \"timeout\", &timeout,\ndiff --git a/http-backend.c b/http-backend.c\nindex d37463cec8b..838374edb91 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -534,7 +534,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg)\n \n \tif (service_name) {\n \t\tconst char *argv[] = {NULL /* service name */,\n-\t\t\t\"--advertise-refs\",\n+\t\t\t\"--http-backend-info-refs\",\n \t\t\t\".\", NULL};\n \t\tstruct rpc_service *svc = select_service(hdr, service_name);\n \ndiff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh\nindex b813099ed2c..49faf5e283b 100755\n--- a/t/t5555-http-smart-common.sh\n+++ b/t/t5555-http-smart-common.sh\n@@ -8,6 +8,20 @@ test_expect_success 'setup' '\n \ttest_commit --no-tag initial\n '\n \n+test_expect_success 'git upload-pack --http-backend-info-refs and --advertise-refs are aliased' '\n+\tgit upload-pack --http-backend-info-refs . >expected 2>err.expected &&\n+\tgit upload-pack --advertise-refs . >actual 2>err.actual &&\n+\ttest_cmp err.expected err.actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git receive-pack --http-backend-info-refs and --advertise-refs are aliased' '\n+\tgit receive-pack --http-backend-info-refs . >expected 2>err.expected &&\n+\tgit receive-pack --advertise-refs . >actual 2>err.actual &&\n+\ttest_cmp err.expected err.actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'git upload-pack --advertise-refs' '\n \tcat >expect <<-EOF &&\n \t$(git rev-parse HEAD) HEAD\n-- \n2.33.0.rc0.597.gc569a812f0a\n\n"},{"id":"433595","messageId":"17efcff7-9a62-046b-ecf9-2cbed84280e9@gmail.com","threadId":"55944","inReplyTo":"patch-v4-09.10-c9a35868933-20210805T011823Z-avarab@gmail.com","subject":"Re: [PATCH v4 09/10] serve.[ch]: remove \"serve_options\", split up --advertise-refs code","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-08-24T16:52:13Z","receivedAt":"2021-08-24T16:52:24Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 8/4/2021 9:25 PM, Ævar Arnfjörð Bjarmason wrote:\n...  \n> -struct upload_pack_options {\n> -\tint stateless_rpc;\n> -\tint advertise_refs;\n> -\tunsigned int timeout;\n> -\tint daemon_mode;\n> -};\n> -\n> -void upload_pack(struct upload_pack_options *options);\n> +void upload_pack(const int advertise_refs, const int stateless_rpc,\n> +\t\t const int timeout);\n\nNormally, I would err on keeping a struct that presents the different\nparameters of a complicated method, since it is easier to add options\nwithout modifying all callers. However, in this case we already had\nsome confusion because the 'daemon_mode' option is dependent on the\nother values.\n\nThe fact that these parameters are translated into a 'struct\nupload_pack_data' immediately within upload_pack() shows that these\ncan be grouped into a \"context\" object for internal use without\nneeding this pattern for the method signature.\n\nWorks for me.\n\nThanks,\n-Stolee\n\n\n"},{"id":"433596","messageId":"ab23d3dc-bec3-9ab8-87d3-264b158aabba@gmail.com","threadId":"55944","inReplyTo":"cover-v4-00.10-00000000000-20210805T011823Z-avarab@gmail.com","subject":"Re: [PATCH v4 00/10] serve.[ch]: general API cleanup + --advertise-refs cleanup","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-08-24T16:55:07Z","receivedAt":"2021-08-24T16:55:12Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 8/4/2021 9:25 PM, Ævar Arnfjörð Bjarmason wrote:\n> This is a general API cleanup and simplifiction of of serve.[ch] and\n> its interaction with \"upload-pack --advertise-refs\".\n\ns/simplifiction/simplification/ (not that it matters much)\n\nThe changes included in this series seem innocent enough. The added\ntests are nice. Organizing struct initializers is good to do before\nadding options, which I see are coming later.\n\nThis version LGTM.\n\nThanks,\n-Stolee\n"}]}