From ecc3e5342de203fa2d84c4a49a46aaa87289534b Mon Sep 17 00:00:00 2001 From: Brandon Williams <bmwill@google.com> Date: Mon, 23 Apr 2018 15:46:22 -0700 Subject: [PATCH 1/3] serve: introduce the server-option capability Introduce the "server-option" capability to protocol version 2. This enables future clients the ability to send server specific options in command requests when using protocol version 2. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/technical/protocol-v2.txt | 10 ++++++++++ serve.c | 1 + t/t5701-git-serve.sh | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt index 136179d7d8..d7b6f38e0a 100644 --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@ -393,3 +393,13 @@ header. 1 - pack data 2 - progress messages 3 - fatal error message just before stream aborts + + server-option +~~~~~~~~~~~~~~~ + +If advertised, indicates that any number of server specific options can be +included in a request. This is done by sending each option as a +"server-option=<option>" capability line in the capability-list section of +a request. + +The provided options must not contain a NUL or LF character. diff --git a/serve.c b/serve.c index a5a7b2f7dd..bda085f09c 100644 --- a/serve.c +++ b/serve.c @@ -56,6 +56,7 @@ static struct protocol_capability capabilities[] = { { "agent", agent_advertise, NULL }, { "ls-refs", always_advertise, ls_refs }, { "fetch", upload_pack_advertise, upload_pack_v2 }, + { "server-option", always_advertise, NULL }, }; static void advertise_capabilities(void) diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh index 72d7bc5628..011a5796db 100755 --- a/t/t5701-git-serve.sh +++ b/t/t5701-git-serve.sh @@ -10,6 +10,7 @@ test_expect_success 'test capability advertisement' ' agent=git/$(git version | cut -d" " -f3) ls-refs fetch=shallow + server-option 0000 EOF @@ -173,4 +174,24 @@ test_expect_success 'symrefs parameter' ' test_cmp actual expect ' +test_expect_success 'sending server-options' ' + test-pkt-line pack >in <<-EOF && + command=ls-refs + server-option=hello + server-option=world + 0001 + ref-prefix HEAD + 0000 + EOF + + cat >expect <<-EOF && + $(git rev-parse HEAD) HEAD + 0000 + EOF + + git serve --stateless-rpc <in >out && + test-pkt-line unpack <out >actual && + test_cmp actual expect +' + test_done From ff473221b4d6cf0894ba47370492d853a36c024d Mon Sep 17 00:00:00 2001 From: Brandon Williams <bmwill@google.com> Date: Mon, 23 Apr 2018 15:46:23 -0700 Subject: [PATCH 2/3] ls-remote: send server options when using protocol v2 Teach ls-remote to optionally accept server options by specifying them on the cmdline via '-o' or '--server-option'. These server options are sent to the remote end when querying for the remote end's refs using protocol version 2. If communicating using a protocol other than v2 the provided options are ignored and not sent to the remote end. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/git-ls-remote.txt | 8 ++++++++ builtin/ls-remote.c | 4 ++++ connect.c | 9 ++++++++- remote.h | 4 +++- t/t5702-protocol-v2.sh | 16 ++++++++++++++++ transport.c | 2 +- transport.h | 6 ++++++ 7 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt index 5f2628c8f8..e5defb1b2d 100644 --- a/Documentation/git-ls-remote.txt +++ b/Documentation/git-ls-remote.txt @@ -60,6 +60,14 @@ OPTIONS upload-pack only shows the symref HEAD, so it will be the only one shown by ls-remote. +-o <option>:: +--server-option=<option>:: + Transmit the given string to the server when communicating using + protocol version 2. The given string must not contain a NUL or LF + character. + When multiple `--server-option=<option>` are given, they are all + sent to the other side in the order listed on the command line. + <repository>:: The "remote" repository to query. This parameter can be either a URL or the name of a remote (see the GIT URLS and diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 380c180270..3150bfb926 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -45,6 +45,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) const char *uploadpack = NULL; const char **pattern = NULL; struct argv_array ref_prefixes = ARGV_ARRAY_INIT; + struct string_list server_options = STRING_LIST_INIT_DUP; struct remote *remote; struct transport *transport; @@ -67,6 +68,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) 2, PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "symref", &show_symref_target, N_("show underlying ref in addition to the object pointed by it")), + OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), OPT_END() }; @@ -107,6 +109,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) transport = transport_get(remote, NULL); if (uploadpack != NULL) transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); + if (server_options.nr) + transport->server_options = &server_options; ref = transport_get_remote_refs(transport, &ref_prefixes); if (transport_disconnect(transport)) diff --git a/connect.c b/connect.c index 54971166ac..3000768c75 100644 --- a/connect.c +++ b/connect.c @@ -408,7 +408,8 @@ out: struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, struct ref **list, int for_push, - const struct argv_array *ref_prefixes) + const struct argv_array *ref_prefixes, + const struct string_list *server_options) { int i; *list = NULL; @@ -419,6 +420,12 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, if (server_supports_v2("agent", 0)) packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized()); + if (server_options && server_options->nr && + server_supports_v2("server-option", 1)) + for (i = 0; i < server_options->nr; i++) + packet_write_fmt(fd_out, "server-option=%s", + server_options->items[i].string); + packet_delim(fd_out); /* When pushing we don't want to request the peeled tags */ if (!for_push) diff --git a/remote.h b/remote.h index 2b3180f94d..93dd97e25f 100644 --- a/remote.h +++ b/remote.h @@ -153,6 +153,7 @@ void free_refs(struct ref *ref); struct oid_array; struct packet_reader; struct argv_array; +struct string_list; extern struct ref **get_remote_heads(struct packet_reader *reader, struct ref **list, unsigned int flags, struct oid_array *extra_have, @@ -161,7 +162,8 @@ extern struct ref **get_remote_heads(struct packet_reader *reader, /* Used for protocol v2 in order to retrieve refs from a remote */ extern struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, struct ref **list, int for_push, - const struct argv_array *ref_prefixes); + const struct argv_array *ref_prefixes, + const struct string_list *server_options); int resolve_remote_symref(struct ref *ref, struct ref *list); int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid); diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 56f7c3c326..71ef1aee17 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -154,6 +154,22 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol test_cmp actual expect ' +test_expect_success 'server-options are sent when using ls-remote' ' + test_when_finished "rm -f log" && + + GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \ + ls-remote -o hello -o world "file://$(pwd)/file_parent" master >actual && + + cat >expect <<-EOF && + $(git -C file_parent rev-parse refs/heads/master)$(printf "\t")refs/heads/master + EOF + + test_cmp actual expect && + grep "server-option=hello" log && + grep "server-option=world" log +' + + test_expect_success 'clone with file:// using protocol v2' ' test_when_finished "rm -f log" && diff --git a/transport.c b/transport.c index 16b2f54f22..e324fa2502 100644 --- a/transport.c +++ b/transport.c @@ -218,7 +218,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus switch (data->version) { case protocol_v2: get_remote_refs(data->fd[1], &reader, &refs, for_push, - ref_prefixes); + ref_prefixes, transport->server_options); break; case protocol_v1: case protocol_v0: diff --git a/transport.h b/transport.h index e783cfa075..73a7be3c8a 100644 --- a/transport.h +++ b/transport.h @@ -71,6 +71,12 @@ struct transport { */ const struct string_list *push_options; + /* + * These strings will be passed to the remote side on each command + * request, if both sides support the server-option capability. + */ + const struct string_list *server_options; + char *pack_lockfile; signed verbose : 3; /** From 5e3548ef161d4d284e35cf5f5d6a181ba4fe707b Mon Sep 17 00:00:00 2001 From: Brandon Williams <bmwill@google.com> Date: Mon, 23 Apr 2018 15:46:24 -0700 Subject: [PATCH 3/3] fetch: send server options when using protocol v2 Teach fetch to optionally accept server options by specifying them on the cmdline via '-o' or '--server-option'. These server options are sent to the remote end when performing a fetch communicating using protocol version 2. If communicating using a protocol other than v2 the provided options are ignored and not sent to the remote end. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/fetch-options.txt | 8 ++++++++ builtin/fetch.c | 5 +++++ fetch-pack.c | 7 +++++++ fetch-pack.h | 1 + t/t5702-protocol-v2.sh | 16 ++++++++++++++++ transport.c | 1 + 6 files changed, 38 insertions(+) diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 8631e365f4..97d3217df9 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -188,6 +188,14 @@ endif::git-pull[] is specified. This flag forces progress status even if the standard error stream is not directed to a terminal. +-o <option>:: +--server-option=<option>:: + Transmit the given string to the server when communicating using + protocol version 2. The given string must not contain a NUL or LF + character. + When multiple `--server-option=<option>` are given, they are all + sent to the other side in the order listed on the command line. + -4:: --ipv4:: Use IPv4 addresses only, ignoring IPv6 addresses. diff --git a/builtin/fetch.c b/builtin/fetch.c index 7ee83ac0f8..5a6f6b2dca 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -62,6 +62,7 @@ static int shown_url = 0; static int refmap_alloc, refmap_nr; static const char **refmap_array; static struct list_objects_filter_options filter_options; +static struct string_list server_options = STRING_LIST_INIT_DUP; static int git_fetch_config(const char *k, const char *v, void *cb) { @@ -170,6 +171,7 @@ static struct option builtin_fetch_options[] = { N_("accept refs that update .git/shallow")), { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"), N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg }, + OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), TRANSPORT_FAMILY_IPV4), OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), @@ -1417,6 +1419,9 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru } } + if (server_options.nr) + gtransport->server_options = &server_options; + sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); refspec = parse_fetch_refspec(ref_nr, refs); diff --git a/fetch-pack.c b/fetch-pack.c index 216d1368be..199eb8a1d8 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1174,6 +1174,13 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, packet_buf_write(&req_buf, "command=fetch"); if (server_supports_v2("agent", 0)) packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized()); + if (args->server_options && args->server_options->nr && + server_supports_v2("server-option", 1)) { + int i; + for (i = 0; i < args->server_options->nr; i++) + packet_write_fmt(fd_out, "server-option=%s", + args->server_options->items[i].string); + } packet_buf_delim(&req_buf); if (args->use_thin_pack) diff --git a/fetch-pack.h b/fetch-pack.h index 6afa08b48b..bb45a366a8 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -15,6 +15,7 @@ struct fetch_pack_args { const char *deepen_since; const struct string_list *deepen_not; struct list_objects_filter_options filter_options; + const struct string_list *server_options; unsigned deepen_relative:1; unsigned quiet:1; unsigned keep_pack:1; diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 71ef1aee17..dbfd0691c0 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -217,6 +217,22 @@ test_expect_success 'ref advertisment is filtered during fetch using protocol v2 ! grep "refs/tags/three" log ' +test_expect_success 'server-options are sent when fetching' ' + test_when_finished "rm -f log" && + + test_commit -C file_parent four && + + GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \ + fetch -o hello -o world origin master && + + git -C file_child log -1 --format=%s origin/master >actual && + git -C file_parent log -1 --format=%s >expect && + test_cmp expect actual && + + grep "server-option=hello" log && + grep "server-option=world" log +' + # Test protocol v2 with 'http://' transport # . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/transport.c b/transport.c index e324fa2502..6bce2b20ca 100644 --- a/transport.c +++ b/transport.c @@ -266,6 +266,7 @@ static int fetch_refs_via_pack(struct transport *transport, args.no_dependents = data->options.no_dependents; args.filter_options = data->options.filter_options; args.stateless_rpc = transport->stateless_rpc; + args.server_options = transport->server_options; if (!data->got_remote_heads) refs_tmp = get_refs_via_connect(transport, 0, NULL);