protocol version 2
Subsystems (2)
PATCH v2 20/27 upload-pack: introduce fetch server command
In reply to Brandon WilliamsIntroduce the 'fetch' server command.
--- Documentation/technical/protocol-v2.txt | 121 +++++++++++++ serve.c | 2 + t/t5701-git-serve.sh | 1 + upload-pack.c | 293 ++++++++++++++++++++++++++++++++ upload-pack.h | 5 + 5 files changed, 422 insertions(+)
Documentation/technical/protocol-v2.txt +121 -0
@@ -147,3 +147,124 @@ The output of ls-refs is as follows: ref-attribute = (symref | peeled) symref = "symref-target:" symref-target peeled = "peeled:" obj-id + + fetch +------- + +`fetch` is the command used to fetch a packfile in v2. It can be looked +at as a modified version of the v1 fetch where the ref-advertisement is +stripped out (since the `ls-refs` command fills that role) and the +message format is tweaked to eliminate redundancies and permit easy +addition of future extensions. + +Additional features not supported in the base command will be advertised +as the value of the command in the capability advertisement in the form +of a space separated list of features, e.g. "<command>=<feature 1> +<feature 2>". + +A `fetch` request can take the following parameters wrapped in +packet-lines: + + want <oid> + Indicates to the server an object which the client wants to + retrieve. + + have <oid> + Indicates to the server an object which the client has locally. + This allows the server to make a packfile which only contains + the objects that the client needs. Multiple 'have' lines can be + supplied. + + done + Indicates to the server that negotiation should terminate (or + not even begin if performing a clone) and that the server should + use the information supplied in the request to construct the + packfile. + + thin-pack + Request that a thin pack be sent, which is a pack with deltas + which reference base objects not contained within the pack (but + are known to exist at the receiving end). This can reduce the + network traffic significantly, but it requires the receiving end + to know how to "thicken" these packs by adding the missing bases + to the pack. + + no-progress + Request that progress information that would normally be sent on + side-band channel 2, during the packfile transfer, should not be + sent. However, the side-band channel 3 is still used for error + responses. + + include-tag + Request that annotated tags should be sent if the objects they + point to are being sent. + + ofs-delta + Indicate that the client understands PACKv2 with delta referring + to its base by position in pack rather than by an oid. That is, + they can read OBJ_OFS_DELTA (ake type 6) in a packfile. + +The response of `fetch` is broken into a number of sections separated by +delimiter packets (0001), with each section beginning with its section +header. + + output = *section + section = (acknowledgments | packfile) + (flush-pkt | delim-pkt) + + acknowledgments = PKT-LINE("acknowledgments" LF) + *(ready | nak | ack) + ready = PKT-LINE("ready" LF) + nak = PKT-LINE("NAK" LF) + ack = PKT-LINE("ACK" SP obj-id LF) + + packfile = PKT-LINE("packfile" LF) + [PACKFILE] + +---- + acknowledgments section + * Always begins with the section header "acknowledgments" + + * The server will respond with "NAK" if none of the object ids sent + as have lines were common. + + * The server will respond with "ACK obj-id" for all of the + object ids sent as have lines which are common. + + * A response cannot have both "ACK" lines as well as a "NAK" + line. + + * The server will respond with a "ready" line indicating that + the server has found an acceptable common base and is ready to + make and send a packfile (which will be found in the packfile + section of the same response) + + * If the client determines that it is finished with negotiations + by sending a "done" line, the acknowledgments sections can be + omitted from the server's response as an optimization. + + * If the server has found a suitable cut point and has decided + to send a "ready" line, then the server can decide to (as an + optimization) omit any "ACK" lines it would have sent during + its response. This is because the server will have already + determined the objects it plans to send to the client and no + further negotiation is needed. + +---- + packfile section + * Always begins with the section header "packfile" + + * The transmission of the packfile begins immediately after the + section header + + * The data transfer of the packfile is always multiplexed, using + the same semantics of the 'side-band-64k' capability from + protocol version 1. This means that each packet, during the + packfile data stream, is made up of a leading 4-byte pkt-line + length (typical of the pkt-line format), followed by a 1-byte + stream code, followed by the actual data. + + The stream code can be one of: + 1 - pack data + 2 - progress messages + 3 - fatal error message just before stream aborts
serve.c +2 -0
@@ -6,6 +6,7 @@#include "argv-array.h" #include "ls-refs.h" #include "serve.h" +#include "upload-pack.h" static int always_advertise (struct repository *r,struct strbuf *value)
@@ -53,6 +54,7 @@ static struct protocol_capability capabilities[] = { { "agent", agent_advertise, NULL }, { "stateless-rpc", always_advertise, NULL }, { "ls-refs", always_advertise, ls_refs }, + { "fetch", always_advertise, upload_pack_v2 }, };static void advertise_capabilities (void )
t/t5701-git-serve.sh +1 -0
@@ -10,6 +10,7 @@ test_expect_success 'test capability advertisement' ' agent=git/$(git version | cut -d" " -f3) stateless-rpc ls-refs + fetch 0000 EOF
upload-pack.c +293 -0
302 lines not on this page · raw message
upload-pack.h +5 -0
@@ -10,4 +10,9 @@ struct upload_pack_options {void upload_pack (struct upload_pack_options *options); +struct repository ; +struct argv_array ; +extern int upload_pack_v2 (struct repository *r,struct argv_array *keys, +struct argv_array *args); +#endif /* UPLOAD_PACK_H */
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 21/27 fetch-pack: perform a fetch using v2
In reply to Brandon WilliamsWhen communicating with a v2 server, perform a fetch by requesting the 'fetch' command.
--- builtin/fetch-pack.c | 2 +- fetch-pack.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++- fetch-pack.h | 4 +- t/t5702-protocol-v2.sh | 40 +++++++ transport.c | 8 +- 5 files changed, 324 insertions(+), 7 deletions(-)
builtin/fetch-pack.c +1 -1
@@ -213,7 +213,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) } ref =fetch_pack (&args, fd, conn, ref, dest, sought, nr_sought, - &shallow, pack_lockfile_ptr); + &shallow, pack_lockfile_ptr, protocol_v0);if (pack_lockfile) {printf ("lock %s\n" , pack_lockfile);fflush (stdout);
fetch-pack.c +274 -3
295 lines not on this page · raw message
fetch-pack.h +3 -1
@@ -3,6 +3,7 @@#include "string-list.h" #include "run-command.h" +#include "protocol.h" struct oid_array ;
@@ -43,7 +44,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args,struct ref **sought, int nr_sought, struct oid_array *shallow, - char **pack_lockfile); + char **pack_lockfile, + enum protocol_version version);/* * Print an appropriate error message for each sought ref that wasn't
t/t5702-protocol-v2.sh +40 -0
@@ -33,4 +33,44 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol ! grep "refs/tags/" log ' +test_expect_success 'clone with file:// using protocol v2' ' + GIT_TRACE_PACKET=1 git -c protocol.version=2 \ + clone "file://$(pwd)/file_parent" file_child 2>log && + + git -C file_child log -1 --format=%s >actual && + git -C file_parent log -1 --format=%s >expect && + test_cmp expect actual && + + # Server responded using protocol v1 + grep "clone< version 2" log +' + +test_expect_success 'fetch with file:// using protocol v2' ' + test_commit -C file_parent two && + + GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=2 \ + fetch origin 2>log && + + git -C file_child log -1 --format=%s origin/master >actual && + git -C file_parent log -1 --format=%s >expect && + test_cmp expect actual && + + # Server responded using protocol v1 + grep "fetch< version 2" log +' + +test_expect_success 'ref advertisment is filtered during fetch using protocol v2' ' + test_commit -C file_parent three && + + GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=2 \ + fetch origin master 2>log && + + 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 "ref-pattern master" log && + ! grep "refs/tags/" log +' + test_done
transport.c +6 -2
@@ -256,14 +256,18 @@ static int fetch_refs_via_pack(struct transport *transport,switch (data->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); + refs =fetch_pack (&args, data->fd, data->conn, + refs_tmp ? refs_tmp : transport->remote_refs, + dest, to_fetch, nr_heads, &data->shallow, + &transport->pack_lockfile, data->version); +packet_flush (data->fd[1 ]);break ;case protocol_v1:case protocol_v0: refs =fetch_pack (&args, data->fd, data->conn, refs_tmp ? refs_tmp : transport->remote_refs, dest, to_fetch, nr_heads, &data->shallow, - &transport->pack_lockfile); + &transport->pack_lockfile, data->version);break ;case protocol_unknown_version:BUG ("unknown protocol version" );
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 22/27 transport-helper: remove name parameter
In reply to Brandon WilliamsCommit 266f1fdfa (transport-helper: be quiet on read errors from helpers, 2013-06-21) removed a call to 'die()' which printed the name of the remote helper passed in to the 'recvline_fh()' function using the 'name' parameter. Once the call to 'die()' was removed the parameter was no longer necessary but wasn't removed. Clean up 'recvline_fh()' parameter list by removing the 'name' parameter.
--- transport-helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
transport-helper.c +3 -3
@@ -49,7 +49,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)die_errno ("Full write to remote helper failed" ); } -static int recvline_fh (FILE *helper,struct strbuf *buffer,const char *name) +static int recvline_fh (FILE *helper,struct strbuf *buffer) {strbuf_reset (buffer);if (debug)
@@ -67,7 +67,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name)static int recvline (struct helper_data *helper,struct strbuf *buffer) { -return recvline_fh (helper->out, buffer, helper->name); +return recvline_fh (helper->out, buffer); }static void write_constant (int fd,const char *str)
@@ -586,7 +586,7 @@ static int process_connect_service(struct transport *transport, goto exit;sendline (data, &cmdbuf); -if (recvline_fh (input, &cmdbuf, name)) +if (recvline_fh (input, &cmdbuf))exit (128 );if (!strcmp (cmdbuf.buf,"" )) {
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 23/27 transport-helper: refactor process_connect_service
In reply to Brandon WilliamsA future patch will need to take advantage of the logic which runs and processes the response of the connect command on a remote helper so factor out this logic from 'process_connect_service()' and place it into a helper function 'run_connect()'.
--- transport-helper.c | 67 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 29 deletions(-)
transport-helper.c +38 -29
@@ -545,14 +545,13 @@ static int fetch_with_import(struct transport *transport,return 0 ; } -static int process_connect_service (struct transport *transport, -const char *name,const char *exec) +static int run_connect (struct transport *transport,struct strbuf *cmdbuf) {struct helper_data *data = transport->data; -struct strbuf cmdbuf =STRBUF_INIT ; -struct child_process *helper; -int r, duped, ret =0 ; +int ret =0 ; +int duped;FILE *input; +struct child_process *helper; helper =get_helper (transport);
@@ -568,44 +567,54 @@ static int process_connect_service(struct transport *transport, input =xfdopen (duped,"r" );setvbuf (input,NULL , _IONBF,0 ); +sendline (data, cmdbuf); +if (recvline_fh (input, cmdbuf)) +exit (128 ); + +if (!strcmp (cmdbuf->buf,"" )) { + data->no_disconnect_req =1 ; +if (debug) +fprintf (stderr,"Debug: Smart transport connection " +"ready.\n" ); + ret =1 ; + }else if (!strcmp (cmdbuf->buf,"fallback" )) { +if (debug) +fprintf (stderr,"Debug: Falling back to dumb " +"transport.\n" ); + }else { +die ("Unknown response to connect: %s" , + cmdbuf->buf); + } + +fclose (input); +return ret; +} + +static int process_connect_service (struct transport *transport, +const char *name,const char *exec) +{ +struct helper_data *data = transport->data; +struct strbuf cmdbuf =STRBUF_INIT ; +int ret =0 ; +/* * Handle --upload-pack and friends. This is fire and forget... * just warn if it fails. */ if (strcmp (name, exec)) { - r =set_helper_option (transport,"servpath" , exec); +int r =set_helper_option (transport,"servpath" , exec);if (r >0 )warning ("Setting remote service path not supported by protocol." );else if (r <0 )warning ("Invalid remote service path." ); } -if (data->connect) +if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); -else - goto exit; - -sendline (data, &cmdbuf); -if (recvline_fh (input, &cmdbuf)) -exit (128 ); - -if (!strcmp (cmdbuf.buf,"" )) { - data->no_disconnect_req =1 ; -if (debug) -fprintf (stderr,"Debug: Smart transport connection " -"ready.\n" ); - ret =1 ; - }else if (!strcmp (cmdbuf.buf,"fallback" )) { -if (debug) -fprintf (stderr,"Debug: Falling back to dumb " -"transport.\n" ); - }else -die ("Unknown response to connect: %s" , - cmdbuf.buf); + ret =run_connect (transport, &cmdbuf); + } -exit:strbuf_release (&cmdbuf); -fclose (input);return ret; }
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 24/27 transport-helper: introduce stateless-connect
In reply to Brandon WilliamsIntroduce the transport-helper capability 'stateless-connect'. This capability indicates that the transport-helper can be requested to run the 'stateless-connect' command which should attempt to make a stateless connection with a remote end. Once established, the connection can be used by the git client to communicate with the remote end natively in a stateless-rpc manner as supported by protocol v2. This means that the client must send everything the server needs in a single request as the client must not assume any state-storing on the part of the server or transport.
If a stateless connection cannot be established then the remote-helper will respond in the same manner as the 'connect' command indicating that the client should fallback to using the dumb remote-helper commands.
--- transport-helper.c | 8 ++++++++ transport.c | 1 + transport.h | 6 ++++++ 3 files changed, 15 insertions(+)
transport-helper.c +8 -0
@@ -26,6 +26,7 @@ struct helper_data { option : 1, push : 1, connect : 1, + stateless_connect : 1, signed_tags : 1, check_connectivity : 1, no_disconnect_req : 1,
@@ -188,6 +189,8 @@ static struct child_process *get_helper(struct transport *transport) refspecs[refspec_nr++] =xstrdup (arg); }else if (!strcmp (capname,"connect" )) { data->connect =1 ; + }else if (!strcmp (capname,"stateless-connect" )) { + data->stateless_connect =1 ; }else if (!strcmp (capname,"signed-tags" )) { data->signed_tags =1 ; }else if (skip_prefix (capname,"export-marks " , &arg)) {
@@ -612,6 +615,11 @@ static int process_connect_service(struct transport *transport,if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); ret =run_connect (transport, &cmdbuf); + }else if (data->stateless_connect) { +strbuf_addf (&cmdbuf,"stateless-connect %s\n" , name); + ret =run_connect (transport, &cmdbuf); +if (ret) + transport->stateless_rpc =1 ; }strbuf_release (&cmdbuf);
transport.c +1 -0
@@ -250,6 +250,7 @@ static int fetch_refs_via_pack(struct transport *transport, data->options.check_self_contained_and_connected; args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; + args.stateless_rpc = transport->stateless_rpc;if (!data->got_remote_heads) refs_tmp =get_refs_via_connect (transport,0 ,NULL );
transport.h +6 -0
@@ -55,6 +55,12 @@ struct transport {*/ unsigned cloning : 1; +/* +* Indicates that the transport is connected via a half-duplex +* connection and should operate in stateless-rpc mode. +*/ + unsigned stateless_rpc : 1; +/* * These strings will be passed to the {pre, post}-receive hook, * on the remote side, if both sides support the push options capability.
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 25/27 pkt-line: add packet_buf_write_len function
In reply to Brandon WilliamsAdd the 'packet_buf_write_len()' function which allows for writing an arbitrary length buffer into a 'struct strbuf' and formatting it in packet-line format.
--- pkt-line.c | 16 ++++++++++++++++ pkt-line.h | 1 + 2 files changed, 17 insertions(+)
pkt-line.c +16 -0
@@ -215,6 +215,22 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)va_end (args); } +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len) +{ +size_t orig_len, n; + + orig_len = buf->len; +strbuf_addstr (buf,"0000" ); +strbuf_add (buf, data, len); + n = buf->len - orig_len; + +if (n >LARGE_PACKET_MAX ) +die ("protocol error: impossibly long line" ); + +set_packet_header (&buf->buf[orig_len], n); +packet_trace (buf->buf + orig_len +4 , n -4 ,1 ); +} +int write_packetized_from_fd (int fd_in,int fd_out) {static char buf[LARGE_PACKET_DATA_MAX ];
pkt-line.h +1 -0
@@ -26,6 +26,7 @@ void packet_buf_flush(struct strbuf *buf);void packet_buf_delim (struct strbuf *buf);void packet_write (int fd_out,const char *buf,size_t size);void packet_buf_write (struct strbuf *buf,const char *fmt, ...) __attribute__((format (printf,2 ,3 ))); +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len);int packet_flush_gently (int fd);int packet_write_fmt_gently (int fd,const char *fmt, ...) __attribute__((format (printf,2 ,3 )));int write_packetized_from_fd (int fd_in,int fd_out);
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 26/27 remote-curl: create copy of the service name
In reply to Brandon WilliamsMake a copy of the service name being requested instead of relying on the buffer pointed to by the passed in 'const char *' to remain unchanged.
--- remote-curl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
remote-curl.c +3 -2
@@ -165,7 +165,7 @@ static int set_option(const char *name, const char *value) }struct discovery { -const char *service; +char *service;char *buf_alloc;char *buf;size_t len;
@@ -257,6 +257,7 @@ static void free_discovery(struct discovery *d)free (d->shallow.oid);free (d->buf_alloc);free_refs (d->refs); +free (d->service);free (d); } }
@@ -343,7 +344,7 @@ static struct discovery *discover_refs(const char *service, int for_push)warning (_ ("redirecting to %s" ), url.buf); last=xcalloc (1 ,sizeof (*last_discovery)); - last->service = service; + last->service =xstrdup (service); last->buf_alloc =strbuf_detach (&buffer, &last->len); last->buf = last->buf_alloc;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v2 27/27 remote-curl: implement stateless-connect command
In reply to Brandon WilliamsTeach remote-curl the 'stateless-connect' command which is used to establish a stateless connection with servers which support protocol version 2. This allows remote-curl to act as a proxy, allowing the git client to communicate natively with a remote end, simply using remote-curl as a pass through to convert requests to http.
--- remote-curl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++- t/t5702-protocol-v2.sh | 41 +++++++++++ 2 files changed, 224 insertions(+), 2 deletions(-)
remote-curl.c +183 -2
@@ -171,6 +171,7 @@ struct discovery {size_t len;struct ref *refs;struct oid_array shallow; +enum protocol_version version; unsigned proto_git : 1; };static struct discovery *last_discovery;
@@ -184,9 +185,13 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)PACKET_READ_CHOMP_NEWLINE | PACKET_READ_GENTLE_ON_EOF); -switch (discover_version (&reader)) { + heads->version =discover_version (&reader); +switch (heads->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); +/* +* Do nothing. Client should run 'stateless-connect' and +* request the refs themselves. +*/ break ;case protocol_v1:case protocol_v0:
@@ -1047,6 +1052,178 @@ static void parse_push(struct strbuf *buf)free (specs); } +struct proxy_state { +char *service_name; +char *service_url; +struct curl_slist *headers; +struct strbuf request_buffer; +int in; +int out; +struct packet_reader reader; +size_t pos; +int seen_flush; +}; + +static void proxy_state_init (struct proxy_state *p,const char *service_name) +{ +struct strbuf buf =STRBUF_INIT ; + +memset (p,0 ,sizeof (*p)); + p->service_name =xstrdup (service_name); + + p->in =0 ; + p->out =1 ; +strbuf_init (&p->request_buffer,0 ); + +strbuf_addf (&buf,"%s%s" , url.buf, p->service_name); + p->service_url =strbuf_detach (&buf,NULL ); + + p->headers =http_copy_default_headers (); + +strbuf_addf (&buf,"Content-Type: application/x-%s-request" , p->service_name); + p->headers =curl_slist_append (p->headers, buf.buf); +strbuf_reset (&buf); + +strbuf_addf (&buf,"Accept: application/x-%s-result" , p->service_name); + p->headers =curl_slist_append (p->headers, buf.buf); + + p->headers =curl_slist_append (p->headers,"Transfer-Encoding: chunked" ); + +packet_reader_init (&p->reader, p->in,NULL ,0 , +PACKET_READ_GENTLE_ON_EOF ); + +strbuf_release (&buf); +} + +static void proxy_state_clear (struct proxy_state *p) +{ +free (p->service_name); +free (p->service_url); +curl_slist_free_all (p->headers); +strbuf_release (&p->request_buffer); +} + +static size_t proxy_in (char *buffer,size_t eltsize, +size_t nmemb,void *userdata) +{ +size_t max = eltsize * nmemb; +struct proxy_state *p = userdata; +size_t avail = p->request_buffer.len - p->pos; + +if (!avail) { +if (p->seen_flush) { + p->seen_flush =0 ; +return 0 ; + } + +strbuf_reset (&p->request_buffer); +switch (packet_reader_read (&p->reader)) { +case PACKET_READ_EOF : +die ("unexpected EOF when reading from parent process" ); +case PACKET_READ_NORMAL : +packet_buf_write_len (&p->request_buffer, p->reader.line, + p->reader.pktlen); +break ; +case PACKET_READ_DELIM : +packet_buf_delim (&p->request_buffer); +break ; +case PACKET_READ_FLUSH : +packet_buf_flush (&p->request_buffer); + p->seen_flush =1 ; +break ; + } + p->pos =0 ; + avail = p->request_buffer.len; + } + +if (max < avail) + avail = max; +memcpy (buffer, p->request_buffer.buf + p->pos, avail); + p->pos += avail; +return avail; +} + +static size_t proxy_out (char *buffer,size_t eltsize, +size_t nmemb,void *userdata) +{ +size_t size = eltsize * nmemb; +struct proxy_state *p = userdata; + +write_or_die (p->out, buffer, size); +return size; +} + +static int proxy_post (struct proxy_state *p) +{ +struct active_request_slot *slot; +int err; + + slot =get_active_slot (); + +curl_easy_setopt (slot->curl,CURLOPT_NOBODY ,0 ); +curl_easy_setopt (slot->curl,CURLOPT_POST ,1 ); +curl_easy_setopt (slot->curl,CURLOPT_URL , p->service_url); +curl_easy_setopt (slot->curl,CURLOPT_HTTPHEADER , p->headers); + +/* Setup function to read request from client */ +curl_easy_setopt (slot->curl,CURLOPT_READFUNCTION , proxy_in); +curl_easy_setopt (slot->curl,CURLOPT_READDATA , p); + +/* Setup function to write server response to client */ +curl_easy_setopt (slot->curl,CURLOPT_WRITEFUNCTION , proxy_out); +curl_easy_setopt (slot->curl,CURLOPT_WRITEDATA , p); + + err =run_slot (slot,NULL ); + +if (err !=HTTP_OK ) + err =-1 ; + +return err; +} + +static int stateless_connect (const char *service_name) +{ +struct discovery *discover; +struct proxy_state p; + +/* +* Run the info/refs request and see if the server supports protocol +* v2. If and only if the server supports v2 can we successfully +* establish a stateless connection, otherwise we need to tell the +* client to fallback to using other transport helper functions to +* complete their request. +*/ + discover =discover_refs (service_name,0 ); +if (discover->version != protocol_v2) { +printf ("fallback\n" ); +fflush (stdout); +return -1 ; + }else { +/* Stateless Connection established */ +printf ("\n" ); +fflush (stdout); + } + +proxy_state_init (&p, service_name); + +/* +* Dump the capability listing that we got from the server earlier +* during the info/refs request. +*/ +write_or_die (p.out, discover->buf, discover->len); + +/* Peek the next packet line. Until we see EOF keep sending POSTs */ +while (packet_reader_peek (&p.reader) !=PACKET_READ_EOF ) { +if (proxy_post (&p)) { +/* We would have an err here */ +break ; + } + } + +proxy_state_clear (&p); +return 0 ; +} +int cmd_main (int argc,const char **argv) {struct strbuf buf =STRBUF_INIT ;
@@ -1115,12 +1292,16 @@ int cmd_main(int argc, const char **argv)fflush (stdout); }else if (!strcmp (buf.buf,"capabilities" )) { +printf ("stateless-connect\n" );printf ("fetch\n" );printf ("option\n" );printf ("push\n" );printf ("check-connectivity\n" );printf ("\n" );fflush (stdout); + }else if (skip_prefix (buf.buf,"stateless-connect " , &arg)) { +if (!stateless_connect (arg)) +break ; }else {error ("remote-curl: unknown command '%s' from git" , buf.buf);return 1 ;
t/t5702-protocol-v2.sh +41 -0
45 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Sorry for chiming in with mostly nitpicks so late since sending this version. Mostly, I tried to read it to see if I could understand the scope of the patch and how this code worked before. It looks very polished, so I the nits were the best I could do.
Quoted 5 lines · Brandon Williams
Changes in v2:
* Added documentation for fetch
* changes #defines for state variables to be enums
* couple code changes to pkt-line functions and documentation
* Added unit tests for the git-serve binary as well as for ls-refs
I'm a fan of more unit-level testing, and I think that will be more important as we go on with these multiple configuration options.
Quoted 11 lines
Areas for improvement
* Push isn't implemented, right now this is ok because if v2 is requested the server can just default to v0. Before this can be merged we may want to change how the client request a new protocol, and not allow for sending "version=2" when pushing even though the user has it configured. Or maybe its fine to just have an older client who doesn't understand how to push (and request v2) to die if the server tries to speak v2 at it.Fixing this essentially would just require piping through a bit more information to the function which ultimately runs connect (for both builtins and remote-curl)
Definitely save push for a later patch. Getting 'fetch' online did require 'ls-refs' at the same time. Future reviews will be easier when adding one command at a time.
Quoted 4 lines
* I want to make sure that the docs are well written before this gets merged so I'm hoping that someone can do a through review on the docs themselves to make sure they are clear.
I made a comment in the docs about the architectural changes. While I think a discussion on that topic would be valuable, I'm not sure that's the point of the document (i.e. documenting what v2 does versus selling the value of the patch). I thought the docs were clear for how the commands work.
Quoted 10 lines
* Right now there is a capability 'stateless-rpc' which essentially makes sure that a server command completes after a single round (this is to make sure http works cleanly). After talking with some folks it may make more sense to just have v2 be stateless in nature so that all commands terminate after a single round trip. This makes things a bit easier if a server wants to have ssh just be a proxy for http.
One potential thing would be to flip this so that by default the protocol is stateless and if a server/command has a state-full mode that can be implemented as a capability at a later point. Thoughts?
At minimum, all commands should be designed with a "stateless first" philosophy since a large number of users communicate via HTTP[S] and any decisions that make stateless communication painful should be rejected.
Quoted 4 lines
* Shallow repositories and shallow clones aren't supported yet. I'm working on it and it can be either added to v2 by default if people think it needs to be in there from the start, or we can add it as a capability at a later point.
I'm happy to say the following:
1. Shallow repositories should not be used for servers, since they cannot service all requests.
2. Since v2 has easy capability features, I'm happy to leave shallow for later. We will want to verify that a shallow clone command reverts to v1.
I fetched bw/protocol-v2 with tip 13c70148, built, set 'protocol.version=2' in the config, and tested fetches against GitHub and VSTS just as a compatibility test. Everything worked just fine.
Is there an easy way to test the existing test suite for clone and fetch using protocol v2 to make sure there are no regressions with protocol.version=2 in the config?
Thanks,
-Stolee
Quoted 48 lines · Derrick Stolee
Sorry for chiming in with mostly nitpicks so late since sending this version. Mostly, I tried to read it to see if I could understand the scope of the patch and how this code worked before. It looks very polished, so I the nits were the best I could do.
Quoted 5 lines · Brandon Williams
Changes in v2:
* Added documentation for fetch
* changes #defines for state variables to be enums
* couple code changes to pkt-line functions and documentation
* Added unit tests for the git-serve binary as well as for ls-refsI'm a fan of more unit-level testing, and I think that will be more important as we go on with these multiple configuration options.
Quoted 11 lines
Areas for improvement
* Push isn't implemented, right now this is ok because if v2 is requested the server can just default to v0. Before this can be merged we may want to change how the client request a new protocol, and not allow for sending "version=2" when pushing even though the user has it configured. Or maybe its fine to just have an older client who doesn't understand how to push (and request v2) to die if the server tries to speak v2 at it.Fixing this essentially would just require piping through a bit more information to the function which ultimately runs connect (for both builtins and remote-curl)
Definitely save push for a later patch. Getting 'fetch' online did require 'ls-refs' at the same time. Future reviews will be easier when adding one command at a time.
Quoted 4 lines
* I want to make sure that the docs are well written before this gets merged so I'm hoping that someone can do a through review on the docs themselves to make sure they are clear.
I made a comment in the docs about the architectural changes. While I think a discussion on that topic would be valuable, I'm not sure that's the point of the document (i.e. documenting what v2 does versus selling the value of the patch). I thought the docs were clear for how the commands work.
Quoted 7 lines
* Right now there is a capability 'stateless-rpc' which essentially makes sure that a server command completes after a single round (this is to make sure http works cleanly). After talking with some folks it may make more sense to just have v2 be stateless in nature so that all commands terminate after a single round trip. This makes things a bit easier if a server wants to have ssh just be a proxy for http.
Quoted 7 lines
Quoted 3 lines
One potential thing would be to flip this so that by default the protocol is stateless and if a server/command has a state-full mode that can be implemented as a capability at a later point. Thoughts?
At minimum, all commands should be designed with a "stateless first" philosophy since a large number of users communicate via HTTP[S] and any decisions that make stateless communication painful should be rejected.
I agree with this and my next version will run with this philosophy in mind (v2 will be stateless by default).
Quoted 15 lines
Quoted 5 lines
* Shallow repositories and shallow clones aren't supported yet. I'm working on it and it can be either added to v2 by default if people think it needs to be in there from the start, or we can add it as a capability at a later point.
I'm happy to say the following:
1. Shallow repositories should not be used for servers, since they cannot service all requests.
2. Since v2 has easy capability features, I'm happy to leave shallow for later. We will want to verify that a shallow clone command reverts to v1.
Quoted 7 lines
I fetched bw/protocol-v2 with tip
13c70148, built, set 'protocol.version=2' in the config, and tested fetches against GitHub and VSTS just as a compatibility test. Everything worked just fine.Is there an easy way to test the existing test suite for clone and fetch using protocol v2 to make sure there are no regressions with protocol.version=2 in the config?
Yes there already exist interop tests for testing the addition of requesting a new protocol at //t/interop/i5700-protocol-transition.sh
Quoted 3 lines
Thanks,
-Stolee
Signature · 1 line
Brandon Williams
Quoted 5 lines · Brandon Williams
Changes in v2:
* Added documentation for fetch
* changes #defines for state variables to be enums
* couple code changes to pkt-line functions and documentation
* Added unit tests for the git-serve binary as well as for ls-refs
[...]
This looks really nice. I'm eager to get this in so we can do some additional commands to help make partial clone more efficient.
Thanks,
Jeff
v3 cover protocol version 2
In reply to Brandon WilliamsChanges in v3:
* There were some comments about how the protocol should be designed stateless first. I've made this change and instead of having to supply the stateless-rpc=true capability to force stateless behavior, the protocol just requires all commands to be stateless.
* Added some patches towards the end of the series to force the client to not request to use protocol v2 when pushing (even if configured to use v2). This is to ease the roll-out process of a push command in protocol v2. This way when servers gain the ability to accept pushing in v2 (and they start responding using v2 when requests are sent to the git-receive-pack endpoint) that clients who still don't understand how to push using v2 won't request to use v2 and then die when they recognize that the server does indeed know how to accept a push under v2.
* I implemented the shallow feature for fetch. This feature encapsulates the existing functionality of all the shallow/deepen capabilities in v0. So now a server can process shallow requests.
* Various other small tweaks that I can't remember :)
After all of that I think the series is in a pretty good state, baring any more critical reviewing feedback.
Thanks!
Brandon Williams (35):
pkt-line: introduce packet_read_with_status
pkt-line: introduce struct packet_reader
pkt-line: add delim packet support
upload-pack: convert to a builtin
upload-pack: factor out processing lines
transport: use get_refs_via_connect to get refs
connect: convert get_remote_heads to use struct packet_reader
connect: discover protocol version outside of get_remote_heads
transport: store protocol version
protocol: introduce enum protocol_version value protocol_v2
test-pkt-line: introduce a packet-line test helper
serve: introduce git-serve
ls-refs: introduce ls-refs server command
connect: request remote refs using v2
transport: convert get_refs_list to take a list of ref patterns
transport: convert transport_get_remote_refs to take a list of ref
patterns
ls-remote: pass ref patterns when requesting a remote's refs
fetch: pass ref patterns when fetching
push: pass ref patterns when pushing
upload-pack: introduce fetch server command
fetch-pack: perform a fetch using v2
upload-pack: support shallow requests
fetch-pack: support shallow requests
connect: refactor git_connect to only get the protocol version once
connect: don't request v2 when pushing
transport-helper: remove name parameter
transport-helper: refactor process_connect_service
transport-helper: introduce stateless-connect
pkt-line: add packet_buf_write_len function
remote-curl: create copy of the service name
remote-curl: store the protocol version the server responded with
http: allow providing extra headers for http requests
http: don't always add Git-Protocol header
remote-curl: implement stateless-connect command
remote-curl: don't request v2 when pushing
.gitignore | 1 + Documentation/technical/protocol-v2.txt | 338 +++++++++++++++++ Makefile | 7 +- builtin.h | 2 + builtin/clone.c | 2 +- builtin/fetch-pack.c | 21 +- builtin/fetch.c | 14 +- builtin/ls-remote.c | 7 +- builtin/receive-pack.c | 6 + builtin/remote.c | 2 +- builtin/send-pack.c | 20 +- builtin/serve.c | 30 ++ builtin/upload-pack.c | 74 ++++ connect.c | 352 +++++++++++++----- connect.h | 7 + fetch-pack.c | 319 +++++++++++++++- fetch-pack.h | 4 +- git.c | 2 + http.c | 25 +- http.h | 2 + ls-refs.c | 96 +++++ ls-refs.h | 9 + pkt-line.c | 149 +++++++- pkt-line.h | 77 ++++ protocol.c | 2 + protocol.h | 1 + remote-curl.c | 257 ++++++++++++- remote.h | 9 +- serve.c | 260 +++++++++++++ serve.h | 15 + t/helper/test-pkt-line.c | 64 ++++ t/t5701-git-serve.sh | 176 +++++++++ t/t5702-protocol-v2.sh | 239 ++++++++++++ transport-helper.c | 84 +++-- transport-internal.h | 4 +- transport.c | 116 ++++-- transport.h | 9 +- upload-pack.c | 625 ++++++++++++++++++++++++-------- upload-pack.h | 21 ++ 39 files changed, 3088 insertions(+), 360 deletions(-) create mode 100644 Documentation/technical/protocol-v2.txt create mode 100644 builtin/serve.c create mode 100644 builtin/upload-pack.c create mode 100644 ls-refs.c create mode 100644 ls-refs.h create mode 100644 serve.c create mode 100644 serve.h create mode 100644 t/helper/test-pkt-line.c create mode 100755 t/t5701-git-serve.sh create mode 100755 t/t5702-protocol-v2.sh create mode 100644 upload-pack.h
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v3 1/35 pkt-line: introduce packet_read_with_status
The current pkt-line API encodes the status of a pkt-line read in the length of the read content. An error is indicated with '-1', a flush with '0' (which can be confusing since a return value of '0' can also indicate an empty pkt-line), and a positive integer for the length of the read content otherwise. This doesn't leave much room for allowing the addition of additional special packets in the future.
To solve this introduce 'packet_read_with_status()' which reads a packet and returns the status of the read encoded as an 'enum packet_status' type. This allows for easily identifying between special and normal packets as well as errors. It also enables easily adding a new special packet in the future.
--- pkt-line.c | 57 +++++++++++++++++++++++++++++++++++++++++++-------------- pkt-line.h | 15 +++++++++++++++ 2 files changed, 58 insertions(+), 14 deletions(-)
pkt-line.c +43 -14
@@ -280,28 +280,33 @@ static int packet_length(const char *linelen)return (val <0 ) ? val : (val <<8 ) |hex2chr (linelen +2 ); } -int packet_read (int fd,char **src_buf,size_t *src_len, -char *buffer,unsigned size,int options) +enum packet_read_status packet_read_with_status (int fd,char **src_buffer,size_t *src_len, +char *buffer,unsigned size,int *pktlen, +int options) { -int len, ret; +int len;char linelen[4 ]; - ret =get_packet_data (fd, src_buf, src_len, linelen,4 , options); -if (ret <0 ) -return ret; +if (get_packet_data (fd, src_buffer, src_len, linelen,4 , options) <0 ) +return PACKET_READ_EOF ; + len =packet_length (linelen); -if (len <0 ) + +if (len <0 ) {die ("protocol error: bad line length character: %.4s" , linelen); -if (!len) { + }else if (!len) {packet_trace ("0000" ,4 ,0 ); -return 0 ; +return PACKET_READ_FLUSH ; + }else if (len <4 ) { +die ("protocol error: bad line length %d" , len); } + len -=4 ; -if (len >= size) +if ((unsigned )len >= size)die ("protocol error: bad line length %d" , len); - ret =get_packet_data (fd, src_buf, src_len, buffer, len, options); -if (ret <0 ) -return ret; + +if (get_packet_data (fd, src_buffer, src_len, buffer, len, options) <0 ) +return PACKET_READ_EOF ;if ((options &PACKET_READ_CHOMP_NEWLINE ) && len && buffer[len-1 ] =='\n' )
@@ -309,7 +314,31 @@ int packet_read(int fd, char **src_buf, size_t *src_len, buffer[len] =0 ;packet_trace (buffer, len,0 ); -return len; + *pktlen = len; +return PACKET_READ_NORMAL ; +} + +int packet_read (int fd,char **src_buffer,size_t *src_len, +char *buffer,unsigned size,int options) +{ +enum packet_read_status status; +int pktlen; + + status =packet_read_with_status (fd, src_buffer, src_len, + buffer, size, &pktlen, + options); +switch (status) { +case PACKET_READ_EOF : + pktlen =-1 ; +break ; +case PACKET_READ_NORMAL : +break ; +case PACKET_READ_FLUSH : + pktlen =0 ; +break ; + } + +return pktlen; } static char *packet_read_line_generic(int fd,
pkt-line.h +15 -0
@@ -65,6 +65,21 @@ int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);int packet_read (int fd,char **src_buffer,size_t *src_len,char *buffer,unsigned size,int options); +/* +* Read a packetized line into a buffer like the 'packet_read()' function but +* returns an 'enum packet_read_status' which indicates the status of the read. +* The number of bytes read will be assigined to *pktlen if the status of the +* read was 'PACKET_READ_NORMAL'. +*/ +enum packet_read_status { +PACKET_READ_EOF =-1 , +PACKET_READ_NORMAL , +PACKET_READ_FLUSH , +}; +enum packet_read_status packet_read_with_status (int fd,char **src_buffer,size_t *src_len, +char *buffer,unsigned size,int *pktlen, +int options); +/* * Convenience wrapper for packet_read that is not gentle, and sets the * CHOMP_NEWLINE option. The return value is NULL for a flush packet,
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 01/35] pkt-line: introduce packet_read_with_status
Hi,
Quoted 7 lines · Brandon Williams
The current pkt-line API encodes the status of a pkt-line read in the length of the read content. An error is indicated with '-1', a flush with '0' (which can be confusing since a return value of '0' can also indicate an empty pkt-line), and a positive integer for the length of the read content otherwise. This doesn't leave much room for allowing the addition of additional special packets in the future.
Quoted 5 lines
To solve this introduce '
packet_read_with_status()' which reads a packet and returns the status of the read encoded as an 'enumpacket_status' type. This allows for easily identifying between special and normal packets as well as errors. It also enables easily adding a new special packet in the future.
Makes sense, thanks. Using an enum return value is less opaque, too.
[...]
pkt-line.c static int packet_length(const char *linelen) · line 285
Quoted 11 lines
pkt-line.c +3
-2@@ -280,28 +280,33 @@ static int packet_length(const char *linelen)return (val <0 ) ? val : (val <<8 ) |hex2chr (linelen +2 ); } -int packet_read (int fd,char **src_buf,size_t *src_len, -char *buffer,unsigned size,int options) +enum packet_read_status packet_read_with_status (int fd,char **src_buffer,size_t *src_len, +char *buffer,unsigned size,int *pktlen, +int options)
This function definition straddles two worlds: it is line-wrapped as though there are a limited number of columns, but it goes far past 80 columns.
Can "make style" or a similar tool take care of rewrapping it?
pkt-line.c static int packet_length(const char *linelen) · line 292
Quoted 11 lines
pkt-line.c +4
-4{ -int len, ret; +int len;char linelen[4 ]; - ret =get_packet_data (fd, src_buf, src_len, linelen,4 , options); -if (ret <0 ) -return ret; +if (get_packet_data (fd, src_buffer, src_len, linelen,4 , options) <0 ) +return PACKET_READ_EOF ; +
EOF is indeed the only error that get_packet_data can return.
Could be worth a doc comment on get_packet_data to make that clearer. It's not too important since it's static, though.
pkt-line.c static int packet_length(const char *linelen) · line 299
Quoted 10 lines
pkt-line.c +4
-3len =packet_length (linelen); -if (len <0 ) + +if (len <0 ) {die ("protocol error: bad line length character: %.4s" , linelen); -if (!len) { + }else if (!len) {packet_trace ("0000" ,4 ,0 ); -return 0 ; +return PACKET_READ_FLUSH ;
The advertised change. Makes sense.
[...]
pkt-line.c static int packet_length(const char *linelen) · line 306
Quoted 3 lines
pkt-line.c +1
-1-if (len >= size) +if ((unsigned )len >= size)die ("protocol error: bad line length %d" , len);
The comparison is safe since we just checked that len >= 0.
Is there some static analysis that can make this kind of operation easier?
[...]
Quoted 20 lines
pkt-line.c +15
-1@@ -309,7 +314,31 @@ int packet_read(int fd, char **src_buf, size_t *src_len, buffer[len] =0 ;packet_trace (buffer, len,0 ); -return len; + *pktlen = len; +return PACKET_READ_NORMAL ; +} + +int packet_read (int fd,char **src_buffer,size_t *src_len, +char *buffer,unsigned size,int options) +{ +enum packet_read_status status; +int pktlen; + + status =packet_read_with_status (fd, src_buffer, src_len, + buffer, size, &pktlen, + options); +switch (status) { +case PACKET_READ_EOF :
pkt-line.c int packet_read(int fd, char **src_buf, size_t *src_len, · line 341
Quoted 10 lines
pkt-line.c +10
-0+ pktlen =-1 ; +break ; +case PACKET_READ_NORMAL : +break ; +case PACKET_READ_FLUSH : + pktlen =0 ; +break ; + } + +return pktlen;
nit: can simplify by avoiding the status temporary:
int pktlen;
switch (packet_read_with_status(...)) {
case PACKET_READ_EOF:
return -1;
case PACKET_READ_FLUSH:
return 0;
case PACKET_READ_NORMAL:
return pktlen;
}As a bonus, that lets static analyzers check that the cases are exhaustive. (On the other hand, C doesn't guarantee that an enum can only have the values listed as enumerators. Did we end up figuring out a way to handle that, beyond always including a 'default: BUG()'?)
Quoted 7 lines
pkt-line.h +1
-0@@ -65,6 +65,21 @@ int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);int packet_read (int fd,char **src_buffer,size_t *src_len,char *buffer,unsigned size,int options); +/*
pkt-line.h int write_packetized_from_buf(const char *src_in, size_t len, int fd_out); · line 78
Quoted 10 lines
pkt-line.h +10
-0+* Read a packetized line into a buffer like the 'packet_read()' function but +* returns an 'enum packet_read_status' which indicates the status of the read. +* The number of bytes read will be assigined to *pktlen if the status of the +* read was 'PACKET_READ_NORMAL'. +*/ +enum packet_read_status { +PACKET_READ_EOF =-1 , +PACKET_READ_NORMAL , +PACKET_READ_FLUSH , +};
nit: do any callers treat the return value as a number? It would be less magical if the numbering were left to the compiler (0, 1, 2).
Thanks,
Jonathan
PATCH v3 2/35 pkt-line: introduce struct packet_reader
In reply to Brandon WilliamsSometimes it is advantageous to be able to peek the next packet line without consuming it (e.g. to be able to determine the protocol version a server is speaking). In order to do that introduce 'struct packet_reader' which is an abstraction around the normal packet reading logic. This enables a caller to be able to peek a single line at a time using 'packet_reader_peek()' and having a caller consume a line by calling 'packet_reader_read()'.
--- pkt-line.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pkt-line.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)
pkt-line.c +59 -0
@@ -406,3 +406,62 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) }return sb_out->len - orig_len; } + +/* Packet Reader Functions */ +void packet_reader_init (struct packet_reader *reader,int fd, +char *src_buffer,size_t src_len, +int options) +{ +memset (reader,0 ,sizeof (*reader)); + + reader->fd = fd; + reader->src_buffer = src_buffer; + reader->src_len = src_len; + reader->buffer = packet_buffer; + reader->buffer_size =sizeof (packet_buffer); + reader->options = options; +} + +enum packet_read_status packet_reader_read (struct packet_reader *reader) +{ +if (reader->line_peeked) { + reader->line_peeked =0 ; +return reader->status; + } + + reader->status =packet_read_with_status (reader->fd, + &reader->src_buffer, + &reader->src_len, + reader->buffer, + reader->buffer_size, + &reader->pktlen, + reader->options); + +switch (reader->status) { +case PACKET_READ_EOF : + reader->pktlen =-1 ; + reader->line =NULL ; +break ; +case PACKET_READ_NORMAL : + reader->line = reader->buffer; +break ; +case PACKET_READ_FLUSH : + reader->pktlen =0 ; + reader->line =NULL ; +break ; + } + +return reader->status; +} + +enum packet_read_status packet_reader_peek (struct packet_reader *reader) +{ +/* Only allow peeking a single line */ +if (reader->line_peeked) +return reader->status; + +/* Peek a line by reading it and setting peeked flag */ +packet_reader_read (reader); + reader->line_peeked =1 ; +return reader->status; +}
pkt-line.h +58 -0
64 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 02/35] pkt-line: introduce struct packet_reader
Hi,
Quoted 1 line · Brandon Williams
Subject: pkt-line: introduce struct
packet_reader
nit: this subject line doesn't describe what the purpose/intent behind the patch is. Maybe something like
pkt-line: allow peeking at a packet line without consuming it
would make it clearer.
Quoted 7 lines
Sometimes it is advantageous to be able to peek the next packet line without consuming it (e.g. to be able to determine the protocol version a server is speaking). In order to do that introduce 'struct
packet_reader' which is an abstraction around the normal packet reading logic. This enables a caller to be able to peek a single line at a time using 'packet_reader_peek()' and having a caller consume a line by calling 'packet_reader_read()'.
Makes sense. The packet_reader owns a buffer to support the peek operation and make buffer reuse a little easier.
[...]
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 120
Quoted 13 lines
pkt-line.h +7
-0@@ -111,6 +111,64 @@ char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);*/ ssize_t read_packetized_to_strbuf (int fd_in,struct strbuf *sb_out); +struct packet_reader { +/* source file descriptor */ +int fd; + +/* source buffer and its size */ +char *src_buffer; +size_t src_len;
Can or should this be a strbuf?
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 124
Quoted 4 lines
pkt-line.h +4
-0+ +/* buffer that pkt-lines are read into and its size */ +char *buffer; +unsigned buffer_size;
Likewise.
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 127
Quoted 3 lines
pkt-line.h +3
-0+ +/* options to be used during reads */ +int options;
What option values are possible?
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 130
Quoted 3 lines
pkt-line.h +3
-0+ +/* status of the last read */ +enum packet_read_status status;
This reminds me of FILE*'s status value, ferror, etc. I'm mildly nervous about it --- it encourages a style of error handling where you ignore errors from an individual operation and hope the recorded status later has the most relevant error.
I think it is being used to support peek --- you need to record the error to reply it. Is that right? I wonder if it would make sense to structure as
struct packet_read_result last_line_read;
};
struct packet_read_result {
enum packet_read_status status;
const char *line;
int len;
};What you have here also seems fine. I think what would help most for readability is if the comment mentioned the purpose --- e.g.
/* status of the last read, to support peeking */
Or if the contract were tied to the purpose:
/* status of the last read, only valid if line_peeked is true */
[...]
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 148
Quoted 7 lines
pkt-line.h +7
-0+/* +* Initialize a 'struct packet_reader' object which is an +* abstraction around the 'packet_read_with_status()' function. +*/ +extern void packet_reader_init (struct packet_reader *reader,int fd, +char *src_buffer,size_t src_len, +int options);
This comment doesn't describe how I should use the function. Is the intent to point the reader to packet_read_with_status for more details about the arguments?
Can src_buffer be a const char *?
[...]
Quoted 2 lines
+/*
+ * Perform a packet read and return the status of the read.
nit: s/Perform a packet read/Read one pkt-line/
Quoted 9 lines
+ * The values of 'pktlen' and 'line' are updated based on the status of the
+ * read as follows:
+ *
+ *PACKET_READ_ERROR: 'pktlen' is set to '-1' and 'line' is set to NULL
+ *PACKET_READ_NORMAL: 'pktlen' is set to the number of bytes read
+ * 'line' is set to point at the read line
+ *PACKET_READ_FLUSH: 'pktlen' is set to '0' and 'line' is set to NULL
+ */
+extern enumpacket_read_statuspacket_reader_read(struct packet_reader *reader);
This is reasonable. As described above an alternative would be possible to have a separate packet_read_result output parameter but the interface described here looks pretty easy/pleasant to use.
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 163
Quoted 3 lines
pkt-line.h +3
-0+ +/* +* Peek the next packet line without consuming it and return the status.
nit: s/Peek/Peek at/, or s/Peek/Read/
Quoted 2 lines
+ * The next call to '
packet_reader_read()' will perform a read of the same line
+ * that was peeked, consuming the line.
nit: s/peeked/peeked at/
Quoted 5 lines
+ *
+ * Peeking multiple times without calling 'packet_reader_read()' will return
+ * the same result.
+ */
+extern enumpacket_read_statuspacket_reader_peek(struct packet_reader *reader);
Nice.
[...]
pkt-line.c ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) · line 413
Quoted 11 lines
pkt-line.c +5
-0@@ -406,3 +406,62 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) }return sb_out->len - orig_len; } + +/* Packet Reader Functions */ +void packet_reader_init (struct packet_reader *reader,int fd, +char *src_buffer,size_t src_len, +int options)
This comment looks like it's attached to packet_reader_init, but it's meant to be attached to the whole collection. It's possible that this title-above-multiple-functions won't be maintained, but that's okay.
pkt-line.c ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) · line 421
Quoted 8 lines
pkt-line.c +8
-0+{ +memset (reader,0 ,sizeof (*reader)); + + reader->fd = fd; + reader->src_buffer = src_buffer; + reader->src_len = src_len; + reader->buffer = packet_buffer; + reader->buffer_size =sizeof (packet_buffer);
Looks like this is very non-reentrant. Can the doc comment warn about that? Or even better, can it be made reentrant by owning its own strbuf?
pkt-line.c ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) · line 430
Quoted 9 lines
pkt-line.c +9
-0+ reader->options = options; +} + +enum packet_read_status packet_reader_read (struct packet_reader *reader) +{ +if (reader->line_peeked) { + reader->line_peeked =0 ; +return reader->status; + }
Nice.
Quoted 19 lines
pkt-line.c +19
-0+ + reader->status =packet_read_with_status (reader->fd, + &reader->src_buffer, + &reader->src_len, + reader->buffer, + reader->buffer_size, + &reader->pktlen, + reader->options); + +switch (reader->status) { +case PACKET_READ_EOF : + reader->pktlen =-1 ; + reader->line =NULL ; +break ; +case PACKET_READ_NORMAL : + reader->line = reader->buffer; +break ; +case PACKET_READ_FLUSH : + reader->pktlen =0 ;
pkt-line.c ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) · line 459
Quoted 10 lines
pkt-line.c +10
-0+ reader->line =NULL ; +break ; + } + +return reader->status; +} + +enum packet_read_status packet_reader_peek (struct packet_reader *reader) +{ +/* Only allow peeking a single line */
nit: s/peeking at/
pkt-line.c ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) · line 463
Quoted 4 lines
pkt-line.c +4
-0+if (reader->line_peeked) +return reader->status; + +/* Peek a line by reading it and setting peeked flag */
nit: s/Peek/Peek at/
pkt-line.c ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out) · line 467
Quoted 4 lines
pkt-line.c +4
-0+packet_reader_read (reader); + reader->line_peeked =1 ; +return reader->status; +}
Thanks for a pleasant read.
Jonathan
Re: [PATCH v3 02/35] pkt-line: introduce struct packet_reader
Quoted 17 lines · Jonathan Nieder
[...]
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 120
Quoted 13 lines
pkt-line.h +7
-0@@ -111,6 +111,64 @@ char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);*/ ssize_t read_packetized_to_strbuf (int fd_in,struct strbuf *sb_out); +struct packet_reader { +/* source file descriptor */ +int fd; + +/* source buffer and its size */ +char *src_buffer; +size_t src_len;Can or should this be a strbuf?
Quoted 7 lines
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 124
Quoted 4 lines
pkt-line.h +4
-0+ +/* buffer that pkt-lines are read into and its size */ +char *buffer; +unsigned buffer_size;Likewise.
This struct is setup to be a drop in replacement for the existing read_packet() family of functions. Because of this I tried to make the interface as similar as possible to make it easy to convert to using it as well as having no need to clean anything up (because the struct is really just a wrapper and doesn't own anything).
Signature · 1 line
Brandon Williams
Re: [PATCH v3 02/35] pkt-line: introduce struct packet_reader
Quoted 25 lines · Brandon Williams
Quoted 16 lines · Jonathan Nieder
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 120
Quoted 13 lines
pkt-line.h +7
-0@@ -111,6 +111,64 @@ char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);*/ ssize_t read_packetized_to_strbuf (int fd_in,struct strbuf *sb_out); +struct packet_reader { +/* source file descriptor */ +int fd; + +/* source buffer and its size */ +char *src_buffer; +size_t src_len;Can or should this be a strbuf?
Quoted 6 lines
pkt-line.h char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); · line 124
Quoted 4 lines
pkt-line.h +4
-0+ +/* buffer that pkt-lines are read into and its size */ +char *buffer; +unsigned buffer_size;Likewise.
Quoted 5 lines
This struct is setup to be a drop in replacement for the existing
read_packet()family of functions. Because of this I tried to make the interface as similar as possible to make it easy to convert to using it as well as having no need to clean anything up (because the struct is really just a wrapper and doesn't own anything).
Sorry, I don't completely follow. Are you saying some callers play with the buffer, or are you saying you haven't checked? (If the latter, that's perfectly fine; I'm just trying to understand the API.)
Either way, can you add some comments about ownership / who is allowed to write to it / etc to make it easier to clean up later?
Thanks,
Jonathan
Re: [PATCH v3 02/35] pkt-line: introduce struct packet_reader
In reply to Brandon WilliamsHi,
Quoted 8 lines · Brandon Williams
Sometimes it is advantageous to be able to peek the next packet line without consuming it (e.g. to be able to determine the protocol version a server is speaking). In order to do that introduce 'struct
packet_reader' which is an abstraction around the normal packet reading logic. This enables a caller to be able to peek a single line at a time using 'packet_reader_peek()' and having a caller consume a line by calling 'packet_reader_read()'.
Quoted 5 lines
Signed-off-by Brandon Williams <[email protected]>--- pkt-line.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pkt-line.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)
I like it!
The questions and nits from
https://public-inbox.org/git/[email protected]/
still apply. In particular, the ownership of the buffers inside the 'struct packet_reader' is still unclear; could the packet_reader create its own (strbuf) buffers so that the contract around them (who is allowed to write to them; who is responsible for freeing them) is more obvious?
Thanks,
Jonathan
Re: [PATCH v3 02/35] pkt-line: introduce struct packet_reader
Quoted 16 lines · Jonathan Nieder
Quoted 8 lines · Brandon Williams
Sometimes it is advantageous to be able to peek the next packet line without consuming it (e.g. to be able to determine the protocol version a server is speaking). In order to do that introduce 'struct
packet_reader' which is an abstraction around the normal packet reading logic. This enables a caller to be able to peek a single line at a time using 'packet_reader_peek()' and having a caller consume a line by calling 'packet_reader_read()'.Quoted 5 lines
Signed-off-by Brandon Williams <[email protected]>--- pkt-line.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pkt-line.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)
Quoted 8 lines
I like it!
The questions and nits from
https://public-inbox.org/git/[email protected]/
still apply. In particular, the ownership of the buffers inside the 'structpacket_reader' is still unclear; could thepacket_readercreate its own (strbuf) buffers so that the contract around them (who is allowed to write to them; who is responsible for freeing them) is more obvious?
Just to be clear: I sent that review after you sent this patch, so there should not have been any reason for me to expect the q's and nits to magically not apply. ;-)
PATCH v3 3/35 pkt-line: add delim packet support
In reply to Brandon WilliamsOne of the design goals of protocol-v2 is to improve the semantics of flush packets. Currently in protocol-v1, flush packets are used both to indicate a break in a list of packet lines as well as an indication that one side has finished speaking. This makes it particularly difficult to implement proxies as a proxy would need to completely understand git protocol instead of simply looking for a flush packet.
To do this, introduce the special deliminator packet '0001'. A delim packet can then be used as a deliminator between lists of packet lines while flush packets can be reserved to indicate the end of a response.
--- pkt-line.c | 17 +++++++++++++++++ pkt-line.h | 3 +++ 2 files changed, 20 insertions(+)
pkt-line.c +17 -0
@@ -91,6 +91,12 @@ void packet_flush(int fd)write_or_die (fd,"0000" ,4 ); } +void packet_delim (int fd) +{ +packet_trace ("0001" ,4 ,1 ); +write_or_die (fd,"0001" ,4 ); +} +int packet_flush_gently (int fd) {packet_trace ("0000" ,4 ,1 );
@@ -105,6 +111,12 @@ void packet_buf_flush(struct strbuf *buf)strbuf_add (buf,"0000" ,4 ); } +void packet_buf_delim (struct strbuf *buf) +{ +packet_trace ("0001" ,4 ,1 ); +strbuf_add (buf,"0001" ,4 ); +} +static void set_packet_header (char *buf,const int size) {static char hexchar[] ="0123456789abcdef" ;
@@ -297,6 +309,9 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer, size_ }else if (!len) {packet_trace ("0000" ,4 ,0 );return PACKET_READ_FLUSH ; + }else if (len ==1 ) { +packet_trace ("0001" ,4 ,0 ); +return PACKET_READ_DELIM ; }else if (len <4 ) {die ("protocol error: bad line length %d" , len); }
@@ -333,6 +348,7 @@ int packet_read(int fd, char **src_buffer, size_t *src_len,break ;case PACKET_READ_NORMAL :break ; +case PACKET_READ_DELIM :case PACKET_READ_FLUSH : pktlen =0 ;break ;
@@ -445,6 +461,7 @@ enum packet_read_status packet_reader_read(struct packet_reader *reader)case PACKET_READ_NORMAL : reader->line = reader->buffer;break ; +case PACKET_READ_DELIM :case PACKET_READ_FLUSH : reader->pktlen =0 ; reader->line =NULL ;
pkt-line.h +3 -0
@@ -20,8 +20,10 @@* side can't, we stay with pure read/write interfaces. */ void packet_flush (int fd); +void packet_delim (int fd);void packet_write_fmt (int fd,const char *fmt, ...) __attribute__((format (printf,2 ,3 )));void packet_buf_flush (struct strbuf *buf); +void packet_buf_delim (struct strbuf *buf);void packet_write (int fd_out,const char *buf,size_t size);void packet_buf_write (struct strbuf *buf,const char *fmt, ...) __attribute__((format (printf,2 ,3 )));int packet_flush_gently (int fd);
@@ -75,6 +77,7 @@ enum packet_read_status {PACKET_READ_EOF =-1 ,PACKET_READ_NORMAL ,PACKET_READ_FLUSH , + PACKET_READ_DELIM, }; enum packet_read_status packet_read_with_status(int fd, char **src_buffer, size_t *src_len, char *buffer, unsigned size, int *pktlen,
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 03/35] pkt-line: add delim packet support
Quoted 10 lines · Brandon Williams
One of the design goals of protocol-v2 is to improve the semantics of flush packets. Currently in protocol-v1, flush packets are used both to indicate a break in a list of packet lines as well as an indication that one side has finished speaking. This makes it particularly difficult to implement proxies as a proxy would need to completely understand git protocol instead of simply looking for a flush packet.
To do this, introduce the special deliminator packet '0001'. A delim packet can then be used as a deliminator between lists of packet lines while flush packets can be reserved to indicate the end of a response.
Please mention where this can be found in the documentation. (Defer to later patch?)
As the commit message states, this is only to be used for v2, in v0 it is still an illegal pkt.
Quoted 2 lines
Signed-off-by Brandon Williams <[email protected]>
The code is
Re: [PATCH v3 03/35] pkt-line: add delim packet support
Quoted 8 lines · Stefan Beller
Quoted 7 lines · Brandon Williams
One of the design goals of protocol-v2 is to improve the semantics of flush packets. Currently in protocol-v1, flush packets are used both to indicate a break in a list of packet lines as well as an indication that one side has finished speaking. This makes it particularly difficult to implement proxies as a proxy would need to completely understand git protocol instead of simply looking for a flush packet.
Quoted 6 lines
Quoted 3 lines
To do this, introduce the special deliminator packet '0001'. A delim packet can then be used as a deliminator between lists of packet lines while flush packets can be reserved to indicate the end of a response.
Please mention where this can be found in the documentation. (Defer to later patch?)
Yeah the documentation does get added in a future patch, I'll make a comment to that effect.
Quoted 8 lines
As the commit message states, this is only to be used for v2, in v0 it is still an illegal pkt.
Quoted 2 lines
Signed-off-by Brandon Williams <[email protected]>The code is
Reviewed-by Stefan Beller <[email protected]>
Signature · 1 line
Brandon Williams
PATCH v3 4/35 upload-pack: convert to a builtin
In reply to Brandon WilliamsIn order to allow for code sharing with the server-side of fetch in protocol-v2 convert upload-pack to be a builtin.
--- Makefile | 3 +- builtin.h | 1 + builtin/upload-pack.c | 67 +++++++++++++++++++++++++++++++ git.c | 1 + upload-pack.c | 107 ++++++++++++-------------------------------------- upload-pack.h | 13 ++++++ 6 files changed, 109 insertions(+), 83 deletions(-) create mode 100644 builtin/upload-pack.c create mode 100644 upload-pack.h
Makefile +2 -1
@@ -639,7 +639,6 @@ PROGRAM_OBJS += imap-send.o PROGRAM_OBJS += sh-i18n--envsubst.o PROGRAM_OBJS += shell.o PROGRAM_OBJS += show-index.o -PROGRAM_OBJS += upload-pack.o PROGRAM_OBJS += remote-testsvn.o # Binary suffix, set to .exe for Windows builds
@@ -909,6 +908,7 @@ LIB_OBJS += tree-diff.o LIB_OBJS += tree.o LIB_OBJS += tree-walk.o LIB_OBJS += unpack-trees.o +LIB_OBJS += upload-pack.o LIB_OBJS += url.o LIB_OBJS += urlmatch.o LIB_OBJS += usage.o
@@ -1026,6 +1026,7 @@ BUILTIN_OBJS += builtin/update-index.o BUILTIN_OBJS += builtin/update-ref.o BUILTIN_OBJS += builtin/update-server-info.o BUILTIN_OBJS += builtin/upload-archive.o +BUILTIN_OBJS += builtin/upload-pack.o BUILTIN_OBJS += builtin/var.o BUILTIN_OBJS += builtin/verify-commit.o BUILTIN_OBJS += builtin/verify-pack.o
builtin.h +1 -0
@@ -231,6 +231,7 @@ extern int cmd_update_ref(int argc, const char **argv, const char *prefix);extern int cmd_update_server_info (int argc,const char **argv,const char *prefix);extern int cmd_upload_archive (int argc,const char **argv,const char *prefix);extern int cmd_upload_archive_writer (int argc,const char **argv,const char *prefix); +extern int cmd_upload_pack (int argc,const char **argv,const char *prefix);extern int cmd_var (int argc,const char **argv,const char *prefix);extern int cmd_verify_commit (int argc,const char **argv,const char *prefix);extern int cmd_verify_tag (int argc,const char **argv,const char *prefix);
builtin/upload-pack.c new file +67 -0
67 lines not on this page · raw message
git.c +1 -0
@@ -478,6 +478,7 @@ static struct cmd_struct commands[] = { { "update-server-info", cmd_update_server_info, RUN_SETUP }, { "upload-archive", cmd_upload_archive }, { "upload-archive--writer", cmd_upload_archive_writer }, + { "upload-pack", cmd_upload_pack }, { "var", cmd_var, RUN_SETUP_GENTLY }, { "verify-commit", cmd_verify_commit, RUN_SETUP }, { "verify-pack", cmd_verify_pack },
upload-pack.c +25 -82
145 lines not on this page · raw message
upload-pack.h new file +13 -0
@@ -0,0 +1,13 @@ +#ifndef UPLOAD_PACK_H +#define UPLOAD_PACK_H + +struct upload_pack_options { +int stateless_rpc; +int advertise_refs; +unsigned int timeout; +int daemon_mode; +}; + +void upload_pack (struct upload_pack_options *options); + +#endif /* UPLOAD_PACK_H */
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 4 lines · On Tue, 6 Feb 2018 17:12:41 -0800 Brandon Williams <[email protected]> wrote:
In order to allow for code sharing with the server-side of fetch in protocol-v2 convert upload-pack to be a builtin.
Signed-off-by Brandon Williams <[email protected]>
As Stefan mentioned in [1], also mention in the commit message that this means that the "git-upload-pack" invocation gains additional capabilities (for example, invoking a pager for --help).
Having said that, the main purpose of this patch seems to be to libify upload-pack, and the move to builtin is just a way of putting the program somewhere - we could have easily renamed upload-pack.c and created a new upload-pack.c containing the main(), preserving the non-builtin-ness of upload-pack, while still gaining the benefits of libifying upload-pack.
If the community does want to make upload-pack a builtin, I would write the commit message this way:
upload-pack: libify
Libify upload-pack. The main() function is moved to builtin/upload-pack.c, thus making upload-pack a builtin. Note that this means that "git-upload-pack" gains functionality such as the ability to invoke a pager when passed "--help".
And if not:
upload-pack: libify
Libify upload-pack by moving most of the functionality in upload-pack.c into a file upload-pack-lib.c (or some other name), to be used in subsequent patches.
[1] https://public-inbox.org/git/CAGZ79kb2=uU0_K8wr27gNdNX-T+P+7gVdgc5EBdYc3zBobsR8w@mail.gmail.com/
Quoted 16 lines
upload-pack.c +0
-16-static void upload_pack (void ) -{ -struct string_list symref =STRING_LIST_INIT_DUP ; - -head_ref_namespaced (find_symref, &symref); - -if (advertise_refs || !stateless_rpc) { -reset_timeout (); -head_ref_namespaced (send_ref, &symref); -for_each_namespaced_ref (send_ref, &symref); -advertise_shallow_grafts (1 ); -packet_flush (1 ); - }else { -head_ref_namespaced (check_ref,NULL ); -for_each_namespaced_ref (check_ref,NULL ); - }
upload-pack.c static int find_symref(const char *refname, const struct object_id *oid, · line 1005
Quoted 10 lines
upload-pack.c +0
-10-string_list_clear (&symref,1 ); -if (advertise_refs) -return ; - -receive_needs (); -if (want_obj.nr) { -get_common_commits (); -create_pack_file (); - } -}
I see that this function had to be moved to the bottom because it now also needs to make use of functions like upload_pack_config() - that's fine.
upload-pack.h line 9
Quoted 6 lines
upload-pack.h new file +6
-0+struct upload_pack_options { +int stateless_rpc; +int advertise_refs; +unsigned int timeout; +int daemon_mode; +};
I would have expected "unsigned stateless_rpc : 1" etc., but I see that this makes it easier to use with OPT_BOOL (which needs us to pass it a pointer-to-int).
As for what existing code does, files like fetch-pack and diff use "unsigned : 1", but they also process arguments without OPT_, so I don't think they are relevant.
I think that we should decide if we're going to prefer "unsigned : 1" or "int" for flags in new code. Personally, I prefer "unsigned : 1" (despite the slight inconvenience in that argument parsers will need to declare their own temporary "int" and then assign its contents to the options struct) because of the stronger type, but I'm OK either way. Whatever the decision, I don't think it needs to block the review of this patch set.
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 11 lines · Jonathan Tan
Quoted 4 lines · On Tue, 6 Feb 2018 17:12:41 -0800 Brandon Williams <[email protected]> wrote:
In order to allow for code sharing with the server-side of fetch in protocol-v2 convert upload-pack to be a builtin.
Signed-off-by Brandon Williams <[email protected]>As Stefan mentioned in [1], also mention in the commit message that this means that the "git-upload-pack" invocation gains additional capabilities (for example, invoking a pager for --help).
And possibly respecting pager.upload-pack, which would violate our rule that it is safe to run upload-pack in untrusted repositories.
(This actually doesn't work right now because pager.* is broken for builtins that don't specify RUN_SETUP; but I think with the fixes last year to the config code, we can now drop that restriction).
Obviously we can work around this with an extra RUN_NO_PAGER_CONFIG flag. But I think it points to a general danger in making upload-pack a builtin. I'm not sure what other features it would want to avoid (or what might grow in the future).
Quoted 6 lines
Having said that, the main purpose of this patch seems to be to libify upload-pack, and the move to builtin is just a way of putting the program somewhere - we could have easily renamed
upload-pack.cand created a newupload-pack.ccontaining themain(), preserving the non-builtin-ness of upload-pack, while still gaining the benefits of libifying upload-pack.
Yeah, this seems like a better route to me.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 8 lines · Jeff King
Quoted 6 lines · Jonathan Tan
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:41 -0800 Brandon Williams <[email protected]> wrote:
In order to allow for code sharing with the server-side of fetch in protocol-v2 convert upload-pack to be a builtin.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Signed-off-by Brandon Williams <[email protected]>As Stefan mentioned in [1], also mention in the commit message that this means that the "git-upload-pack" invocation gains additional capabilities (for example, invoking a pager for --help).
And possibly respecting pager.upload-pack, which would violate our rule that it is safe to run upload-pack in untrusted repositories.
And this isn't an issue with receive-pack because this same guarantee doesn't exist?
Quoted 20 lines
(This actually doesn't work right now because pager.* is broken for builtins that don't specify
RUN_SETUP; but I think with the fixes last year to the config code, we can now drop that restriction).Obviously we can work around this with an extra
RUN_NO_PAGER_CONFIGflag. But I think it points to a general danger in making upload-pack a builtin. I'm not sure what other features it would want to avoid (or what might grow in the future).Quoted 6 lines
Having said that, the main purpose of this patch seems to be to libify upload-pack, and the move to builtin is just a way of putting the program somewhere - we could have easily renamed
upload-pack.cand created a newupload-pack.ccontaining themain(), preserving the non-builtin-ness of upload-pack, while still gaining the benefits of libifying upload-pack.Yeah, this seems like a better route to me.
-Peff
Signature · 1 line
Brandon Williams
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 15 lines · Brandon Williams
Quoted 14 lines · Jeff King
Quoted 11 lines · Jonathan Tan
Quoted 4 lines · On Tue, 6 Feb 2018 17:12:41 -0800 Brandon Williams <[email protected]> wrote:
In order to allow for code sharing with the server-side of fetch in protocol-v2 convert upload-pack to be a builtin.
Signed-off-by Brandon Williams <[email protected]>As Stefan mentioned in [1], also mention in the commit message that this means that the "git-upload-pack" invocation gains additional capabilities (for example, invoking a pager for --help).
Quoted 5 lines
Quoted 2 lines
And possibly respecting pager.upload-pack, which would violate our rule that it is safe to run upload-pack in untrusted repositories.
And this isn't an issue with receive-pack because this same guarantee doesn't exist?
Yes, exactly (which is confusing and weird, yes, but that's how it is).
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Hi,
Quoted 8 lines · Jeff King
Quoted 7 lines · Brandon Williams
Quoted 6 lines · Jeff King
Quoted 5 lines · Jonathan Tan
Quoted 2 lines · On Tue, 6 Feb 2018 17:12:41 -0800 Brandon Williams <[email protected]> wrote:
In order to allow for code sharing with the server-side of fetch in protocol-v2 convert upload-pack to be a builtin.
[...]
Quoted 11 lines
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
As Stefan mentioned in [1], also mention in the commit message that this means that the "git-upload-pack" invocation gains additional capabilities (for example, invoking a pager for --help).
And possibly respecting pager.upload-pack, which would violate our rule that it is safe to run upload-pack in untrusted repositories.
And this isn't an issue with receive-pack because this same guarantee doesn't exist?
Yes, exactly (which is confusing and weird, yes, but that's how it is).
To be clear, which of the following are you (most) worried about?
1. being invoked with --help and spawning a pager
2. receiving and acting on options between 'git' and 'upload-pack'
3. repository discovery
4. pager config
5. alias discovery
6. increased code surface / unknown threats
For (1), "--help" has to be the first argument. "git daemon" passes --strict so it doesn't happen there. "git http-backend" passes --stateless-rpc so it doesn't happen there. "git shell" sanitizes input to avoid it happening there.
A custom setup could provide their own entry point that doesn't do such sanitization. I think that in some sense it's out of our hands, but it would be nice to harden as described upthread.
For (2), I am having trouble imagining a setup where it would happen.
upload-pack doesn't have the RUN_SETUP or RUN_SETUP_GENTLY flag set, so (3) doesn't apply.
Although in most setups the user does not control the config files on a server, item (4) looks like a real issue worth solving. I think we should introduce a flag to skip looking for pager config. We could use it for receive-pack, too.
Builtins are handled before aliases, so (5) doesn't apply.
(6) is a real issue: it is why "git shell" is not a builtin, for example. But I would rather that we use appropriate sanitization before upload-pack is invoked than live in fear. git upload-pack is sufficiently complicated that I don't think the git.c wrapper increases the complexity by a significant amount.
That leaves me with a personal answer of only being worried about (4) and not the rest. What do you think? Is one of the other items I listed above worrisome, or is there another item I missed?
Thanks,
Jonathan
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 8 lines · Jonathan Nieder
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
And possibly respecting pager.upload-pack, which would violate our rule that it is safe to run upload-pack in untrusted repositories.
And this isn't an issue with receive-pack because this same guarantee doesn't exist?
Yes, exactly (which is confusing and weird, yes, but that's how it is).
Quoted 8 lines
To be clear, which of the following are you (most) worried about?
1. being invoked with --help and spawning a pager
2. receiving and acting on options between 'git' and 'upload-pack'
3. repository discovery
4. pager config
5. alias discovery
6. increased code surface / unknown threats
My immediate concern is (4). But my greater concern is that people who work on git.c should not have to worry about accidentally violating this principle when they add a new feature or config option.
In other words, it seems like an accident waiting to happen. I'd be more amenable to it if there was some compelling reason for it to be a builtin, but I don't see one listed in the commit message. I see only "let's make it easier to share the code", which AFAICT is equally served by just lib-ifying the code and calling it from the standalone upload-pack.c.
Quoted 4 lines
Although in most setups the user does not control the config files on a server, item (4) looks like a real issue worth solving. I think we should introduce a flag to skip looking for pager config. We could use it for receive-pack, too.
There's not much point for receive-pack. It respects hooks, so any security ship has already sailed there.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 13 lines · Jeff King
Quoted 8 lines
To be clear, which of the following are you (most) worried about?
1. being invoked with --help and spawning a pager
2. receiving and acting on options between 'git' and 'upload-pack'
3. repository discovery
4. pager config
5. alias discovery
6. increased code surface / unknown threatsMy immediate concern is (4). But my greater concern is that people who work on
git.cshould not have to worry about accidentally violating this principle when they add a new feature or config option.
Quoted 6 lines
In other words, it seems like an accident waiting to happen. I'd be more amenable to it if there was some compelling reason for it to be a builtin, but I don't see one listed in the commit message. I see only "let's make it easier to share the code", which AFAICT is equally served by just lib-ifying the code and calling it from the standalone
upload-pack.c.
By the way, any decision here would presumably need to be extended to git-serve, etc. The current property is that it's safe to fetch from an untrusted repository, even over ssh. If we're keeping that for protocol v1, we'd want it to apply to protocol v2, as well.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 3 lines · Jeff King
The current property is that it's safe to fetch from an
untrusted repository, even over ssh. If we're keeping that for protocol v1, we'd want it to apply to protocol v2, as well.
Ah, this is what I had been missing (the non-ssh case).
I see your point. I think we need to fix the pager config issue and add some clarifying documentation to git.c so that people know what to look out for.
Keep in mind that git upload-archive (a read-only command, just like git upload-pack) also already has the same issues.
Thanks,
Jonathan
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 2 lines · Jonathan Nieder
Keep in mind that git upload-archive (a read-only command, just like git upload-pack) also already has the same issues.
Yuck. I don't think we've ever made a historical promise about that. But then, I don't think the promise about upload-pack has ever really been documented, except in mailing list discussions.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Hi,
Quoted 8 lines · Jeff King
Quoted 2 lines · Jonathan Nieder
Keep in mind that git upload-archive (a read-only command, just like git upload-pack) also already has the same issues.
Yuck. I don't think we've ever made a historical promise about that. But then, I don't think the promise about upload-pack has ever really been documented, except in mailing list discussions.
Sorry to revive this old side-thread. Good news: for a dashed command like git-upload-archive, the pager selection code only runs for commands with RUN_SETUP or RUN_SETUP_GENTLY:
if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) && !(p->option & DELAY_PAGER_CONFIG)) use_pager = check_pager_config(p->cmd);
None of upload-pack, receive-pack,git-serve, or upload-archive set those flags, so we (narrowly) escape trouble here.
Later today I should be able to send a cleanup to make the behavior more obvious.
Thanks again,
Jonathan
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 16 lines · Jonathan Nieder
Hi,
Quoted 8 lines · Jeff King
Quoted 2 lines · Jonathan Nieder
Keep in mind that git upload-archive (a read-only command, just like git upload-pack) also already has the same issues.
Yuck. I don't think we've ever made a historical promise about that. But then, I don't think the promise about upload-pack has ever really been documented, except in mailing list discussions.
Sorry to revive this old side-thread. Good news: for a dashed command like git-upload-archive, the pager selection code only runs for commands with
RUN_SETUPorRUN_SETUP_GENTLY:
Quoted 6 lines
if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) && !(p->option & DELAY_PAGER_CONFIG)) use_pager = check_pager_config(p->cmd);None of upload-pack, receive-pack,git-serve, or upload-archive set those flags, so we (narrowly) escape trouble here.
Right, I saw that earlier. But I actually think that is stale from the days when it wasn't safe to call check_pager_config() too early. So I could very well see somebody removing it and causing a spooky vulnerability at a distance.
Quoted 2 lines
Later today I should be able to send a cleanup to make the behavior more obvious.
Thanks. I'm still on the fence over the whole builtin concept, but certainly a "don't ever turn on a pager" flag seems like a reasonable thing to have.
An alternative approach is some kind of global for "don't trust the local repo" flag. That could be respected from very low-level code (e.g., where we read and/or respect the pager command, but also in other places like hooks, other config that runs arbitrary commands, etc). And then upload-pack would set that to "do not trust", and other programs would default to "trust".
We could even give it an environment variable, which would allow something like:
tar xf maybe-evil.git.tar
cd maybe-evil
export GIT_TRUST_REPO=false
git log
without worrying about pager.log config, etc. My two concerns with this approach would be:
1. We have to manually annotate any "dangerous" code to act more safely when it sees the flag. Which means it's highly likely to a spot, or to add a new feature which doesn't respect it. And suddenly that's a security hole. So I'm concerned it may create a false sense of security and actually make things worse.
2. As a global, I'm not sure how it would interact with multi-repo processes like submodules. In theory it ought to go into the repository struct, but it would often need to be set globally before we've even discovered the repo.
That might be fine, though. It's really more about context than about a specific repo (so you may say "don't trust this repo", and that extends to any submodules you happen to access, too).
I dunno. I think (2) is probably OK, but (1) really gives me pause.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 7 lines · Jeff King
We could even give it an environment variable, which would allow something like:
tar xf maybe-evil.git.tar
cd maybe-evil
export GIT_TRUST_REPO=false
git log
Interesting idea. Putting it in an envvar means it gets inherited by child processes, which if I understand you correctly is a good thing.
[...]
Quoted 5 lines
1. We have to manually annotate any "dangerous" code to act more safely when it sees the flag. Which means it's highly likely to a spot, or to add a new feature which doesn't respect it. And suddenly that's a security hole. So I'm concerned it may create a false sense of security and actually make things worse.
As an internal implementation detail, this is so obviously fragile that it wouldn't give me any feeling of security. ;-) So it should be strictly an improvement.
As a public-facing feature, I suspect it's a bad idea for exactly that reason.
FWIW for pager specifically I am going for a whitelisting approach:
new commands would have to explicitly set ALLOW_PAGER if they want to respect pager config. That doesn't guarantee people think about it again as things evolve but it should at least help with getting the right setting for new plumbing.
Thanks,
Jonathan
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 9 lines · Jonathan Nieder
Quoted 7 lines · Jeff King
We could even give it an environment variable, which would allow something like:
tar xf maybe-evil.git.tar
cd maybe-evil
export GIT_TRUST_REPO=false
git log
Quoted 7 lines
[...]
As an internal implementation detail, this is so obviously fragile that it wouldn't give me any feeling of security. ;-) So it should be strictly an improvement.As a public-facing feature, I suspect it's a bad idea for exactly that reason.
So that pretty much kills off the GIT_TRUST_REPO idea, I guess.
Quoted 5 lines
FWIW for pager specifically I am going for a whitelisting approach:
new commands would have to explicitly setALLOW_PAGERif they want to respect pager config. That doesn't guarantee people think about it again as things evolve but it should at least help with getting the right setting for new plumbing.
I suspect we'd be about as well off with the "don't trust the repo" internal flag. Touching the ALLOW_PAGER setup code is about as likely to set off red flags for the developers (or reviewers) as code that checks the "trust" flag.
Forcing a whitelist on ALLOW_PAGER _is_ more likely to catch people adding new commands. But I don't think we actually want to add more commands to the "safe to run in a malicious repo" list. It's already a slightly sketchy concept. This is really all about upload-pack and its existing promises.
But ALLOW_PAGER would _just_ fix the pager issue. When we inevitably find another problem spot, it won't help us there. But a global "trust" flag might.
I dunno. I guess I'm OK with either approach, but it seems like the global trust flag has more room to grow.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
In reply to Jeff KingQuoted 22 lines · Jeff King
Quoted 13 lines · Jeff King
Quoted 8 lines
To be clear, which of the following are you (most) worried about?
1. being invoked with --help and spawning a pager
2. receiving and acting on options between 'git' and 'upload-pack'
3. repository discovery
4. pager config
5. alias discovery
6. increased code surface / unknown threatsMy immediate concern is (4). But my greater concern is that people who work on
git.cshould not have to worry about accidentally violating this principle when they add a new feature or config option.Quoted 6 lines
In other words, it seems like an accident waiting to happen. I'd be more amenable to it if there was some compelling reason for it to be a builtin, but I don't see one listed in the commit message. I see only "let's make it easier to share the code", which AFAICT is equally served by just lib-ifying the code and calling it from the standalone
upload-pack.c.
Quoted 6 lines
By the way, any decision here would presumably need to be extended to git-serve, etc. The current property is that it's safe to fetch from an untrusted repository, even over ssh. If we're keeping that for protocol v1, we'd want it to apply to protocol v2, as well.
-Peff
This may be more complicated. Right now (for backward compatibility) all fetches for v2 are issued to the upload-pack endpoint. So even though I've introduced git-serve it doesn't have requests issued to it and no requests can be issued to it currently (support isn't added to http-backend or git-daemon). This just means that the command already exists to make it easy for testing specific v2 stuff and if we want to expose it as an endpoint (like when we have a brand new server command that is completely incompatible with v1) its already there and support just needs to be plumbed in.
This whole notion of treating upload-pack differently from receive-pack has bad consequences for v2 though. The idea for v2 is to be able to run any number of commands via the same endpoint, so at the end of the day the endpoint you used is irrelevant. So you could issue both fetch and push commands via the same endpoint in v2 whether its git-serve, receive-pack, or upload-pack. So really, like Jonathan has said elsewhere, we need to figure out how to be ok with having receive-pack and upload-pack builtins, or having neither of them builtins, because it doesn't make much sense for v2 to straddle that line. I mean you could do some complicated advertising of commands based on the endpoint you hit, but then what does that mean if you're hitting the git-serve endpoint where you should presumably be able to do any operation.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 15 lines · Brandon Williams
Quoted 4 lines
By the way, any decision here would presumably need to be extended to git-serve, etc. The current property is that it's safe to fetch from an untrusted repository, even over ssh. If we're keeping that for protocol v1, we'd want it to apply to protocol v2, as well.
This may be more complicated. Right now (for backward compatibility) all fetches for v2 are issued to the upload-pack endpoint. So even though I've introduced git-serve it doesn't have requests issued to it and no requests can be issued to it currently (support isn't added to http-backend or git-daemon). This just means that the command already exists to make it easy for testing specific v2 stuff and if we want to expose it as an endpoint (like when we have a brand new server command that is completely incompatible with v1) its already there and support just needs to be plumbed in.
Quoted 9 lines
This whole notion of treating upload-pack differently from receive-pack has bad consequences for v2 though. The idea for v2 is to be able to run any number of commands via the same endpoint, so at the end of the day the endpoint you used is irrelevant. So you could issue both fetch and push commands via the same endpoint in v2 whether its git-serve, receive-pack, or upload-pack. So really, like Jonathan has said elsewhere, we need to figure out how to be ok with having receive-pack and upload-pack builtins, or having neither of them builtins, because it doesn't make much sense for v2 to straddle that line.
It seems like it would be OK if the whole code path of git-serve invoking upload-pack happened without being a builtin, even if it would be possible to run a builtin receive-pack from that same (non-builtin) git-serve.
Remember that the client is driving the whole operation here, and we can assume that git-serve is operating on the client's behalf. So a client who chooses not to trigger receive-pack would be fine.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
In reply to Jeff KingHi,
Quoted 4 lines · Jeff King
Quoted 2 lines · Jonathan Nieder
To be clear, which of the following are you (most) worried about?
Quoted 8 lines
Quoted 6 lines
1. being invoked with --help and spawning a pager
2. receiving and acting on options between 'git' and 'upload-pack'
3. repository discovery
4. pager config
5. alias discovery
6. increased code surface / unknown threatsMy immediate concern is (4).
Thanks for clarifying.
Quoted 3 lines
But my greater concern is that people who
work ongit.cshould not have to worry about accidentally violating this principle when they add a new feature or config option.
That sounds like a combination of (6) and insufficient documentation or tests. Ideas for how we can help prevent such accidents?
Quoted 6 lines
In other words, it seems like an accident waiting to happen. I'd be more amenable to it if there was some compelling reason for it to be a builtin, but I don't see one listed in the commit message. I see only "let's make it easier to share the code", which AFAICT is equally served by just lib-ifying the code and calling it from the standalone
upload-pack.c.
If we have so little control of the common code used by git commands that could be invoked by a remote user, I think we're in trouble already. I don't think being a builtin vs not makes that significantly different, since there are plenty of builtins that can be triggered by remote users. Further, if we have so little control over the security properties of git.c, what hope do we have of making the rest of libgit.a usable in secure code?
In other words, having to pay more attention to the git wrapper from a security pov actually feels to me like a *good* thing. The git wrapper is the entry point to almost all git commands. If it is an accident waiting to happen, then anything that calls git commands is already an accident waiting to happen. So how can we make it not an accident waiting to happen? :)
Quoted 7 lines
Quoted 4 lines
Although in most setups the user does not control the config files on a server, item (4) looks like a real issue worth solving. I think we should introduce a flag to skip looking for pager config. We could use it for receive-pack, too.
There's not much point for receive-pack. It respects hooks, so any security ship has already sailed there.
Yet there are plenty of cases where people who can push are not supposed to have root privilege. I am not worried about hooks specifically (although the changes described at [1] might help and I still plan to work on those) but I am worried about e.g. commandline injection issues. I don't think we can treat receive-pack as out of scope.
And to be clear, I don't think you were saying receive-pack *is* out of scope. But you seem to be trying to draw some boundary, where I only see something fuzzier (e.g. if a bug only applies to receive-pack, then that certainly decreases its impact, but it doesn't make the impact go away).
Thanks,
Jonathan
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 6 lines · Jonathan Nieder
Quoted 3 lines
But my greater concern is that people who
work ongit.cshould not have to worry about accidentally violating this principle when they add a new feature or config option.That sounds like a combination of (6) and insufficient documentation or tests. Ideas for how we can help prevent such accidents?
I don't think it's insufficient tests. How can we test against some problem in the untrusted repository when the feature that would trigger it has not been written yet?
E.g., imagine we begin to allow alias.* to override command names in git.c. Now suddenly setting "alias.upload-pack" is a vulnerability. Should we add a test for that _now_ as a precaution? I don't think so, because we can't guess what new features are going to be added. So we'd be lucky if such a test actually did anything useful.
A comment could help, but it seems quite likely that whatever feature somebody is adding might not be right next to that comment (and thus not seen). I think we're mostly relying on institutional knowledge during review to catch these things. Which is not great, but I'm not sure where we'd document that knowledge that people would actually see it at the right time.
Quoted 7 lines
Quoted 6 lines
In other words, it seems like an accident waiting to happen. I'd be more amenable to it if there was some compelling reason for it to be a builtin, but I don't see one listed in the commit message. I see only "let's make it easier to share the code", which AFAICT is equally served by just lib-ifying the code and calling it from the standalone
upload-pack.c.
Quoted 7 lines
If we have so little control of the common code used by git commands that could be invoked by a remote user, I think we're in trouble already. I don't think being a builtin vs not makes that significantly different, since there are plenty of builtins that can be triggered by remote users. Further, if we have so little control over the security properties of
git.c, what hope do we have of making the rest of libgit.a usable in secure code?
I agree that the situation is already pretty dicey. But I also think that using the git wrapper is more risky than the rest of libgit.a. There's tons of dangerous code in libgit.a, but upload-pack is smart enough not to call it. And people modifying upload-pack have a greater chance of thinking about the security implications, because they know they're working with upload-pack. Whereas people are likely to touch git.c without considering upload-pack at all.
The big danger in libgit.a is from modifying some low-level code called by upload-pack in a way that trusts the on-disk contents more than it should. My gut says that's less likely, though certainly not impossible (a likely candidate would perhaps be a ref backend config that opens up holes; e.g., if you could point a database backend at some random path).
Quoted 6 lines
In other words, having to pay more attention to the git wrapper from a security pov actually feels to me like a *good* thing. The git wrapper is the entry point to almost all git commands. If it is an accident waiting to happen, then anything that calls git commands is already an accident waiting to happen. So how can we make it not an accident waiting to happen? :)
But I don't think it _is_ an accident waiting to happen for the rest of the commands. upload-pack is special. The point is that people may touch git.c thinking they are adding a nice new feature (like pager config, or aliases, or default options, or whatever). And it _would_ be a nice new feature for most commands, but not for upload-pack, because its requirements are different.
So thinking about security in the git wrapper is just a burden for those other commands.
Quoted 10 lines
Quoted 2 lines
There's not much point for receive-pack. It respects hooks, so any security ship has already sailed there.
Yet there are plenty of cases where people who can push are not supposed to have root privilege. I am not worried about hooks specifically (although the changes described at [1] might help and I still plan to work on those) but I am worried about e.g. commandline injection issues. I don't think we can treat receive-pack as out of scope.
Quoted 5 lines
And to be clear, I don't think you were saying receive-pack *is* out of scope. But you seem to be trying to draw some boundary, where I only see something fuzzier (e.g. if a bug only applies to receive-pack, then that certainly decreases its impact, but it doesn't make the impact go away).
Right, I think command-line injection is a separate issue. My concern is _just_ about "can we be run against on-disk repo contents". And nothing matters for receive-pack there, because you can already execute arbitrary code with hooks.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 9 lines · Jeff King
But I don't think it _is_ an accident waiting to happen for the rest of the commands. upload-pack is special. The point is that people may touch
git.cthinking they are adding a nice new feature (like pager config, or aliases, or default options, or whatever). And it _would_ be a nice new feature for most commands, but not for upload-pack, because its requirements are different.So thinking about security in the git wrapper is just a burden for those other commands.
All of that said, I think the current code is quite dangerous already, and maybe even broken. upload-pack may run sub-commands like rev-list or pack-objects, which are themselves builtins.
For example:
git init --bare evil.git git -C evil.git --work-tree=. commit --allow-empty -m foo git -C evil.git config pager.pack-objects 'echo >&2 oops' git clone --no-local evil.git victim
That doesn't _quite_ work, because we route pack-objects' stderr into a pipe, which suppresses the pager. But we don't for rev-list, which we call when checking reachability. It's a bit tricky to get a client to trigger those for a vanilla fetch, though. Here's the best I could come up with:
git init --bare evil.git git -C evil.git --work-tree=. commit --allow-empty -m one git -C evil.git config pager.rev-list 'echo >&2 oops'
git init super
(
cd super
# obviously use host:path if you're attacking somebody over ssh git submodule add ../evil.git evil
git commit -am 'add evil submodule'
)
git -C evil.git config uploadpack.allowReachableSHA1InWant true git -C evil.git update-ref -d refs/heads/master
git clone --recurse-submodules super victim
I couldn't quite get it to work, but I think it's because I'm doing something wrong with the submodules. But I also think this attack would _have_ to be done over ssh, because on a local system the submodule clone would a hard-link rather than a real fetch.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 3 lines · Jeff King
All of that said, I think the current code is quite dangerous already, and maybe even broken. upload-pack may run sub-commands like rev-list or pack-objects, which are themselves builtins.
Sounds like more commands to set the IGNORE_PAGER_CONFIG flag for in git.c.
Thanks for looking this over thoughtfully.
[...]
Quoted 4 lines
I couldn't quite get it to work, but I think it's because I'm doing something wrong with the submodules. But I also think this attack would _have_ to be done over ssh, because on a local system the submodule clone would a hard-link rather than a real fetch.
What happens if the submodule URL starts with file://?
Thanks,
Jonathan
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 6 lines · Jonathan Nieder
Quoted 4 lines
I couldn't quite get it to work, but I think it's because I'm doing something wrong with the submodules. But I also think this attack would _have_ to be done over ssh, because on a local system the submodule clone would a hard-link rather than a real fetch.
What happens if the submodule URL starts with file://?
Ah, that would do it. Or I guess any follow-up fetch.
I'm still having trouble convincing submodules to fetch _just_ the desired sha1, though. It always just fetches everything. I know there's a way that this kicks in (that's why we have things like allowReachableSHA1InWant), but I'm not sufficiently well-versed in submodules to know how to trigger it.
-Peff
Re: [PATCH v3 04/35] upload-pack: convert to a builtin
Quoted 9 lines · Jeff King
Quoted 6 lines · Jonathan Nieder
Quoted 4 lines
I couldn't quite get it to work, but I think it's because I'm doing something wrong with the submodules. But I also think this attack would _have_ to be done over ssh, because on a local system the submodule clone would a hard-link rather than a real fetch.
What happens if the submodule URL starts with file://?
Quoted 7 lines
Ah, that would do it. Or I guess any follow-up fetch.
I'm still having trouble convincing submodules to fetch _just_ the desired sha1, though. It always just fetches everything. I know there's a way that this kicks in (that's why we have things like allowReachableSHA1InWant), but I'm not sufficiently well-versed in submodules to know how to trigger it.
<facepalm> This won't work anyway. I was right when I said that we don't redirect stderr for rev-list, but of course it's stdout that determines the pager behavior. So I don't think you could get rev-list to trigger a pager here.
I don't think there's currently any vulnerability, but it's more to do with luck than any amount of carefulness on our part.
-Peff
PATCH v3 5/35 upload-pack: factor out processing lines
In reply to Brandon WilliamsFactor out the logic for processing shallow, deepen, deepen_since, and deepen_not lines into their own functions to simplify the 'receive_needs()' function in addition to making it easier to reuse some of this logic when implementing protocol_v2.
--- upload-pack.c | 113 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 39 deletions(-)
upload-pack.c +74 -39
@@ -724,6 +724,75 @@ static void deepen_by_rev_list(int ac, const char **av,packet_flush (1 ); } +static int process_shallow (const char *line,struct object_array *shallows) +{ +const char *arg; +if (skip_prefix (line,"shallow " , &arg)) { +struct object_id oid; +struct object *object; +if (get_oid_hex (arg, &oid)) +die ("invalid shallow line: %s" , line); + object =parse_object (&oid); +if (!object) +return 1 ; +if (object->type !=OBJ_COMMIT ) +die ("invalid shallow object %s" ,oid_to_hex (&oid)); +if (!(object->flags &CLIENT_SHALLOW )) { + object->flags |=CLIENT_SHALLOW ; +add_object_array (object,NULL , shallows); + } +return 1 ; + } + +return 0 ; +} + +static int process_deepen (const char *line,int *depth) +{ +const char *arg; +if (skip_prefix (line,"deepen " , &arg)) { +char *end =NULL ; + *depth = (int )strtol (arg, &end,0 ); +if (!end || *end || *depth <=0 ) +die ("Invalid deepen: %s" , line); +return 1 ; + } + +return 0 ; +} + +static int process_deepen_since (const char *line,timestamp_t *deepen_since,int *deepen_rev_list) +{ +const char *arg; +if (skip_prefix (line,"deepen-since " , &arg)) { +char *end =NULL ; + *deepen_since =parse_timestamp (arg, &end,0 ); +if (!end || *end || !deepen_since || +/* revisions.c's max_age -1 is special */ + *deepen_since ==-1 ) +die ("Invalid deepen-since: %s" , line); + *deepen_rev_list =1 ; +return 1 ; + } +return 0 ; +} + +static int process_deepen_not (const char *line,struct string_list *deepen_not,int *deepen_rev_list) +{ +const char *arg; +if (skip_prefix (line,"deepen-not " , &arg)) { +char *ref =NULL ; +struct object_id oid; +if (expand_ref (arg,strlen (arg), &oid, &ref) !=1 ) +die ("git upload-pack: ambiguous deepen-not: %s" , line); +string_list_append (deepen_not, ref); +free (ref); + *deepen_rev_list =1 ; +return 1 ; + } +return 0 ; +} +static void receive_needs (void ) {struct object_array shallows =OBJECT_ARRAY_INIT ;
@@ -745,49 +814,15 @@ static void receive_needs(void)if (!line)break ; -if (skip_prefix (line,"shallow " , &arg)) { -struct object_id oid; -struct object *object; -if (get_oid_hex (arg, &oid)) -die ("invalid shallow line: %s" , line); - object =parse_object (&oid); -if (!object) -continue ; -if (object->type !=OBJ_COMMIT ) -die ("invalid shallow object %s" ,oid_to_hex (&oid)); -if (!(object->flags &CLIENT_SHALLOW )) { - object->flags |=CLIENT_SHALLOW ; -add_object_array (object,NULL , &shallows); - } +if (process_shallow (line, &shallows))continue ; - } -if (skip_prefix (line,"deepen " , &arg)) { -char *end =NULL ; - depth =strtol (arg, &end,0 ); -if (!end || *end || depth <=0 ) -die ("Invalid deepen: %s" , line); +if (process_deepen (line, &depth))continue ; - } -if (skip_prefix (line,"deepen-since " , &arg)) { -char *end =NULL ; - deepen_since =parse_timestamp (arg, &end,0 ); -if (!end || *end || !deepen_since || -/* revisions.c's max_age -1 is special */ - deepen_since ==-1 ) -die ("Invalid deepen-since: %s" , line); - deepen_rev_list =1 ; +if (process_deepen_since (line, &deepen_since, &deepen_rev_list))continue ; - } -if (skip_prefix (line,"deepen-not " , &arg)) { -char *ref =NULL ; -struct object_id oid; -if (expand_ref (arg,strlen (arg), &oid, &ref) !=1 ) -die ("git upload-pack: ambiguous deepen-not: %s" , line); -string_list_append (&deepen_not, ref); -free (ref); - deepen_rev_list =1 ; +if (process_deepen_not (line, &deepen_not, &deepen_rev_list))continue ; - } +if (!skip_prefix (line,"want " , &arg) ||get_oid_hex (arg, &oid_buf)) die("git upload-pack: protocol error, "
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 05/35] upload-pack: factor out processing lines
Quoted 6 lines · Brandon Williams
Factor out the logic for processing shallow, deepen,
deepen_since, anddeepen_notlines into their own functions to simplify the 'receive_needs()' function in addition to making it easier to reuse some of this logic when implementingprotocol_v2.Signed-off-by Brandon Williams <[email protected]>
for the stated purpose of just refactoring existing code for better reuse later.
I do have a few comments on the code in general, which might be out of scope for this series.
A close review would have been fastest if we had some sort of
https://public-inbox.org/git/[email protected]/
which I might revive soon for this purpose. (it showed that I would need it)
Quoted 1 line
+ *depth = (int)strtol(arg, &end, 0);
strtol is not used quite correctly here IMHO, as we do not inspect errno for ERANGE
Thanks,
Stefan
Re: [PATCH v3 05/35] upload-pack: factor out processing lines
Quoted 6 lines · Stefan Beller
Quoted 5 lines · Brandon Williams
Factor out the logic for processing shallow, deepen,
deepen_since, anddeepen_notlines into their own functions to simplify the 'receive_needs()' function in addition to making it easier to reuse some of this logic when implementingprotocol_v2.
Quoted 7 lines
Quoted 1 line
Signed-off-by Brandon Williams <[email protected]>Reviewed-by Stefan Beller <[email protected]>for the stated purpose of just refactoring existing code for better reuse later.
I do have a few comments on the code in general, which might be out of scope for this series.
Yeah you mentioned some comments in a previous round based on style preference. I'm going to refrain from changing the style of this patch since it is a matter of preference.
Quoted 13 lines
A close review would have been fastest if we had some sort of
https://public-inbox.org/git/[email protected]/
which I might revive soon for this purpose. (it showed that I would need it)Quoted 1 line
+ *depth = (int)strtol(arg, &end, 0);
strtol is not used quite correctly here IMHO, as we do not inspect errno for ERANGE
Thanks,
Stefan
Signature · 1 line
Brandon Williams
PATCH v3 6/35 transport: use get_refs_via_connect to get refs
In reply to Brandon WilliamsRemove code duplication and use the existing 'get_refs_via_connect()' function to retrieve a remote's heads in 'fetch_refs_via_pack()' and 'git_transport_push()'.
--- transport.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
transport.c +4 -14
@@ -230,12 +230,8 @@ static int fetch_refs_via_pack(struct transport *transport, args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; -if (!data->got_remote_heads) { -connect_setup (transport,0 ); -get_remote_heads (data->fd[0 ],NULL ,0 , &refs_tmp,0 , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) + refs_tmp =get_refs_via_connect (transport,0 ); refs = fetch_pack(&args, data->fd, data->conn, refs_tmp ? refs_tmp : transport->remote_refs,
@@ -541,14 +537,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_restruct send_pack_args args;int ret; -if (!data->got_remote_heads) { -struct ref *tmp_refs; -connect_setup (transport,1 ); - -get_remote_heads (data->fd[0 ],NULL ,0 , &tmp_refs,REF_NORMAL , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) +get_refs_via_connect (transport,1 );memset (&args,0 ,sizeof (args)); args.send_mirror = !!(flags &TRANSPORT_PUSH_MIRROR );
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 06/35] transport: use get_refs_via_connect to get refs
Quoted 8 lines · Brandon Williams
Remove code duplication and use the existing '
get_refs_via_connect()' function to retrieve a remote's heads in 'fetch_refs_via_pack()' and 'git_transport_push()'.Signed-off-by Brandon Williams <[email protected]>--- transport.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
I like the diffstat.
[...]
transport.c static int fetch_refs_via_pack(struct transport *transport, · line 234
Quoted 13 lines
+++
b/transport.ctransport.c +2
-6@@ -230,12 +230,8 @@ static int fetch_refs_via_pack(struct transport *transport, args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; -if (!data->got_remote_heads) { -connect_setup (transport,0 ); -get_remote_heads (data->fd[0 ],NULL ,0 , &refs_tmp,0 , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) + refs_tmp =get_refs_via_connect (transport,0 );
The only difference between the old and new code is that the old code passes NULL as 'extra_have' and the new code passes &data->extra_have.
That means this populates the data->extra_have oid_array. Does it matter?
Quoted 4 lines
transport.c +0
-0@@ -541,14 +537,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_restruct send_pack_args args;int ret;
transport.c static int git_transport_push(struct transport *transport, struct ref *remote_re · line 541
Quoted 10 lines
transport.c +2
-8-if (!data->got_remote_heads) { -struct ref *tmp_refs; -connect_setup (transport,1 ); - -get_remote_heads (data->fd[0 ],NULL ,0 , &tmp_refs,REF_NORMAL , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) +get_refs_via_connect (transport,1 );
not a new problem, just curious: Does this leak tmp_refs?
Same question as the other caller about whether we mind getting extra_have populated.
Thanks,
Jonathan
Re: [PATCH v3 06/35] transport: use get_refs_via_connect to get refs
Quoted 38 lines · Jonathan Nieder
Quoted 8 lines · Brandon Williams
Remove code duplication and use the existing '
get_refs_via_connect()' function to retrieve a remote's heads in 'fetch_refs_via_pack()' and 'git_transport_push()'.Signed-off-by Brandon Williams <[email protected]>--- transport.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)I like the diffstat.
[...]
transport.c static int fetch_refs_via_pack(struct transport *transport, · line 234
Quoted 13 lines
+++
b/transport.ctransport.c +2
-6@@ -230,12 +230,8 @@ static int fetch_refs_via_pack(struct transport *transport, args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; -if (!data->got_remote_heads) { -connect_setup (transport,0 ); -get_remote_heads (data->fd[0 ],NULL ,0 , &refs_tmp,0 , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) + refs_tmp =get_refs_via_connect (transport,0 );The only difference between the old and new code is that the old code passes NULL as '
extra_have' and the new code passes&data->extra_have.That means this populates the
data->extra_haveoid_array. Does it matter?Quoted 4 lines
transport.c +0
-0@@ -541,14 +537,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_restruct send_pack_args args;int ret;
Quoted 12 lines
transport.c static int git_transport_push(struct transport *transport, struct ref *remote_re · line 541
Quoted 10 lines
transport.c +2
-8-if (!data->got_remote_heads) { -struct ref *tmp_refs; -connect_setup (transport,1 ); - -get_remote_heads (data->fd[0 ],NULL ,0 , &tmp_refs,REF_NORMAL , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) +get_refs_via_connect (transport,1 );not a new problem, just curious: Does this leak
tmp_refs?
Maybe, though its removed by this patch.
Quoted 3 lines
Same question as the other caller about whether we mind getting
extra_havepopulated.
I don't think its a problem to have extra_have populated, least I haven't seen anything to lead me to believe it would be a problem.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 06/35] transport: use get_refs_via_connect to get refs
Quoted 22 lines · Brandon Williams
Quoted 21 lines · Jonathan Nieder
transport.c static int fetch_refs_via_pack(struct transport *transport, · line 234
Quoted 13 lines · Brandon Williams
+++
b/transport.ctransport.c +2
-6@@ -230,12 +230,8 @@ static int fetch_refs_via_pack(struct transport *transport, args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; -if (!data->got_remote_heads) { -connect_setup (transport,0 ); -get_remote_heads (data->fd[0 ],NULL ,0 , &refs_tmp,0 , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) + refs_tmp =get_refs_via_connect (transport,0 );The only difference between the old and new code is that the old code passes NULL as '
extra_have' and the new code passes&data->extra_have.That means this populates the
data->extra_haveoid_array. Does it matter?
[...]
Quoted 2 lines
I don't think its a problem to have
extra_havepopulated, least I haven't seen anything to lead me to believe it would be a problem.
Assuming it gets properly freed later, the only effect I can imagine is some increased memory usage.
I'm inclined to agree with you that the simplicity is worth it. It seems worth mentioning in the commit message, though.
[...]
Quoted 17 lines
Quoted 4 lines
Quoted 4 lines
transport.c +0
-0@@ -541,14 +537,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_restruct send_pack_args args;int ret;Quoted 12 lines
transport.c static int git_transport_push(struct transport *transport, struct ref *remote_re · line 541
Quoted 10 lines
transport.c +2
-8-if (!data->got_remote_heads) { -struct ref *tmp_refs; -connect_setup (transport,1 ); - -get_remote_heads (data->fd[0 ],NULL ,0 , &tmp_refs,REF_NORMAL , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) +get_refs_via_connect (transport,1 );not a new problem, just curious: Does this leak
tmp_refs?
Quoted 1 line
Maybe, though its removed by this patch.
Sorry for the lack of clarity. If it was leaked before, then it is still leaked now, via the discarded return value from get_refs_via_connect.
Any idea how we can track that down? E.g. are there ways to tell leak checkers "just tell me about this particular allocation"?
Thanks,
Jonathan
Re: [PATCH v3 06/35] transport: use get_refs_via_connect to get refs
Quoted 51 lines · Jonathan Nieder
Quoted 22 lines · Brandon Williams
Quoted 21 lines · Jonathan Nieder
transport.c static int fetch_refs_via_pack(struct transport *transport, · line 234
Quoted 13 lines · Brandon Williams
+++
b/transport.ctransport.c +2
-6@@ -230,12 +230,8 @@ static int fetch_refs_via_pack(struct transport *transport, args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; -if (!data->got_remote_heads) { -connect_setup (transport,0 ); -get_remote_heads (data->fd[0 ],NULL ,0 , &refs_tmp,0 , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) + refs_tmp =get_refs_via_connect (transport,0 );The only difference between the old and new code is that the old code passes NULL as '
extra_have' and the new code passes&data->extra_have.That means this populates the
data->extra_haveoid_array. Does it matter?[...]
Quoted 2 lines
I don't think its a problem to have
extra_havepopulated, least I haven't seen anything to lead me to believe it would be a problem.Assuming it gets properly freed later, the only effect I can imagine is some increased memory usage.
I'm inclined to agree with you that the simplicity is worth it. It seems worth mentioning in the commit message, though.
[...]
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
transport.c +0
-0@@ -541,14 +537,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_restruct send_pack_args args;int ret;transport.c static int git_transport_push(struct transport *transport, struct ref *remote_re · line 541
Quoted 10 lines
transport.c +2
-8-if (!data->got_remote_heads) { -struct ref *tmp_refs; -connect_setup (transport,1 ); - -get_remote_heads (data->fd[0 ],NULL ,0 , &tmp_refs,REF_NORMAL , -NULL , &data->shallow); - data->got_remote_heads =1 ; - } +if (!data->got_remote_heads) +get_refs_via_connect (transport,1 );not a new problem, just curious: Does this leak
tmp_refs?
Quoted 8 lines
Quoted 1 line
Maybe, though its removed by this patch.
Sorry for the lack of clarity. If it was leaked before, then it is still leaked now, via the discarded return value from
get_refs_via_connect.Any idea how we can track that down? E.g. are there ways to tell leak checkers "just tell me about this particular allocation"?
Hmm I wonder if that code path is even used, because it just throws away the result.
Signature · 1 line
Brandon Williams
PATCH v3 7/35 connect: convert get_remote_heads to use struct packet_reader
In reply to Brandon WilliamsIn order to allow for better control flow when protocol_v2 is introduced convert 'get_remote_heads()' to use 'struct packet_reader' to read packet lines. This enables a client to be able to peek the first line of a server's response (without consuming it) in order to determine the protocol version its speaking and then passing control to the appropriate handler.
This is needed because the initial response from a server speaking protocol_v0 includes the first ref, while subsequent protocol versions respond with a version line. We want to be able to read this first line without consuming the first ref sent in the protocol_v0 case so that the protocol version the server is speaking can be determined outside of 'get_remote_heads()' in a future patch.
--- connect.c | 174 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 96 insertions(+), 78 deletions(-)
connect.c +96 -78
@@ -48,6 +48,12 @@ int check_ref_type(const struct ref *ref, int flags)static void die_initial_contact (int unexpected) { +/* +* A hang-up after seeing some response from the other end +* means that it is unexpected, as we know the other end is +* willing to talk to us. A hang-up before seeing any +* response does not necessarily mean an ACL problem, though. +*/ if (unexpected)die (_ ("The remote end hung up upon initial contact" ));else
@@ -56,6 +62,41 @@ static void die_initial_contact(int unexpected) "and the repository exists.")); } +static enum protocol_version discover_version (struct packet_reader *reader) +{ +enum protocol_version version = protocol_unknown_version; + +/* +* Peek the first line of the server's response to +* determine the protocol version the server is speaking. +*/ +switch (packet_reader_peek (reader)) { +case PACKET_READ_EOF : +die_initial_contact (0 ); +case PACKET_READ_FLUSH : +case PACKET_READ_DELIM : + version = protocol_v0; +break ; +case PACKET_READ_NORMAL : + version =determine_protocol_version_client (reader->line); +break ; + } + +/* Maybe process capabilities here, at least for v2 */ +switch (version) { +case protocol_v1: +/* Read the peeked version line */ +packet_reader_read (reader); +break ; +case protocol_v0: +break ; +case protocol_unknown_version: +die ("unknown protocol version: '%s'\n" , reader->line); + } + +return version; +} +static void parse_one_symref_info (struct string_list *symref,const char *val,int len) {char *sym, *target;
@@ -109,60 +150,21 @@ static void annotate_refs_with_symref_info(struct ref *ref)string_list_clear (&symref,0 ); } -/* -* Read one line of a server's ref advertisement into packet_buffer. -*/ -static int read_remote_ref (int in,char **src_buf,size_t *src_len, -int *responded) +static void process_capabilities (const char *line,int *len) { -int len =packet_read (in, src_buf, src_len, - packet_buffer,sizeof (packet_buffer), -PACKET_READ_GENTLE_ON_EOF | -PACKET_READ_CHOMP_NEWLINE ); -const char *arg; -if (len <0 ) -die_initial_contact (*responded); -if (len >4 &&skip_prefix (packet_buffer,"ERR " , &arg)) -die ("remote error: %s" , arg); - - *responded =1 ; - -return len; -} - -#define EXPECTING_PROTOCOL_VERSION 0 -#define EXPECTING_FIRST_REF 1 -#define EXPECTING_REF 2 -#define EXPECTING_SHALLOW 3 - -/* Returns 1 if packet_buffer is a protocol version pkt-line, 0 otherwise. */ -static int process_protocol_version (void ) -{ -switch (determine_protocol_version_client (packet_buffer)) { -case protocol_v1: -return 1 ; -case protocol_v0: -return 0 ; -default : -die ("server is speaking an unknown protocol" ); - } -} - -static void process_capabilities (int *len) -{ -int nul_location =strlen (packet_buffer); +int nul_location =strlen (line);if (nul_location == *len)return ; - server_capabilities =xstrdup (packet_buffer + nul_location +1 ); + server_capabilities =xstrdup (line + nul_location +1 ); *len = nul_location; } -static int process_dummy_ref (void ) +static int process_dummy_ref (const char *line) {struct object_id oid;const char *name; -if (parse_oid_hex (packet_buffer, &oid, &name)) +if (parse_oid_hex (line, &oid, &name))return 0 ;if (*name !=' ' )return 0 ;
@@ -171,20 +173,20 @@ static int process_dummy_ref(void)return !oidcmp (&null_oid, &oid) && !strcmp (name,"capabilities^{}" ); } -static void check_no_capabilities (int len) +static void check_no_capabilities (const char *line,int len) { -if (strlen (packet_buffer) != len) +if (strlen (line) != len)warning ("Ignoring capabilities after first line '%s'" , - packet_buffer +strlen (packet_buffer)); + line +strlen (line)); } -static int process_ref (int len,struct ref ***list,unsigned int flags, -struct oid_array *extra_have) +static int process_ref (const char *line,int len,struct ref ***list, +unsigned int flags,struct oid_array *extra_have) {struct object_id old_oid;const char *name; -if (parse_oid_hex (packet_buffer, &old_oid, &name)) +if (parse_oid_hex (line, &old_oid, &name))return 0 ;if (*name !=' ' )return 0 ;
@@ -200,16 +202,17 @@ static int process_ref(int len, struct ref ***list, unsigned int flags, **list = ref; *list = &ref->next; } -check_no_capabilities (len); +check_no_capabilities (line, len);return 1 ; } -static int process_shallow (int len,struct oid_array *shallow_points) +static int process_shallow (const char *line,int len, +struct oid_array *shallow_points) {const char *arg;struct object_id old_oid; -if (!skip_prefix (packet_buffer,"shallow " , &arg)) +if (!skip_prefix (line,"shallow " , &arg))return 0 ;if (get_oid_hex (arg, &old_oid))
@@ -217,10 +220,17 @@ static int process_shallow(int len, struct oid_array *shallow_points)if (!shallow_points)die ("repository on the other end cannot be shallow" );oid_array_append (shallow_points, &old_oid); -check_no_capabilities (len); +check_no_capabilities (line, len);return 1 ; } +enum get_remote_heads_state { +EXPECTING_FIRST_REF =0 , +EXPECTING_REF , +EXPECTING_SHALLOW , +EXPECTING_DONE , +}; +/* * Read all the refs from the other end */
@@ -230,47 +240,55 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, struct oid_array *shallow_points) {struct ref **orig_list = list; +int len =0 ; +enum get_remote_heads_state state =EXPECTING_FIRST_REF ; +struct packet_reader reader; +const char *arg; -/* -* A hang-up after seeing some response from the other end -* means that it is unexpected, as we know the other end is -* willing to talk to us. A hang-up before seeing any -* response does not necessarily mean an ACL problem, though. -*/ -int responded =0 ; -int len; -int state =EXPECTING_PROTOCOL_VERSION ; +packet_reader_init (&reader, in, src_buf, src_len, +PACKET_READ_CHOMP_NEWLINE | +PACKET_READ_GENTLE_ON_EOF ); + +discover_version (&reader); *list =NULL ; -while ((len =read_remote_ref (in, &src_buf, &src_len, &responded))) { +while (state !=EXPECTING_DONE ) { +switch (packet_reader_read (&reader)) { +case PACKET_READ_EOF : +die_initial_contact (1 ); +case PACKET_READ_NORMAL : + len = reader.pktlen; +if (len >4 &&skip_prefix (reader.line,"ERR " , &arg)) +die ("remote error: %s" , arg); +break ; +case PACKET_READ_FLUSH : + state =EXPECTING_DONE ; +break ; +case PACKET_READ_DELIM : +die ("invalid packet" ); + } +switch (state) { -case EXPECTING_PROTOCOL_VERSION : -if (process_protocol_version ()) { - state =EXPECTING_FIRST_REF ; -break ; - } - state =EXPECTING_FIRST_REF ; -/* fallthrough */ case EXPECTING_FIRST_REF : -process_capabilities (&len); -if (process_dummy_ref ()) { +process_capabilities (reader.line, &len); +if (process_dummy_ref (reader.line)) { state =EXPECTING_SHALLOW ;break ; } state =EXPECTING_REF ;/* fallthrough */ case EXPECTING_REF : -if (process_ref (len, &list, flags, extra_have)) +if (process_ref (reader.line, len, &list, flags, extra_have))break ; state =EXPECTING_SHALLOW ;/* fallthrough */ case EXPECTING_SHALLOW : -if (process_shallow (len, shallow_points)) +if (process_shallow (reader.line, len, shallow_points))break ; -die ("protocol error: unexpected '%s'" , packet_buffer); -default : -die ("unexpected state %d" , state); +die ("protocol error: unexpected '%s'" , reader.line); +case EXPECTING_DONE : +break ; } }
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 07/35] connect: convert get_remote_heads to use struct packet_reader
Quoted 15 lines · Brandon Williams
connect.c +11
-0@@ -56,6 +62,41 @@ static void die_initial_contact(int unexpected) "and the repository exists.")); } +static enum protocol_version discover_version (struct packet_reader *reader) +{ +enum protocol_version version = protocol_unknown_version; + +/* +* Peek the first line of the server's response to +* determine the protocol version the server is speaking. +*/ +switch (packet_reader_peek (reader)) { +case PACKET_READ_EOF : +die_initial_contact (0 );
connect.c static void die_initial_contact(int unexpected) · line 85
Quoted 10 lines
connect.c +10
-0+case PACKET_READ_FLUSH : +case PACKET_READ_DELIM : + version = protocol_v0; +break ; +case PACKET_READ_NORMAL : + version =determine_protocol_version_client (reader->line); +break ; + } + +/* Maybe process capabilities here, at least for v2 */
We do not (yet) react to v2, so this comment only makes sense after a later patch? If so please include it later, as this is confusing for now.
connect.c static void die_initial_contact(int unexpected) · line 89
Quoted 4 lines
connect.c +4
-0+switch (version) { +case protocol_v1: +/* Read the peeked version line */ +packet_reader_read (reader);
I wonder if we want to assign version to v0 here,
as now all v1 is done and we could treat the remaining communication as a v0. Not sure if that helps with some switch/cases, but as we'd give all cases to have the compiler not yell at us, this would be no big deal. So I guess we can keep it v1.
With or without the comment nit, this patch is
Re: [PATCH v3 07/35] connect: convert get_remote_heads to use struct packet_reader
In reply to Brandon WilliamsQuoted 2 lines
connect.c +2
-0+static enum protocol_version discover_version (struct packet_reader *reader) +{
...
connect.c static void die_initial_contact(int unexpected) · line 94
Quoted 11 lines
connect.c +11
-0+ +/* Maybe process capabilities here, at least for v2 */ +switch (version) { +case protocol_v1: +/* Read the peeked version line */ +packet_reader_read (reader); +break ; +case protocol_v0: +break ; +case protocol_unknown_version: +die ("unknown protocol version: '%s'\n" , reader->line);
The following patches introduce more of the switch(version) cases. And there it actually is a
BUG("protocol version unknown? should have been set in discover_version")
but here it is a mere
die (_("The server uses a different protocol version than we can
speak: %s\n"),
reader->line);
so I would think here it is reasonable to add _(translation).
Re: [PATCH v3 07/35] connect: convert get_remote_heads to use struct packet_reader
Quoted 15 lines · Stefan Beller
Quoted 2 lines
connect.c +2
-0+static enum protocol_version discover_version (struct packet_reader *reader) +{...
connect.c static void die_initial_contact(int unexpected) · line 94
Quoted 11 lines
connect.c +11
-0+ +/* Maybe process capabilities here, at least for v2 */ +switch (version) { +case protocol_v1: +/* Read the peeked version line */ +packet_reader_read (reader); +break ; +case protocol_v0: +break ; +case protocol_unknown_version: +die ("unknown protocol version: '%s'\n" , reader->line);
Quoted 8 lines
The following patches introduce more of the switch(version) cases. And there it actually is a
BUG("protocol version unknown? should have been set in discover_version")
but here it is a mere
die (_("The server uses a different protocol version than we can
speak: %s\n"),reader->line);so I would think here it is reasonable to add
_(translation).
This should be a BUG as it shouldn't ever be unknown at this point. And I'll also drop that comment.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 07/35] connect: convert get_remote_heads to use struct packet_reader
Quoted 25 lines · Brandon Williams
Quoted 15 lines · Stefan Beller
Quoted 2 lines
connect.c +2
-0+static enum protocol_version discover_version (struct packet_reader *reader) +{...
connect.c static void die_initial_contact(int unexpected) · line 94
Quoted 11 lines
connect.c +11
-0+ +/* Maybe process capabilities here, at least for v2 */ +switch (version) { +case protocol_v1: +/* Read the peeked version line */ +packet_reader_read (reader); +break ; +case protocol_v0: +break ; +case protocol_unknown_version: +die ("unknown protocol version: '%s'\n" , reader->line);Quoted 8 lines
The following patches introduce more of the switch(version) cases. And there it actually is a
BUG("protocol version unknown? should have been set in discover_version")
but here it is a mere
die (_("The server uses a different protocol version than we can
speak: %s\n"),reader->line);so I would think here it is reasonable to add
_(translation).
Quoted 2 lines
This should be a BUG as it shouldn't ever be unknown at this point. And I'll also drop that comment.
Huh?
Then I miss-understood the flow of code. When the server announces its answer is version 42, but the client cannot handle it, which die call is responsible for reporting it to the user?
(That is technically a BUG on the server side, as we probably never asked for v42, so I would not want to print BUG locally at the client?)
Re: [PATCH v3 07/35] connect: convert get_remote_heads to use struct packet_reader
Quoted 29 lines · Stefan Beller
Quoted 25 lines · Brandon Williams
Quoted 15 lines · Stefan Beller
Quoted 2 lines
connect.c +2
-0+static enum protocol_version discover_version (struct packet_reader *reader) +{...
connect.c static void die_initial_contact(int unexpected) · line 94
Quoted 11 lines
connect.c +11
-0+ +/* Maybe process capabilities here, at least for v2 */ +switch (version) { +case protocol_v1: +/* Read the peeked version line */ +packet_reader_read (reader); +break ; +case protocol_v0: +break ; +case protocol_unknown_version: +die ("unknown protocol version: '%s'\n" , reader->line);Quoted 8 lines
The following patches introduce more of the switch(version) cases. And there it actually is a
BUG("protocol version unknown? should have been set in discover_version")
but here it is a mere
die (_("The server uses a different protocol version than we can
speak: %s\n"),reader->line);so I would think here it is reasonable to add
_(translation).Quoted 2 lines
This should be a BUG as it shouldn't ever be unknown at this point. And I'll also drop that comment.
Quoted 6 lines
Huh?
Then I miss-understood the flow of code. When the server announces its answer is version 42, but the client cannot handle it, which die call is responsible for reporting it to the user?
(That is technically a BUG on the server side, as we probably never asked for v42, so I would not want to print BUG locally at the client?)
This is handled in determine_protocol_version_client(const char *server_response), which is just a few lines out of context here.
Signature · 1 line
Brandon Williams
PATCH v3 8/35 connect: discover protocol version outside of get_remote_heads
In reply to Brandon WilliamsIn order to prepare for the addition of protocol_v2 push the protocol version discovery outside of 'get_remote_heads()'. This will allow for keeping the logic for processing the reference advertisement for protocol_v1 and protocol_v0 separate from the logic for protocol_v2.
--- builtin/fetch-pack.c | 16 +++++++++++++++- builtin/send-pack.c | 17 +++++++++++++++-- connect.c | 27 ++++++++++----------------- connect.h | 3 +++ remote-curl.c | 20 ++++++++++++++++++-- remote.h | 5 +++-- transport.c | 24 +++++++++++++++++++----- 7 files changed, 83 insertions(+), 29 deletions(-)
builtin/fetch-pack.c +15 -1
@@ -4,6 +4,7 @@#include "remote.h" #include "connect.h" #include "sha1-array.h" +#include "protocol.h" static const char fetch_pack_usage[] ="git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] "
@@ -52,6 +53,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)struct fetch_pack_args args;struct oid_array shallow =OID_ARRAY_INIT ;struct string_list deepen_not =STRING_LIST_INIT_DUP ; +struct packet_reader reader;packet_trace_identity ("fetch-pack" );
@@ -193,7 +195,19 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)if (!conn)return args.diag_url ?0 :1 ; } -get_remote_heads (fd[0 ],NULL ,0 , &ref,0 ,NULL , &shallow); + +packet_reader_init (&reader, fd[0 ],NULL ,0 , +PACKET_READ_CHOMP_NEWLINE | +PACKET_READ_GENTLE_ON_EOF ); + +switch (discover_version (&reader)) { +case protocol_v1: +case protocol_v0: +get_remote_heads (&reader, &ref,0 ,NULL , &shallow); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + } ref =fetch_pack (&args, fd, conn, ref, dest, sought, nr_sought, &shallow, pack_lockfile_ptr);
builtin/send-pack.c +15 -2
@@ -14,6 +14,7 @@#include "sha1-array.h" #include "gpg-interface.h" #include "gettext.h" +#include "protocol.h" static const char *const send_pack_usage[] = { N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
@@ -154,6 +155,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)int progress =-1 ;int from_stdin =0 ;struct push_cas_option cas = {0 }; +struct packet_reader reader;struct option options[] = {OPT__VERBOSITY (&verbose),
@@ -256,8 +258,19 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.verbose ? CONNECT_VERBOSE : 0); } -get_remote_heads (fd[0 ],NULL ,0 , &remote_refs,REF_NORMAL , - &extra_have, &shallow); +packet_reader_init (&reader, fd[0 ],NULL ,0 , +PACKET_READ_CHOMP_NEWLINE | +PACKET_READ_GENTLE_ON_EOF ); + +switch (discover_version (&reader)) { +case protocol_v1: +case protocol_v0: +get_remote_heads (&reader, &remote_refs,REF_NORMAL , + &extra_have, &shallow); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + }transport_verify_remote_names (nr_refspecs, refspecs);
connect.c +10 -17
@@ -62,7 +62,7 @@ static void die_initial_contact(int unexpected) "and the repository exists.")); } -static enum protocol_version discover_version (struct packet_reader *reader) +enum protocol_version discover_version (struct packet_reader *reader) {enum protocol_version version = protocol_unknown_version;
@@ -234,7 +234,7 @@ enum get_remote_heads_state {/* * Read all the refs from the other end */ -struct ref **get_remote_heads (int in,char *src_buf,size_t src_len, +struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have,struct oid_array *shallow_points)
@@ -242,24 +242,17 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,struct ref **orig_list = list;int len =0 ;enum get_remote_heads_state state =EXPECTING_FIRST_REF ; -struct packet_reader reader;const char *arg; -packet_reader_init (&reader, in, src_buf, src_len, -PACKET_READ_CHOMP_NEWLINE | -PACKET_READ_GENTLE_ON_EOF ); - -discover_version (&reader); - *list =NULL ;while (state !=EXPECTING_DONE ) { -switch (packet_reader_read (&reader)) { +switch (packet_reader_read (reader)) {case PACKET_READ_EOF :die_initial_contact (1 );case PACKET_READ_NORMAL : - len = reader.pktlen; -if (len >4 &&skip_prefix (reader.line,"ERR " , &arg)) + len = reader->pktlen; +if (len >4 &&skip_prefix (reader->line,"ERR " , &arg))die ("remote error: %s" , arg);break ;case PACKET_READ_FLUSH :
@@ -271,22 +264,22 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,switch (state) {case EXPECTING_FIRST_REF : -process_capabilities (reader.line, &len); -if (process_dummy_ref (reader.line)) { +process_capabilities (reader->line, &len); +if (process_dummy_ref (reader->line)) { state =EXPECTING_SHALLOW ;break ; } state =EXPECTING_REF ;/* fallthrough */ case EXPECTING_REF : -if (process_ref (reader.line, len, &list, flags, extra_have)) +if (process_ref (reader->line, len, &list, flags, extra_have))break ; state =EXPECTING_SHALLOW ;/* fallthrough */ case EXPECTING_SHALLOW : -if (process_shallow (reader.line, len, shallow_points)) +if (process_shallow (reader->line, len, shallow_points))break ; -die ("protocol error: unexpected '%s'" , reader.line); +die ("protocol error: unexpected '%s'" , reader->line);case EXPECTING_DONE :break ; }
connect.h +3 -0
@@ -13,4 +13,7 @@ extern int parse_feature_request(const char *features, const char *feature);extern const char *server_feature_value (const char *feature,int *len_ret);extern int url_is_local_not_ssh (const char *url); +struct packet_reader ; +extern enum protocol_version discover_version (struct packet_reader *reader); +#endif
remote-curl.c +18 -2
@@ -1,6 +1,7 @@#include "cache.h" #include "config.h" #include "remote.h" +#include "connect.h" #include "strbuf.h" #include "walker.h" #include "http.h"
@@ -13,6 +14,7 @@#include "credential.h" #include "sha1-array.h" #include "send-pack.h" +#include "protocol.h" static struct remote *remote;/* always ends with a trailing slash */
@@ -176,8 +178,22 @@ static struct discovery *last_discovery;static struct ref *parse_git_refs (struct discovery *heads,int for_push) {struct ref *list =NULL ; -get_remote_heads (-1 , heads->buf, heads->len, &list, - for_push ?REF_NORMAL :0 ,NULL , &heads->shallow); +struct packet_reader reader; + +packet_reader_init (&reader,-1 , heads->buf, heads->len, +PACKET_READ_CHOMP_NEWLINE | +PACKET_READ_GENTLE_ON_EOF ); + +switch (discover_version (&reader)) { +case protocol_v1: +case protocol_v0: +get_remote_heads (&reader, &list, for_push ?REF_NORMAL :0 , +NULL , &heads->shallow); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + } +return list; }
remote.h +3 -2
@@ -150,10 +150,11 @@ int check_ref_type(const struct ref *ref, int flags);void free_refs (struct ref *ref);struct oid_array ; -extern struct ref **get_remote_heads (int in,char *src_buf,size_t src_len, +struct packet_reader ; +extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have, -struct oid_array *shallow); +struct oid_array *shallow_points);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);
transport.c +19 -5
@@ -18,6 +18,7 @@#include "sha1-array.h" #include "sigchain.h" #include "transport-internal.h" +#include "protocol.h" static void set_upstreams (struct transport *transport,struct ref *refs,int pretend)
@@ -190,13 +191,26 @@ static int connect_setup(struct transport *transport, int for_push)static struct ref *get_refs_via_connect (struct transport *transport,int for_push) {struct git_transport_data *data = transport->data; -struct ref *refs; +struct ref *refs =NULL ; +struct packet_reader reader;connect_setup (transport, for_push); -get_remote_heads (data->fd[0 ],NULL ,0 , &refs, - for_push ?REF_NORMAL :0 , - &data->extra_have, - &data->shallow); + +packet_reader_init (&reader, data->fd[0 ],NULL ,0 , +PACKET_READ_CHOMP_NEWLINE | +PACKET_READ_GENTLE_ON_EOF ); + +switch (discover_version (&reader)) { +case protocol_v1: +case protocol_v0: +get_remote_heads (&reader, &refs, + for_push ?REF_NORMAL :0 , + &data->extra_have, + &data->shallow); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + } data->got_remote_heads =1 ;return refs;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 08/35] connect: discover protocol version outside of get_remote_heads
Quoted 4 lines · On Tue, 6 Feb 2018 17:12:45 -0800 Brandon Williams <[email protected]> wrote:
builtin/fetch-pack.c +3
-1-get_remote_heads (fd[0 ],NULL ,0 , &ref,0 ,NULL , &shallow); + + packet_reader_init(&reader, fd[0], NULL, 0, + PACKET_READ_CHOMP_NEWLINE |
builtin/fetch-pack.c int cmd_fetch_pack(int argc, const char **argv, const char *prefix) · line 210
Quoted 10 lines
builtin/fetch-pack.c +10
-0+ PACKET_READ_GENTLE_ON_EOF); + +switch (discover_version (&reader)) { +case protocol_v1: +case protocol_v0: +get_remote_heads (&reader, &ref,0 ,NULL , &shallow); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + }
This inlining is repeated a few times, which raises the question: if the intention is to keep the v0/1 logic separately from v2, why not have a single function that wraps them all? Looking at the end result (after all the patches in this patch set are applied), it seems that the v2 version does not have extra_have or shallow parameters, which is a good enough reason for me (I don't think functions that take in many arguments and then selectively use them is a good idea). I think that other reviewers will have this question too, so maybe discuss this in the commit message.
Quoted 5 lines
remote.h +0
-0
remote.h int check_ref_type(const struct ref *ref, int flags); · line 157
Quoted 10 lines
remote.h +3
-2@@ -150,10 +150,11 @@ int check_ref_type(const struct ref *ref, int flags);void free_refs (struct ref *ref);struct oid_array ; -extern struct ref **get_remote_heads (int in,char *src_buf,size_t src_len, +struct packet_reader ; +extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have, -struct oid_array *shallow); +struct oid_array *shallow_points);
This change probably does not belong in this patch, especially since remote.c is unchanged.
Re: [PATCH v3 08/35] connect: discover protocol version outside of get_remote_heads
Quoted 19 lines · Jonathan Tan
Quoted 4 lines · On Tue, 6 Feb 2018 17:12:45 -0800 Brandon Williams <[email protected]> wrote:
builtin/fetch-pack.c +3
-1-get_remote_heads (fd[0 ],NULL ,0 , &ref,0 ,NULL , &shallow); + + packet_reader_init(&reader, fd[0], NULL, 0, + PACKET_READ_CHOMP_NEWLINE |builtin/fetch-pack.c int cmd_fetch_pack(int argc, const char **argv, const char *prefix) · line 210
Quoted 10 lines
builtin/fetch-pack.c +10
-0+ PACKET_READ_GENTLE_ON_EOF); + +switch (discover_version (&reader)) { +case protocol_v1: +case protocol_v0: +get_remote_heads (&reader, &ref,0 ,NULL , &shallow); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + }This inlining is repeated a few times, which raises the question: if the
Quoted 8 lines
intention is to keep the v0/1 logic separately from v2, why not have a single function that wraps them all? Looking at the end result (after all the patches in this patch set are applied), it seems that the v2 version does not have
extra_haveor shallow parameters, which is a good enough reason for me (I don't think functions that take in many arguments and then selectively use them is a good idea). I think that other reviewers will have this question too, so maybe discuss this in the commit message.
Yes this sort of switch statement appears a few times but really there isn't a good way to "have one function to wrap it all" with the current state of the code. That sort of change would take tons of refactoring to get into a state where we could do that, and is outside the scope of this series.
Quoted 8 lines
Quoted 8 lines
remote.h +0
-0@@ -150,10 +150,11 @@ int check_ref_type(const struct ref *ref, int flags);void free_refs (struct ref *ref);
Quoted 11 lines
remote.h int check_ref_type(const struct ref *ref, int flags); · line 157
Quoted 8 lines
remote.h +3
-2struct oid_array ; -extern struct ref **get_remote_heads (int in,char *src_buf,size_t src_len, +struct packet_reader ; +extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have, -struct oid_array *shallow); +struct oid_array *shallow_points);This change probably does not belong in this patch, especially since
remote.cis unchanged.
Yes this hunk is needed, the signature of get_remote_heads changes. It may be difficult to see that due to the fact that we don't really have a clear story on how header files are divided up within the project.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 08/35] connect: discover protocol version outside of get_remote_heads
Quoted 19 lines · On Thu, 22 Feb 2018 10:17:39 -0800 Brandon Williams <[email protected]> wrote:
Quoted 7 lines
Quoted 7 lines
remote.h +0
-0@@ -150,10 +150,11 @@ int check_ref_type(const struct ref *ref, int flags);void free_refs (struct ref *ref);Quoted 11 lines
remote.h int check_ref_type(const struct ref *ref, int flags); · line 157
Quoted 8 lines
remote.h +3
-2struct oid_array ; -extern struct ref **get_remote_heads (int in,char *src_buf,size_t src_len, +struct packet_reader ; +extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have, -struct oid_array *shallow); +struct oid_array *shallow_points);This change probably does not belong in this patch, especially since
remote.cis unchanged.
Quoted 3 lines
Yes this hunk is needed, the signature of
get_remote_headschanges. It may be difficult to see that due to the fact that we don't really have a clear story on how header files are divided up within the project.
Thanks - I indeed didn't notice that the implementation of get_remote_heads() is modified too in this patch.
My initial comment was about just the renaming of "shallow" to "shallow_points", but yes, you're right - I see in the implementation that it is indeed named "shallow_points" there, so this change is justified, especially since you're already changing the signature of this function.
PATCH v3 9/35 transport: store protocol version
In reply to Brandon WilliamsOnce protocol_v2 is introduced requesting a fetch or a push will need to be handled differently depending on the protocol version. Store the protocol version the server is speaking in 'struct git_transport_data' and use it to determine what to do in the case of a fetch or a push.
--- transport.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-)
transport.c +26 -9
@@ -118,6 +118,7 @@ struct git_transport_data {struct child_process *conn;int fd[2 ]; unsigned got_remote_heads : 1; +enum protocol_version version;struct oid_array extra_have;struct oid_array shallow; };
@@ -200,7 +201,8 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pusPACKET_READ_CHOMP_NEWLINE | PACKET_READ_GENTLE_ON_EOF); -switch (discover_version (&reader)) { + data->version =discover_version (&reader); +switch (data->version) {case protocol_v1:case protocol_v0: get_remote_heads(&reader, &refs,
@@ -221,7 +223,7 @@ static int fetch_refs_via_pack(struct transport *transport, {int ret =0 ;struct git_transport_data *data = transport->data; -struct ref *refs; +struct ref *refs =NULL ;char *dest =xstrdup (transport->url);struct fetch_pack_args args;struct ref *refs_tmp =NULL ;
@@ -247,10 +249,18 @@ static int fetch_refs_via_pack(struct transport *transport,if (!data->got_remote_heads) refs_tmp =get_refs_via_connect (transport,0 ); - refs =fetch_pack (&args, data->fd, data->conn, - refs_tmp ? refs_tmp : transport->remote_refs, - dest, to_fetch, nr_heads, &data->shallow, - &transport->pack_lockfile); +switch (data->version) { +case protocol_v1: +case protocol_v0: + refs =fetch_pack (&args, data->fd, data->conn, + refs_tmp ? refs_tmp : transport->remote_refs, + dest, to_fetch, nr_heads, &data->shallow, + &transport->pack_lockfile); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + } +close (data->fd[0 ]);close (data->fd[1 ]);if (finish_connect (data->conn))
@@ -549,7 +559,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re {struct git_transport_data *data = transport->data;struct send_pack_args args; -int ret; +int ret =0 ;if (!data->got_remote_heads)get_refs_via_connect (transport,1 );
@@ -574,8 +584,15 @@ static int git_transport_push(struct transport *transport, struct ref *remote_reelse args.push_cert =SEND_PACK_PUSH_CERT_NEVER ; - ret =send_pack (&args, data->fd, data->conn, remote_refs, - &data->extra_have); +switch (data->version) { +case protocol_v1: +case protocol_v0: + ret =send_pack (&args, data->fd, data->conn, remote_refs, + &data->extra_have); +break ; +case protocol_unknown_version: +BUG ("unknown protocol version" ); + }close (data->fd[1 ]);close (data->fd[0 ]);
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v3 10/35 protocol: introduce enum protocol_version value protocol_v2
In reply to Brandon WilliamsIntroduce protocol_v2, a new value for 'enum protocol_version'. Subsequent patches will fill in the implementation of protocol_v2.
--- builtin/fetch-pack.c | 3 +++ builtin/receive-pack.c | 6 ++++++ builtin/send-pack.c | 3 +++ builtin/upload-pack.c | 7 +++++++ connect.c | 3 +++ protocol.c | 2 ++ protocol.h | 1 + remote-curl.c | 3 +++ transport.c | 9 +++++++++ 9 files changed, 37 insertions(+)
builtin/fetch-pack.c +3 -0
@@ -201,6 +201,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) PACKET_READ_GENTLE_ON_EOF);switch (discover_version (&reader)) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:case protocol_v0:get_remote_heads (&reader, &ref,0 ,NULL , &shallow);
builtin/receive-pack.c +6 -0
@@ -1963,6 +1963,12 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unpack_limit = receive_unpack_limit;switch (determine_protocol_version_server ()) { +case protocol_v2: +/* +* push support for protocol v2 has not been implemented yet, +* so ignore the request to use v2 and fallback to using v0. +*/ +break ;case protocol_v1:/* * v1 is just the original protocol with a version string,
builtin/send-pack.c +3 -0
@@ -263,6 +263,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) PACKET_READ_GENTLE_ON_EOF);switch (discover_version (&reader)) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:case protocol_v0: get_remote_heads(&reader, &remote_refs, REF_NORMAL,
builtin/upload-pack.c +7 -0
@@ -47,6 +47,13 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)die ("'%s' does not appear to be a git repository" , dir);switch (determine_protocol_version_server ()) { +case protocol_v2: +/* +* fetch support for protocol v2 has not been implemented yet, +* so ignore the request to use v2 and fallback to using v0. +*/ +upload_pack (&opts); +break ;case protocol_v1:/* * v1 is just the original protocol with a version string,
connect.c +3 -0
@@ -84,6 +84,9 @@ enum protocol_version discover_version(struct packet_reader *reader)/* Maybe process capabilities here, at least for v2 */ switch (version) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:/* Read the peeked version line */ packet_reader_read (reader);
protocol.c +2 -0
@@ -8,6 +8,8 @@ static enum protocol_version parse_protocol_version(const char *value)return protocol_v0; else if (!strcmp(value, "1"))return protocol_v1; + else if (!strcmp(value, "2")) +return protocol_v2; else return protocol_unknown_version; }
protocol.h +1 -0
@@ -5,6 +5,7 @@ enum protocol_version { protocol_unknown_version =-1 , protocol_v0 =0 , protocol_v1 =1 , + protocol_v2 = 2, };/*
remote-curl.c +3 -0
@@ -185,6 +185,9 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push) PACKET_READ_GENTLE_ON_EOF);switch (discover_version (&reader)) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:case protocol_v0: get_remote_heads(&reader, &list, for_push ? REF_NORMAL : 0,
transport.c +9 -0
@@ -203,6 +203,9 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus data->version =discover_version (&reader);switch (data->version) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:case protocol_v0: get_remote_heads(&reader, &refs,
@@ -250,6 +253,9 @@ static int fetch_refs_via_pack(struct transport *transport, refs_tmp =get_refs_via_connect (transport,0 );switch (data->version) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:case protocol_v0: refs = fetch_pack(&args, data->fd, data->conn,
@@ -585,6 +591,9 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re args.push_cert =SEND_PACK_PUSH_CERT_NEVER ;switch (data->version) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;case protocol_v1:case protocol_v0: ret = send_pack(&args, data->fd, data->conn, remote_refs,
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 10/35] protocol: introduce enum protocol_version value protocol_v2
Hi,
Quoted 5 lines · Brandon Williams
Introduce
protocol_v2, a new value for 'enumprotocol_version'. Subsequent patches will fill in the implementation ofprotocol_v2.Signed-off-by Brandon Williams <[email protected]>---
Yay!
[...]
builtin/fetch-pack.c int cmd_fetch_pack(int argc, const char **argv, const char *prefix) · line 206
Quoted 8 lines
+++
b/builtin/fetch-pack.cbuiltin/fetch-pack.c +3
-0@@ -201,6 +201,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) PACKET_READ_GENTLE_ON_EOF);switch (discover_version (&reader)) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;
This code goes away in a later patch, so no need to do anything about this, but the 'break' is redundant after the 'die'.
[...]
builtin/receive-pack.c int cmd_receive_pack(int argc, const char **argv, const char *prefix) · line 1971
Quoted 12 lines
builtin/receive-pack.c +6
-0@@ -1963,6 +1963,12 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unpack_limit = receive_unpack_limit;switch (determine_protocol_version_server ()) { +case protocol_v2: +/* +* push support for protocol v2 has not been implemented yet, +* so ignore the request to use v2 and fallback to using v0. +*/ +break ;
As you mentioned in the cover letter, it's probably worth doing the same fallback on the client side (send-pack), too.
Otherwise when this client talks to a new-enough server, it would request protocol v2 and then get confused when the server responds with the protocol v2 it requested.
Thanks,
Jonathan
Re: [PATCH v3 10/35] protocol: introduce enum protocol_version value protocol_v2
Quoted 17 lines · Jonathan Nieder
Hi,
Quoted 5 lines · Brandon Williams
Introduce
protocol_v2, a new value for 'enumprotocol_version'. Subsequent patches will fill in the implementation ofprotocol_v2.Signed-off-by Brandon Williams <[email protected]>---
Yay!
[...]
Quoted 4 lines
+++
b/builtin/fetch-pack.c(quoted diff) +0
-0@@ -201,6 +201,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) PACKET_READ_GENTLE_ON_EOF);
Quoted 7 lines
builtin/fetch-pack.c int cmd_fetch_pack(int argc, const char **argv, const char *prefix) · line 206
Quoted 4 lines
builtin/fetch-pack.c +3
-0switch (discover_version (&reader)) { +case protocol_v2: +die ("support for protocol v2 not implemented yet" ); +break ;This code goes away in a later patch, so no need to do anything about this, but the 'break' is redundant after the 'die'.
I'll fix that.
Quoted 18 lines
[...]
builtin/receive-pack.c int cmd_receive_pack(int argc, const char **argv, const char *prefix) · line 1971
Quoted 12 lines
builtin/receive-pack.c +6
-0@@ -1963,6 +1963,12 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unpack_limit = receive_unpack_limit;switch (determine_protocol_version_server ()) { +case protocol_v2: +/* +* push support for protocol v2 has not been implemented yet, +* so ignore the request to use v2 and fallback to using v0. +*/ +break ;As you mentioned in the cover letter, it's probably worth doing the same fallback on the client side (send-pack), too.
Quoted 3 lines
Otherwise when this client talks to a new-enough server, it would request protocol v2 and then get confused when the server responds with the protocol v2 it requested.
Some patches later on ensure this.
Quoted 3 lines
Thanks,
Jonathan
Signature · 1 line
Brandon Williams
PATCH v3 11/35 test-pkt-line: introduce a packet-line test helper
In reply to Brandon WilliamsIntroduce a packet-line test helper which can either pack or unpack an input stream into packet-lines and writes out the result to stdout.
--- Makefile | 1 + t/helper/test-pkt-line.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 t/helper/test-pkt-line.c
Makefile +1 -0
@@ -669,6 +669,7 @@ TEST_PROGRAMS_NEED_X += test-mktemp TEST_PROGRAMS_NEED_X += test-online-cpus TEST_PROGRAMS_NEED_X += test-parse-options TEST_PROGRAMS_NEED_X += test-path-utils +TEST_PROGRAMS_NEED_X += test-pkt-line TEST_PROGRAMS_NEED_X += test-prio-queue TEST_PROGRAMS_NEED_X += test-read-cache TEST_PROGRAMS_NEED_X += test-write-cache
t/helper/test-pkt-line.c new file +64 -0
64 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 11/35] test-pkt-line: introduce a packet-line test helper
t/helper/test-pkt-line.c line 5
Quoted 3 lines · Brandon Williams
t/helper/test-pkt-line.c new file +3
-0+static void pack_line (const char *line) +{ +if (!strcmp (line,"0000" ) || !strcmp (line,"0000\n" ))
From our in-office discussion:
v1/v0 packs pktlines twice in http, which is not possible to construct using this test helper when using the same string for the packed and unpacked representation of flush and delim packets, i.e. test-pkt-line --pack $(test-pkt-line --pack 0000) would produce '0000' instead of '00090000\n'.
To fix it we'd have to replace the unpacked versions of these pkts to something else such as "FLUSH" "DELIM".
However as we do not anticipate the test helper to be used in further tests for v0, this ought to be no big issue.
Maybe someone else cares though?
Re: [PATCH v3 11/35] test-pkt-line: introduce a packet-line test helper
Quoted 15 lines · Stefan Beller
t/helper/test-pkt-line.c line 5
Quoted 3 lines · Brandon Williams
t/helper/test-pkt-line.c new file +3
-0+static void pack_line (const char *line) +{ +if (!strcmp (line,"0000" ) || !strcmp (line,"0000\n" ))From our in-office discussion:
v1/v0 packs pktlines twice in http, which is not possible to construct using this test helper when using the same string for the packed and unpacked representation of flush and delim packets, i.e. test-pkt-line --pack $(test-pkt-line --pack 0000) would produce '0000' instead of '00090000\n'.
To fix it we'd have to replace the unpacked versions of these pkts to something else such as "FLUSH" "DELIM".
Quoted 3 lines
However as we do not anticipate the test helper to be used in further tests for v0, this ought to be no big issue.
Maybe someone else cares though?
I'm going to punt and say, if someone cares enough they can update this test-helper when they want to use it for v1/v0 stuff.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 11/35] test-pkt-line: introduce a packet-line test helper
Quoted 16 lines · Brandon Williams
Quoted 15 lines · Stefan Beller
t/helper/test-pkt-line.c line 5
Quoted 3 lines · Brandon Williams
t/helper/test-pkt-line.c new file +3
-0+static void pack_line (const char *line) +{ +if (!strcmp (line,"0000" ) || !strcmp (line,"0000\n" ))From our in-office discussion:
v1/v0 packs pktlines twice in http, which is not possible to construct using this test helper when using the same string for the packed and unpacked representation of flush and delim packets, i.e. test-pkt-line --pack $(test-pkt-line --pack 0000) would produce '0000' instead of '00090000\n'.
To fix it we'd have to replace the unpacked versions of these pkts to something else such as "FLUSH" "DELIM".
Quoted 6 lines
Quoted 3 lines
However as we do not anticipate the test helper to be used in further tests for v0, this ought to be no big issue.
Maybe someone else cares though?I'm going to punt and say, if someone cares enough they can update this test-helper when they want to use it for v1/v0 stuff.
I recently add packetize and depacketize helpers for testing v0 streams; see 4414a15002 (t/lib-git-daemon: add network-protocol helpers, 2018-01-24). Is it worth folding these together?
-Peff
Re: [PATCH v3 11/35] test-pkt-line: introduce a packet-line test helper
Quoted 22 lines · Jeff King
Quoted 20 lines · Brandon Williams
Quoted 18 lines · Stefan Beller
t/helper/test-pkt-line.c line 5
Quoted 3 lines · Brandon Williams
t/helper/test-pkt-line.c new file +3
-0+static void pack_line (const char *line) +{ +if (!strcmp (line,"0000" ) || !strcmp (line,"0000\n" ))From our in-office discussion:
v1/v0 packs pktlines twice in http, which is not possible to construct using this test helper when using the same string for the packed and unpacked representation of flush and delim packets, i.e. test-pkt-line --pack $(test-pkt-line --pack 0000) would produce '0000' instead of '00090000\n'.
To fix it we'd have to replace the unpacked versions of these pkts to something else such as "FLUSH" "DELIM".However as we do not anticipate the test helper to be used in further tests for v0, this ought to be no big issue.
Maybe someone else cares though?
Quoted 6 lines
Quoted 2 lines
I'm going to punt and say, if someone cares enough they can update this test-helper when they want to use it for v1/v0 stuff.
I recently add packetize and depacketize helpers for testing v0 streams; see
4414a15002(t/lib-git-daemon: add network-protocol helpers, 2018-01-24). Is it worth folding these together?
I didn't know something like that existed! (of course if it was just added this year then it didn't exist when I started working on this stuff). Yeah its probably a good idea to fold these together, I can take a look at how your packetize and depacketize helpers work and add the small amount of functionality that I'd need to replace the helper I made.
Quoted 2 lines
-Peff
Signature · 1 line
Brandon Williams
PATCH v3 12/35 serve: introduce git-serve
In reply to Brandon WilliamsIntroduce git-serve, the base server for protocol version 2.
Protocol version 2 is intended to be a replacement for Git's current wire protocol. The intention is that it will be a simpler, less wasteful protocol which can evolve over time.
Protocol version 2 improves upon version 1 by eliminating the initial ref advertisement. In its place a server will export a list of capabilities and commands which it supports in a capability advertisement. A client can then request that a particular command be executed by providing a number of capabilities and command specific parameters. At the completion of a command, a client can request that another command be executed or can terminate the connection by sending a flush packet.
--- .gitignore | 1 + Documentation/technical/protocol-v2.txt | 114 +++++++++++++++ Makefile | 2 + builtin.h | 1 + builtin/serve.c | 30 ++++ git.c | 1 + serve.c | 250 ++++++++++++++++++++++++++++++++ serve.h | 15 ++ t/t5701-git-serve.sh | 60 ++++++++ 9 files changed, 474 insertions(+) create mode 100644 Documentation/technical/protocol-v2.txt create mode 100644 builtin/serve.c create mode 100644 serve.c create mode 100644 serve.h create mode 100755 t/t5701-git-serve.sh
.gitignore +1 -0
@@ -140,6 +140,7 @@ /git-rm /git-send-email /git-send-pack +/git-serve /git-sh-i18n /git-sh-i18n--envsubst /git-sh-setup
Documentation/technical/protocol-v2.txt new file +114 -0
114 lines not on this page · raw message
Makefile +2 -0
@@ -881,6 +881,7 @@ LIB_OBJS += revision.o LIB_OBJS += run-command.o LIB_OBJS += send-pack.o LIB_OBJS += sequencer.o +LIB_OBJS += serve.o LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1-array.o
@@ -1014,6 +1015,7 @@ BUILTIN_OBJS += builtin/rev-parse.o BUILTIN_OBJS += builtin/revert.o BUILTIN_OBJS += builtin/rm.o BUILTIN_OBJS += builtin/send-pack.o +BUILTIN_OBJS += builtin/serve.o BUILTIN_OBJS += builtin/shortlog.o BUILTIN_OBJS += builtin/show-branch.o BUILTIN_OBJS += builtin/show-ref.o
builtin.h +1 -0
@@ -215,6 +215,7 @@ extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);extern int cmd_revert (int argc,const char **argv,const char *prefix);extern int cmd_rm (int argc,const char **argv,const char *prefix);extern int cmd_send_pack (int argc,const char **argv,const char *prefix); +extern int cmd_serve (int argc,const char **argv,const char *prefix);extern int cmd_shortlog (int argc,const char **argv,const char *prefix);extern int cmd_show (int argc,const char **argv,const char *prefix);extern int cmd_show_branch (int argc,const char **argv,const char *prefix);
builtin/serve.c new file +30 -0
@@ -0,0 +1,30 @@ +#include "cache.h" +#include "builtin.h" +#include "parse-options.h" +#include "serve.h" + +static char const *const serve_usage[] = { +N_ ("git serve [<options>]" ), +NULL +}; + +int cmd_serve (int argc,const char **argv,const char *prefix) +{ +struct serve_options opts =SERVE_OPTIONS_INIT ; + +struct option options[] = { +OPT_BOOL (0 ,"stateless-rpc" , &opts.stateless_rpc, +N_ ("quit after a single request/response exchange" )), +OPT_BOOL (0 ,"advertise-capabilities" , &opts.advertise_capabilities, +N_ ("exit immediately after advertising capabilities" )), +OPT_END () + }; + +/* ignore all unknown cmdline switches for now */ + argc =parse_options (argc, argv, prefix, options, serve_usage, +PARSE_OPT_KEEP_DASHDASH | +PARSE_OPT_KEEP_UNKNOWN ); +serve (&opts); + +return 0 ; +}
git.c +1 -0
@@ -461,6 +461,7 @@ static struct cmd_struct commands[] = { { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, { "rm", cmd_rm, RUN_SETUP }, { "send-pack", cmd_send_pack, RUN_SETUP }, + { "serve", cmd_serve, RUN_SETUP }, { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER }, { "show", cmd_show, RUN_SETUP }, { "show-branch", cmd_show_branch, RUN_SETUP },
serve.c new file +250 -0
250 lines not on this page · raw message
serve.h new file +15 -0
@@ -0,0 +1,15 @@ +#ifndef SERVE_H +#define SERVE_H + +struct argv_array ; +extern int has_capability (const struct argv_array *keys,const char *capability, +const char **value); + +struct serve_options { +unsigned advertise_capabilities; +unsigned stateless_rpc; +}; +#define SERVE_OPTIONS_INIT { 0 } +extern void serve (struct serve_options *options); + +#endif /* SERVE_H */
t/t5701-git-serve.sh new file +60 -0
60 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 7 lines · On Tue, 6 Feb 2018 17:12:49 -0800 Brandon Williams <[email protected]> wrote:
.gitignore | 1 + Documentation/technical/protocol-v2.txt | 114 +++++++++++++++ Makefile | 2 + builtin.h | 1 + builtin/serve.c | 30 ++++ git.c | 1 + serve.c | 250 ++++++++++++++++++++++++++++++++
Quoted 8 lines
serve.h | 15 ++ t/t5701-git-serve.sh | 60 ++++++++ 9 files changed, 474 insertions(+) create mode 100644 Documentation/technical/protocol-v2.txt create mode 100644 builtin/serve.c create mode 100644 serve.c create mode 100644 serve.h create mode 100755 t/t5701-git-serve.sh
As someone who is implementing the server side of protocol V2 in JGit, I now have a bit more insight into this :-)
First of all, I used to not have a strong opinion on the existence of a new endpoint, but now I think that it's better to *not* have git-serve. As it is, as far as I can tell, upload-pack also needs to support (and does support, as of the end of this patch set) protocol v2 anyway, so it might be better to merely upgrade upload-pack.
Documentation/technical/protocol-v2.txt line 69
Quoted 7 lines
Documentation/technical/protocol-v2.txt new file +7
-0+A client then responds to select the command it wants with any particular +capabilities or arguments. There is then an optional section where the +client can provide any command specific parameters or queries. + + command-request = command + capability-list + (command-args)
If you are stating that this is optional, write "*1command-args". (RFC 5234 also supports square brackets, but "*1" is already used in pack-protocol.txt and http-protocol.txt.)
Documentation/technical/protocol-v2.txt line 74
Quoted 5 lines
Documentation/technical/protocol-v2.txt new file +5
-0+ flush-pkt + command = PKT-LINE("command=" key LF) + command-args = delim-pkt + *arg + arg = 1*CHAR
arg should be wrapped in PKT-LINE, I think, and terminated by an LF.
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 19 lines · Jonathan Tan
Quoted 7 lines · On Tue, 6 Feb 2018 17:12:49 -0800 Brandon Williams <[email protected]> wrote:
.gitignore | 1 + Documentation/technical/protocol-v2.txt | 114 +++++++++++++++ Makefile | 2 + builtin.h | 1 + builtin/serve.c | 30 ++++ git.c | 1 + serve.c | 250 ++++++++++++++++++++++++++++++++Quoted 8 lines
serve.h | 15 ++ t/t5701-git-serve.sh | 60 ++++++++ 9 files changed, 474 insertions(+) create mode 100644 Documentation/technical/protocol-v2.txt create mode 100644 builtin/serve.c create mode 100644 serve.c create mode 100644 serve.h create mode 100755 t/t5701-git-serve.sh
Quoted 8 lines
As someone who is implementing the server side of protocol V2 in JGit, I now have a bit more insight into this :-)
First of all, I used to not have a strong opinion on the existence of a new endpoint, but now I think that it's better to *not* have git-serve. As it is, as far as I can tell, upload-pack also needs to support (and does support, as of the end of this patch set) protocol v2 anyway, so it might be better to merely upgrade upload-pack.
Having it allows for easier testing and the easy ability to make it a true endpoint when we want to. As of right now, git-serve isn't an endpoint as you can't issue requests there via http-backend or git-daemon.
Quoted 20 lines
Documentation/technical/protocol-v2.txt line 69
Quoted 8 lines
Documentation/technical/protocol-v2.txt new file +7
-0+A client then responds to select the command it wants with any particular +capabilities or arguments. There is then an optional section where the +client can provide any command specific parameters or queries. + + command-request = command + capability-list + (command-args)If you are stating that this is optional, write "*1command-args". (RFC 5234 also supports square brackets, but "*1" is already used in pack-protocol.txt and http-protocol.txt.)
Documentation/technical/protocol-v2.txt line 74
Quoted 5 lines
Documentation/technical/protocol-v2.txt new file +5
-0+ flush-pkt + command = PKT-LINE("command=" key LF) + command-args = delim-pkt + *arg + arg = 1*CHARarg should be wrapped in PKT-LINE, I think, and terminated by an LF.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 10 lines · On Fri, 23 Feb 2018 13:33:15 -0800 Brandon Williams <[email protected]> wrote:
Quoted 8 lines · Jonathan Tan
As someone who is implementing the server side of protocol V2 in JGit, I now have a bit more insight into this :-)
First of all, I used to not have a strong opinion on the existence of a new endpoint, but now I think that it's better to *not* have git-serve. As it is, as far as I can tell, upload-pack also needs to support (and does support, as of the end of this patch set) protocol v2 anyway, so it might be better to merely upgrade upload-pack.
Quoted 4 lines
Having it allows for easier testing and the easy ability to make it a true endpoint when we want to. As of right now, git-serve isn't an endpoint as you can't issue requests there via http-backend or git-daemon.
Is git-serve planned to be a new endpoint?
If yes, I now don't think it's a good idea - it's an extra burden to reimplementors without much benefit (to have a new endpoint that does the same things as upload-pack).
If not, I don't think that easier testing makes it worth having an extra binary. Couldn't the same tests be done by running upload-pack directly?
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 18 lines · Jonathan Tan
Quoted 10 lines · On Fri, 23 Feb 2018 13:33:15 -0800 Brandon Williams <[email protected]> wrote:
Quoted 8 lines · Jonathan Tan
As someone who is implementing the server side of protocol V2 in JGit, I now have a bit more insight into this :-)
First of all, I used to not have a strong opinion on the existence of a new endpoint, but now I think that it's better to *not* have git-serve. As it is, as far as I can tell, upload-pack also needs to support (and does support, as of the end of this patch set) protocol v2 anyway, so it might be better to merely upgrade upload-pack.
Quoted 4 lines
Having it allows for easier testing and the easy ability to make it a true endpoint when we want to. As of right now, git-serve isn't an endpoint as you can't issue requests there via http-backend or git-daemon.
Quoted 5 lines
Is git-serve planned to be a new endpoint?
If yes, I now don't think it's a good idea - it's an extra burden to reimplementors without much benefit (to have a new endpoint that does the same things as upload-pack).
I'm still going to include it, with the potential for it to become an endpoint if we so choose (it isn't now), because when we start to introduce more things to v2 (push or other commands we haven't dreamed up yet) it just makes more sense to contact an endpoint that doesn't explicitly say what it does.
Quoted 3 lines
If not, I don't think that easier testing makes it worth having an extra binary. Couldn't the same tests be done by running upload-pack directly?
its builtin and not a new binary, and yes it makes testing much easier because its assumes v2 from the start instead of v0.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 12/35] serve: introduce git-serve
In reply to Brandon WilliamsDocumentation/technical/protocol-v2.txt line 30
Quoted 5 lines · Brandon Williams
Documentation/technical/protocol-v2.txt new file +5
-0+In protocol v2 communication is command oriented. When first contacting a +server a list of capabilities will advertised. Some of these capabilities +will be commands which a client can request be executed. Once a command +has completed, a client can reuse the connection and request that other +commands be executed.
If I understand this correctly, we'll potentially have a lot more round-trips between the client and server (one per "command"). And for git-over-http, each one will be its own HTTP request?
We've traditionally tried to minimize HTTP requests, but I guess it's not too bad if we can keep the connection open in most cases. Then we just suffer some extra framing bytes, but we don't have to re-establish the TCP connection each time.
I do wonder if the extra round trips will be noticeable in high-latency conditions. E.g., if I'm 200ms away, converting the current ref-advertisement spew to "capabilities, then the client asks for refs, then we spew the refs" is going to cost an extra 200ms, even if the fetch just ends up being a noop. I'm not sure how bad that is in the grand scheme of things (after all, the TCP handshake involves some round-trips, too).
Quoted 25 lines
+ Capability Advertisement
+--------------------------Documentation/technical/protocol-v2.txt new file +23
-0+ +A server which decides to communicate (based on a request from a client) +using protocol version 2, notifies the client by sending a version string +in its initial response followed by an advertisement of its capabilities. +Each capability is a key with an optional value. Clients must ignore all +unknown keys. Semantics of unknown values are left to the definition of +each key. Some capabilities will describe commands which can be requested +to be executed by the client. + + capability-advertisement = protocol-version + capability-list + flush-pkt + + protocol-version = PKT-LINE("version 2" LF) + capability-list = *capability + capability = PKT-LINE(key[=value] LF) + + key = 1*CHAR + value = 1*CHAR + CHAR = 1*(ALPHA / DIGIT / "-" / "_") + +A client then responds to select the command it wants with any particular +capabilities or arguments. There is then an optional section where the
Documentation/technical/protocol-v2.txt line 74
Quoted 10 lines
Documentation/technical/protocol-v2.txt new file +10
-0+client can provide any command specific parameters or queries. + + command-request = command + capability-list + (command-args) + flush-pkt + command = PKT-LINE("command=" key LF) + command-args = delim-pkt + *arg + arg = 1*CHAR
For a single stateful TCP connection like git:// or git-over-ssh, the client would get the capabilities once and then issue a series of commands. For git-over-http, how does it work?
The client speaks first in HTTP, so we'd first make a request to get just the capabilities from the server? And then proceed from there with a series of requests, assuming that the capabilities for each server we subsequently contact are the same? That's probably reasonable (and certainly the existing http protocol makes that capabilities assumption).
I don't see any documentation on how this all works with http. But reading patch 34, it looks like we just do the usual service=git-upload-pack request (with the magic request for v2), and then the server would send us capabilities. Which follows my line of thinking in the paragraph above.
-Peff
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 17 lines · Jeff King
Documentation/technical/protocol-v2.txt line 30
Quoted 5 lines · Brandon Williams
Documentation/technical/protocol-v2.txt new file +5
-0+In protocol v2 communication is command oriented. When first contacting a +server a list of capabilities will advertised. Some of these capabilities +will be commands which a client can request be executed. Once a command +has completed, a client can reuse the connection and request that other +commands be executed.If I understand this correctly, we'll potentially have a lot more round-trips between the client and server (one per "command"). And for git-over-http, each one will be its own HTTP request?
We've traditionally tried to minimize HTTP requests, but I guess it's not too bad if we can keep the connection open in most cases. Then we just suffer some extra framing bytes, but we don't have to re-establish the TCP connection each time.
Quoted 7 lines
I do wonder if the extra round trips will be noticeable in high-latency conditions. E.g., if I'm 200ms away, converting the current ref-advertisement spew to "capabilities, then the client asks for refs, then we spew the refs" is going to cost an extra 200ms, even if the fetch just ends up being a noop. I'm not sure how bad that is in the grand scheme of things (after all, the TCP handshake involves some round-trips, too).
I think this is the price of extending the protocol in a backward compatible way. If we don't want to be backwards compatible (allowing for graceful fallback to v1) then we could design this differently. Even so we're not completely out of luck just yet.
Back when I introduced the GIT_PROTOCOL side-channel I was able to demonstrate that arbitrary data could be sent to the server and it would only respect the stuff it knows about. This means that we can do a follow up to v2 at some point to introduce an optimization where we can stuff a request into GIT_PROTOCOL and short-circuit the first round-trip if the server supports it.
Quoted 41 lines
Quoted 26 lines
+ Capability Advertisement
+--------------------------Documentation/technical/protocol-v2.txt new file +23
-0+ +A server which decides to communicate (based on a request from a client) +using protocol version 2, notifies the client by sending a version string +in its initial response followed by an advertisement of its capabilities. +Each capability is a key with an optional value. Clients must ignore all +unknown keys. Semantics of unknown values are left to the definition of +each key. Some capabilities will describe commands which can be requested +to be executed by the client. + + capability-advertisement = protocol-version + capability-list + flush-pkt + + protocol-version = PKT-LINE("version 2" LF) + capability-list = *capability + capability = PKT-LINE(key[=value] LF) + + key = 1*CHAR + value = 1*CHAR + CHAR = 1*(ALPHA / DIGIT / "-" / "_") + +A client then responds to select the command it wants with any particular +capabilities or arguments. There is then an optional section where theDocumentation/technical/protocol-v2.txt line 74
Quoted 10 lines
Documentation/technical/protocol-v2.txt new file +10
-0+client can provide any command specific parameters or queries. + + command-request = command + capability-list + (command-args) + flush-pkt + command = PKT-LINE("command=" key LF) + command-args = delim-pkt + *arg + arg = 1*CHARFor a single stateful TCP connection like
git://or git-over-ssh, the client would get the capabilities once and then issue a series of commands. For git-over-http, how does it work?
Quoted 8 lines
The client speaks first in HTTP, so we'd first make a request to get just the capabilities from the server? And then proceed from there with a series of requests, assuming that the capabilities for each server we subsequently contact are the same? That's probably reasonable (and certainly the existing http protocol makes that capabilities assumption).
I don't see any documentation on how this all works with http. But
I can add in a bit for the initial request when using http, but the rest of it should function the same.
Quoted 4 lines
reading patch 34, it looks like we just do the usual service=git-upload-pack request (with the magic request for v2), and then the server would send us capabilities. Which follows my line of thinking in the paragraph above.
Yes this is exactly how it should work. First we make an info/refs request and if the server speaks v2 then instead of a refs request we should get back a capability listing. Then subsequent requests are made assuming the capabilities are the same like we've done with the existing protocol.
The great thing about this is that from the POV of the git-client, it doesn't care if its speaking using the git://, ssh://, file://, or
http:// transport; it's all the same protocol. In my next re-roll I'll even drop the "# service" bit from the http server response and then the responses will truly be identical in all cases.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 11 lines · Brandon Williams
I think this is the price of extending the protocol in a backward compatible way. If we don't want to be backwards compatible (allowing for graceful fallback to v1) then we could design this differently. Even so we're not completely out of luck just yet.
Back when I introduced the
GIT_PROTOCOLside-channel I was able to demonstrate that arbitrary data could be sent to the server and it would only respect the stuff it knows about. This means that we can do a follow up to v2 at some point to introduce an optimization where we can stuff a request intoGIT_PROTOCOLand short-circuit the first round-trip if the server supports it.
If that's our end-game, it does make me wonder if we'd be happier just jumping to that at first. Before you started the v2 protocol work, I had a rough patch series passing what I called "early capabilities". The idea was to let the client speak a few optional capabilities before the ref advertisement, and be ready for the server to ignore them completely. That doesn't clean up all the warts with the v0 protocol, but it handles the major one (allowing more efficient ref advertisements).
I dunno. There's a lot more going on here in v2 and I'm not sure I've fully digested it.
Quoted 5 lines
The great thing about this is that from the POV of the git-client, it doesn't care if its speaking using the
git://, ssh://, file://, or
http:// transport; it's all the same protocol. In my next re-roll I'll even drop the "# service" bit from the http server response and then the responses will truly be identical in all cases.
This part has me a little confused still. The big difference between http and the other protocols is that the other ones are full-duplex, and http is a series of stateless request/response pairs.
Are the other protocols becoming stateless request/response pairs, too? Or will they be "the same protocol" only in the sense of using the same transport?
(There are a lot of reasons not to like the stateless pair thing; it has some horrid corner cases during want/have negotiation).
-Peff
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 14 lines · Jeff King
Quoted 11 lines · Brandon Williams
I think this is the price of extending the protocol in a backward compatible way. If we don't want to be backwards compatible (allowing for graceful fallback to v1) then we could design this differently. Even so we're not completely out of luck just yet.
Back when I introduced the
GIT_PROTOCOLside-channel I was able to demonstrate that arbitrary data could be sent to the server and it would only respect the stuff it knows about. This means that we can do a follow up to v2 at some point to introduce an optimization where we can stuff a request intoGIT_PROTOCOLand short-circuit the first round-trip if the server supports it.
Quoted 8 lines
If that's our end-game, it does make me wonder if we'd be happier just jumping to that at first. Before you started the v2 protocol work, I had a rough patch series passing what I called "early capabilities". The idea was to let the client speak a few optional capabilities before the ref advertisement, and be ready for the server to ignore them completely. That doesn't clean up all the warts with the v0 protocol, but it handles the major one (allowing more efficient ref advertisements).
I didn't really want to get to that just yet, simply because I want to try and keep the scope of this smaller while still being able to fix most of the issues we have with v0.
Quoted 2 lines
I dunno. There's a lot more going on here in v2 and I'm not sure I've fully digested it.
I tried to keep it similar enough to v0 such that it wouldn't be that big of a leap (small steps). For example negotiation is really done the same as it is in v0 during fetch (a next step would be to actually improve that). We can definitely talk about all this in more detail later this week too.
Quoted 11 lines
Quoted 6 lines
The great thing about this is that from the POV of the git-client, it doesn't care if its speaking using the
git://, ssh://, file://, or
http:// transport; it's all the same protocol. In my next re-roll I'll even drop the "# service" bit from the http server response and then the responses will truly be identical in all cases.This part has me a little confused still. The big difference between http and the other protocols is that the other ones are full-duplex, and http is a series of stateless request/response pairs.
Quoted 6 lines
Are the other protocols becoming stateless request/response pairs, too? Or will they be "the same protocol" only in the sense of using the same transport?
(There are a lot of reasons not to like the stateless pair thing; it has some horrid corner cases during want/have negotiation).
Junio made a comment on the Spec in the most recent version of the series about how I state that v2 is stateless and "MUST NOT" rely on state being stored on the server side. In reality I think this needs to be tweaked a bit because when you do have a full-duplex connection you may probably want to use that to reduce the amount of data that you send in some cases.
In the current protocol http has a lot of additional stuff that's had to be done to it to get it to work with a protocol that was designed to be stateful first. What I want is for the protocol to be designed stateless first so that http functions essentially the same as ssh or file or git transports and we don't have to do any hackery to get it to work. This also makes it very simple to implement a new feature in the protocol because you only need to think about implementing it once instead of twice like you kind of have to do with v0. So in the most recent series everything is a chain of request/response pairs even in the non-http cases.
In a previous version of the series I had each command being able to last any number of rounds and having a 'stateless' capability indicating if the command needed to be run stateless. I didn't think that was a good design because by default you are still designing the stateful thing first and the http (stateless) case can be an afterthought. So instead maybe we'll need commands which can benefit from state to have a 'stateful' feature that can be advertised when a full-duplex connection is possible. This still gives you the opportunity to not advertise that and have the same behavior over ssh as http. I actually remember hearing someone talk about how they would like to allow for ssh connections to their server and just have it be a proxy for http and this would enable that.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 10 lines · Brandon Williams
In the current protocol http has a lot of additional stuff that's had to be done to it to get it to work with a protocol that was designed to be stateful first. What I want is for the protocol to be designed stateless first so that http functions essentially the same as ssh or file or git transports and we don't have to do any hackery to get it to work. This also makes it very simple to implement a new feature in the protocol because you only need to think about implementing it once instead of twice like you kind of have to do with v0. So in the most recent series everything is a chain of request/response pairs even in the non-http cases.
I agree that would be a lot more pleasant for adding protocol features. But I just worry that the stateful protocols get a lot less efficient. I'm having trouble coming up with an easy reproduction, but my recollection is that http has some nasty corner cases, because each round of "have" lines sent to the server has to summarize the previous conversation. So you can get a case where the client's requests keep getting bigger and bigger during the negotiation (and eventually getting large enough to cause problems).
If anything, I wish we could push the http protocol in a more stateful direction with something like websockets. But I suspect that's an unrealistic dream, just because not everybody's http setup (proxies, etc) will be able to handle that.
-Peff
Re: [PATCH v3 12/35] serve: introduce git-serve
Hi,
Quoted 8 lines · Jeff King
I agree that would be a lot more pleasant for adding protocol features. But I just worry that the stateful protocols get a lot less efficient. I'm having trouble coming up with an easy reproduction, but my recollection is that http has some nasty corner cases, because each round of "have" lines sent to the server has to summarize the previous conversation. So you can get a case where the client's requests keep getting bigger and bigger during the negotiation (and eventually getting large enough to cause problems).
That's not so much a corner case as just how negotiation works over http.
We want to do better (e.g. see [1]) but that's a bigger change than the initial protocol v2.
As Brandon explained it to me, we really do want to use stateless-rpc semantics by default, since that's just better for maintainability. Instead of having two protocols, one that is sane and one that struggles to hoist that into stateless-rpc, there would be one stateless baseline plus capabilities to make use of state.
For example, it would be nice to have a capability to remember negotiation state between rounds, to get around exactly the problem you're describing when using a stateful protocol. Stateless backends would just not advertise such a capability. But doing that without [1] still sort of feels like a cop-out. If we can get a reasonable baseline using ideas like [1] and then have a capability to keep server-side state as icing on the cake instead of having a negotiation process that only really makes sense when you have server-side state, then that would be even better.
Quoted 4 lines
If anything, I wish we could push the http protocol in a more stateful direction with something like websockets. But I suspect that's an unrealistic dream, just because not everybody's http setup (proxies, etc) will be able to handle that.
Agreed. I think we have to continue to deal with stateless-rpc semantics, at least for the near future.
Jonathan
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 11 lines · Jonathan Nieder
Quoted 8 lines
I agree that would be a lot more pleasant for adding protocol features. But I just worry that the stateful protocols get a lot less efficient. I'm having trouble coming up with an easy reproduction, but my recollection is that http has some nasty corner cases, because each round of "have" lines sent to the server has to summarize the previous conversation. So you can get a case where the client's requests keep getting bigger and bigger during the negotiation (and eventually getting large enough to cause problems).
That's not so much a corner case as just how negotiation works over http.
Sure. What I meant more was "there are corner cases where it gets out of control and doesn't work".
I have had to give the advice in the past "if your fetch over http doesn't work, try it over ssh". If we change the ssh protocol to be stateless, too, then that closes that escape hatch.
I haven't had to give that advice for a while, though. Maybe tweaks to the parameters or just larger buffers have made the problem go away over the years?
Quoted 8 lines
We want to do better (e.g. see [1]) but that's a bigger change than the initial protocol v2.
As Brandon explained it to me, we really do want to use stateless-rpc semantics by default, since that's just better for maintainability. Instead of having two protocols, one that is sane and one that struggles to hoist that into stateless-rpc, there would be one stateless baseline plus capabilities to make use of state.
Yes, I think that would be a nice end-game. It just wasn't clear to me where we'd be in the interim.
-Peff
Re: [PATCH v3 12/35] serve: introduce git-serve
Quoted 11 lines · Jeff King
Quoted 8 lines
We want to do better (e.g. see [1]) but that's a bigger change than the initial protocol v2.
As Brandon explained it to me, we really do want to use stateless-rpc semantics by default, since that's just better for maintainability. Instead of having two protocols, one that is sane and one that struggles to hoist that into stateless-rpc, there would be one stateless baseline plus capabilities to make use of state.
Yes, I think that would be a nice end-game. It just wasn't clear to me where we'd be in the interim.
After some more thinking about this, and a little chatting with Brandon at the contrib summit, I'm willing to soften my position on this.
Basically I was concerned about this as a regression where git-over-ssh would stop working in a few corner cases. And it would cease to be available as an escape hatch for those cases where http wouldn't work.
But we may be OK in this "interim" period (before unified stateful-negotiation bits are added back) because v2 would not yet be the default. So the ssh cases can't regress without flipping the v2 switch manually, and any escape hatch would continue to work by flipping back to v1 anyway.
So it's probably OK to continue experimenting in this direction and see how often it's a problem in practice.
-Peff
PATCH v3 13/35 ls-refs: introduce ls-refs server command
In reply to Brandon WilliamsIntroduce the ls-refs server command. In protocol v2, the ls-refs command is used to request the ref advertisement from the server. Since it is a command which can be requested (as opposed to mandatory in v1), a client can sent a number of parameters in its request to limit the ref advertisement based on provided ref-patterns.
--- Documentation/technical/protocol-v2.txt | 32 +++++++++ Makefile | 1 + ls-refs.c | 96 ++++++++++++++++++++++++++ ls-refs.h | 9 +++ serve.c | 8 +++ t/t5701-git-serve.sh | 115 ++++++++++++++++++++++++++++++++ 6 files changed, 261 insertions(+) create mode 100644 ls-refs.c create mode 100644 ls-refs.h
Documentation/technical/protocol-v2.txt +32 -0
@@ -112,3 +112,35 @@ printable ASCII characters except space (i.e., the byte range 32 < x < "git/1.8.3.1"). The agent strings are purely informative for statistics and debugging purposes, and MUST NOT be used to programmatically assume the presence or absence of particular features. + + ls-refs +--------- + +`ls-refs` is the command used to request a reference advertisement in v2. +Unlike the current reference advertisement, ls-refs takes in parameters +which can be used to limit the refs sent from the server. + +Additional features not supported in the base command will be advertised +as the value of the command in the capability advertisement in the form +of a space separated list of features, e.g. "<command>=<feature 1> +<feature 2>". + +ls-refs takes in the following parameters wrapped in packet-lines: + + symrefs + In addition to the object pointed by it, show the underlying ref + pointed by it when showing a symbolic ref. + peel + Show peeled tags. + ref-pattern <pattern> + When specified, only references matching the one of the provided + patterns are displayed. + +The output of ls-refs is as follows: + + output = *ref + flush-pkt + ref = PKT-LINE(obj-id SP refname *(SP ref-attribute) LF) + ref-attribute = (symref | peeled) + symref = "symref-target:" symref-target + peeled = "peeled:" obj-id
Makefile +1 -0
@@ -825,6 +825,7 @@ LIB_OBJS += list-objects-filter-options.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o +LIB_OBJS += ls-refs.o LIB_OBJS += mailinfo.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o
ls-refs.c new file +96 -0
96 lines not on this page · raw message
ls-refs.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef LS_REFS_H +#define LS_REFS_H + +struct repository ; +struct argv_array ; +extern int ls_refs (struct repository *r,struct argv_array *keys, +struct argv_array *args); + +#endif /* LS_REFS_H */
serve.c +8 -0
@@ -4,8 +4,15 @@#include "pkt-line.h" #include "version.h" #include "argv-array.h" +#include "ls-refs.h" #include "serve.h" +static int always_advertise (struct repository *r, +struct strbuf *value) +{ +return 1 ; +} +static int agent_advertise (struct repository *r,struct strbuf *value) {
@@ -44,6 +51,7 @@ struct protocol_capability {static struct protocol_capability capabilities[] = { {"agent" , agent_advertise,NULL }, + {"ls-refs" , always_advertise, ls_refs }, };static void advertise_capabilities (void )
t/t5701-git-serve.sh +115 -0
125 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Documentation/technical/protocol-v2.txt printable ASCII characters except space (i.e., the byte range 32 < x < · line 137
Quoted 10 lines · Brandon Williams
Documentation/technical/protocol-v2.txt +10
-0+ls-refs takes in the following parameters wrapped in packet-lines: + + symrefs + In addition to the object pointed by it, show the underlying ref + pointed by it when showing a symbolic ref. + peel + Show peeled tags. + ref-pattern <pattern> + When specified, only references matching the one of the provided + patterns are displayed.
How do we match those patterns? That's probably an important thing to include in the spec.
Looking at the code, I see:
ls-refs.c line 19
Quoted 5 lines
ls-refs.c new file +5
-0+/* +* Check if one of the patterns matches the tail part of the ref. +* If no patterns were provided, all refs match. +*/ +static int ref_match (const struct argv_array *patterns,const char *refname)
This kind of tail matching can't quite implement all of the current behavior. Because we actually do the normal dwim_ref() matching, which includes stuff like "refs/remotes/%s/HEAD".
The other problem with tail-matching is that it's inefficient on the server. Ideally we could get a request for "master" and only look up refs/heads/master, refs/tags/master, etc. And if there are 50,000 refs in refs/pull, we wouldn't have to process those at all. Of course this is no worse than the current code, which not only looks at each ref but actually _sends_ it. But it would be nice if we could fix this.
There's some more discussion in this old thread:
Quoted 7 lines
ls-refs.c new file +7
-0+{ +char *pathbuf; +int i; + +if (!patterns->argc) +return 1 ;/* no restriction */ +
ls-refs.c line 36
Quoted 10 lines
ls-refs.c new file +10
-0+ pathbuf =xstrfmt ("/%s" , refname); +for (i =0 ; i < patterns->argc; i++) { +if (!wildmatch (patterns->argv[i], pathbuf,0 )) { +free (pathbuf); +return 1 ; + } + } +free (pathbuf); +return 0 ; +}
Does the client have to be aware that we're using wildmatch? I think they'd need "refs/heads/**" to actually implement what we usually specify in refspecs as "refs/heads/*". Or does the lack of WM_PATHNAME make this work with just "*"?
Do we anticipate that the client would left-anchor the refspec like "/refs/heads/*" so that in theory the server could avoid looking outside of /refs/heads/?
-Peff
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Quoted 15 lines · Jeff King
Documentation/technical/protocol-v2.txt printable ASCII characters except space (i.e., the byte range 32 < x < · line 137
Quoted 10 lines · Brandon Williams
Documentation/technical/protocol-v2.txt +10
-0+ls-refs takes in the following parameters wrapped in packet-lines: + + symrefs + In addition to the object pointed by it, show the underlying ref + pointed by it when showing a symbolic ref. + peel + Show peeled tags. + ref-pattern <pattern> + When specified, only references matching the one of the provided + patterns are displayed.How do we match those patterns? That's probably an important thing to include in the spec.
Yeah I thought about it when I first wrote it and was hoping that someone who nudge me in the right direction :)
Quoted 20 lines
Looking at the code, I see:
ls-refs.c line 19
Quoted 5 lines
ls-refs.c new file +5
-0+/* +* Check if one of the patterns matches the tail part of the ref. +* If no patterns were provided, all refs match. +*/ +static int ref_match (const struct argv_array *patterns,const char *refname)This kind of tail matching can't quite implement all of the current behavior. Because we actually do the normal
dwim_ref()matching, which includes stuff like "refs/remotes/%s/HEAD".The other problem with tail-matching is that it's inefficient on the server. Ideally we could get a request for "master" and only look up refs/heads/master, refs/tags/master, etc. And if there are 50,000 refs in refs/pull, we wouldn't have to process those at all. Of course this is no worse than the current code, which not only looks at each ref but actually _sends_ it. But it would be nice if we could fix this.
Quoted 3 lines
There's some more discussion in this old thread:
Thanks for the pointer. I was told to be wary a while about about performance implications on the server but no discussion ensued till now about it :)
We always have the ability to extend the patterns accepted via a feature (or capability) to ls-refs, so maybe the best thing to do now would only support a few patterns with specific semantics. Something like if you say "master" only match against refs/heads/ and refs/tags/ and if you want something else you would need to specify "refs/pull/master"?
That way we could only support globs at the end "master*" where * can match anything (including slashes)
Quoted 24 lines
Quoted 8 lines
ls-refs.c new file +7
-0+{ +char *pathbuf; +int i; + +if (!patterns->argc) +return 1 ;/* no restriction */ +ls-refs.c line 36
Quoted 10 lines
ls-refs.c new file +10
-0+ pathbuf =xstrfmt ("/%s" , refname); +for (i =0 ; i < patterns->argc; i++) { +if (!wildmatch (patterns->argv[i], pathbuf,0 )) { +free (pathbuf); +return 1 ; + } + } +free (pathbuf); +return 0 ; +}Does the client have to be aware that we're using wildmatch? I think they'd need "refs/heads/**" to actually implement what we usually specify in refspecs as "refs/heads/*". Or does the lack of
WM_PATHNAMEmake this work with just "*"?
Quoted 3 lines
Do we anticipate that the client would left-anchor the refspec like "/refs/heads/*" so that in theory the server could avoid looking outside of /refs/heads/?
Yeah we may want to anchor it by providing the leading '/' instead of just "refs/<blah>".
Quoted 2 lines
-Peff
I need to read over the discussion you linked to more but what sort of ref patterns do you believe we should support as part of the initial release of v2? It seems like you wanted this at some point in the past so I assume you have an idea of what sort of filtering would be beneficial.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Quoted 48 lines · Brandon Williams
Quoted 15 lines · Jeff King
Documentation/technical/protocol-v2.txt printable ASCII characters except space (i.e., the byte range 32 < x < · line 137
Quoted 10 lines · Brandon Williams
Documentation/technical/protocol-v2.txt +10
-0+ls-refs takes in the following parameters wrapped in packet-lines: + + symrefs + In addition to the object pointed by it, show the underlying ref + pointed by it when showing a symbolic ref. + peel + Show peeled tags. + ref-pattern <pattern> + When specified, only references matching the one of the provided + patterns are displayed.How do we match those patterns? That's probably an important thing to include in the spec.
Yeah I thought about it when I first wrote it and was hoping that someone who nudge me in the right direction :)
Quoted 20 lines
Looking at the code, I see:
ls-refs.c line 19
Quoted 5 lines
ls-refs.c new file +5
-0+/* +* Check if one of the patterns matches the tail part of the ref. +* If no patterns were provided, all refs match. +*/ +static int ref_match (const struct argv_array *patterns,const char *refname)This kind of tail matching can't quite implement all of the current behavior. Because we actually do the normal
dwim_ref()matching, which includes stuff like "refs/remotes/%s/HEAD".The other problem with tail-matching is that it's inefficient on the server. Ideally we could get a request for "master" and only look up refs/heads/master, refs/tags/master, etc. And if there are 50,000 refs in refs/pull, we wouldn't have to process those at all. Of course this is no worse than the current code, which not only looks at each ref but actually _sends_ it. But it would be nice if we could fix this.
Quoted 3 lines
There's some more discussion in this old thread:
Thanks for the pointer. I was told to be wary a while about about performance implications on the server but no discussion ensued till now about it :)
Quoted 8 lines
We always have the ability to extend the patterns accepted via a feature (or capability) to ls-refs, so maybe the best thing to do now would only support a few patterns with specific semantics. Something like if you say "master" only match against refs/heads/ and refs/tags/ and if you want something else you would need to specify "refs/pull/master"?
That way we could only support globs at the end "master*" where * can match anything (including slashes)
After some in-office discussion it seems like the best thing to do for this (right now since if we change our mind we can just introduce a capability which extends the patterns supported) would be to left-anchor the ref-patterns and only allow for a single wildcard character '*' which matches zero or more characters (and doesn't care about slashes '/'). This wildcard character should only be supported at the end of the ref pattern. This means that if a client wants 'master' then they would need to specify 'refs/heads/master' (and the other ref_rev_parse_rules expansions) as a ref pattern. But they could say "refs/heads/*" for all refs under refs/heads.
Quoted 43 lines
Quoted 25 lines
Quoted 9 lines
ls-refs.c new file +7
-0+{ +char *pathbuf; +int i; + +if (!patterns->argc) +return 1 ;/* no restriction */ +ls-refs.c line 36
Quoted 10 lines
ls-refs.c new file +10
-0+ pathbuf =xstrfmt ("/%s" , refname); +for (i =0 ; i < patterns->argc; i++) { +if (!wildmatch (patterns->argv[i], pathbuf,0 )) { +free (pathbuf); +return 1 ; + } + } +free (pathbuf); +return 0 ; +}Does the client have to be aware that we're using wildmatch? I think they'd need "refs/heads/**" to actually implement what we usually specify in refspecs as "refs/heads/*". Or does the lack of
WM_PATHNAMEmake this work with just "*"?Quoted 3 lines
Do we anticipate that the client would left-anchor the refspec like "/refs/heads/*" so that in theory the server could avoid looking outside of /refs/heads/?
Yeah we may want to anchor it by providing the leading '/' instead of just "refs/<blah>".
Quoted 2 lines
-Peff
I need to read over the discussion you linked to more but what sort of ref patterns do you believe we should support as part of the initial release of v2? It seems like you wanted this at some point in the past so I assume you have an idea of what sort of filtering would be beneficial.
Signature · 1 line
Brandon Williams
Signature · 1 line
Brandon Williams
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Quoted 9 lines · Brandon Williams
Quoted 8 lines
We always have the ability to extend the patterns accepted via a feature (or capability) to ls-refs, so maybe the best thing to do now would only support a few patterns with specific semantics. Something like if you say "master" only match against refs/heads/ and refs/tags/ and if you want something else you would need to specify "refs/pull/master"?
That way we could only support globs at the end "master*" where * can match anything (including slashes)
Quoted 10 lines
After some in-office discussion it seems like the best thing to do for this (right now since if we change our mind we can just introduce a capability which extends the patterns supported) would be to left-anchor the ref-patterns and only allow for a single wildcard character '*' which matches zero or more characters (and doesn't care about slashes '/'). This wildcard character should only be supported at the end of the ref pattern. This means that if a client wants 'master' then they would need to specify 'refs/heads/master' (and the other
ref_rev_parse_rulesexpansions) as a ref pattern. But they could say "refs/heads/*" for all refs under refs/heads.
Heh, I just responded without having read this and came up with the same suggestion.
So I agree that is the right path. Or the simplification I mentioned that "refs/heads/master" would return that ref or possibly "refs/heads/master/foo" if it exists. Remember that it's fine to be overly broad here. This is purely an optimization in the advertisement, as we'd still pick out the refs we care about in a separate step.
-Peff
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
In reply to Brandon WilliamsQuoted 19 lines · Brandon Williams
Quoted 11 lines
This kind of tail matching can't quite implement all of the current behavior. Because we actually do the normal
dwim_ref()matching, which includes stuff like "refs/remotes/%s/HEAD".The other problem with tail-matching is that it's inefficient on the server. Ideally we could get a request for "master" and only look up refs/heads/master, refs/tags/master, etc. And if there are 50,000 refs in refs/pull, we wouldn't have to process those at all. Of course this is no worse than the current code, which not only looks at each ref but actually _sends_ it. But it would be nice if we could fix this.
Quoted 3 lines
There's some more discussion in this old thread:
Thanks for the pointer. I was told to be wary a while about about performance implications on the server but no discussion ensued till now about it :)
Quoted 5 lines
We always have the ability to extend the patterns accepted via a feature (or capability) to ls-refs, so maybe the best thing to do now would only support a few patterns with specific semantics. Something like if you say "master" only match against refs/heads/ and refs/tags/ and if you want something else you would need to specify "refs/pull/master"?
The big question is whether you want to break compatibility with the existing program behavior. If not, then I think you have to ask for every variant in ref_rev_parse_rules (of which there are 6 variants).
Which sounds pretty gross, but it actually may not be _too_ bad. Most fetches tend to ask for either a single name, or they use left-anchored wildcards. So it would work to just have the client expand all of the possibilities itself into fully-qualified refs, and keep the server as dumb as possible.
And then the server for now can just cull based on the pattern list, like you have here. But later, we could optimize it to look up the individual patterns, which should be cheaper, since we'd generally have many fewer patterns than total refs.
Quoted 11 lines
Quoted 8 lines
Does the client have to be aware that we're using wildmatch? I think they'd need "refs/heads/**" to actually implement what we usually specify in refspecs as "refs/heads/*". Or does the lack of
WM_PATHNAMEmake this work with just "*"?Do we anticipate that the client would left-anchor the refspec like "/refs/heads/*" so that in theory the server could avoid looking outside of /refs/heads/?
Yeah we may want to anchor it by providing the leading '/' instead of just "refs/<blah>".
I actually wonder if we should just specify that the patterns must _always_ be fully-qualified, but may end with a single "/*" to iterate over wildcards. Or even simpler, that "refs/heads/foo" would find that ref itself, and anything under it.
That drops any question about how wildcards work (e.g., does "refs/foo*" work to find "refs/foobar"?).
Quoted 5 lines
I need to read over the discussion you linked to more but what sort of ref patterns do you believe we should support as part of the initial release of v2? It seems like you wanted this at some point in the past so I assume you have an idea of what sort of filtering would be beneficial.
My goals were just optimizing:
1. Don't send all the refs across the wire if we can avoid it.
2. Don't even iterate over all the refs internally if we can avoid it.
Especially with the new binary-searching packed-refs code, we should be able to serve a request like "ls-refs refs/heads/*" without looking into "refs/pull" or "refs/changes" at all.
-Peff
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Quoted 7 lines · Jeff King
I actually wonder if we should just specify that the patterns must _always_ be fully-qualified, but may end with a single "/*" to iterate over wildcards. Or even simpler, that "refs/heads/foo" would find that ref itself, and anything under it.
That drops any question about how wildcards work (e.g., does "refs/foo*" work to find "refs/foobar"?).
Sounds quite sensible to me.
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
In reply to Jeff KingOn Sat, Feb 24 2018, Jeff King jotted:
Quoted 10 lines · Brandon Williams
Quoted 9 lines
Quoted 8 lines
Does the client have to be aware that we're using wildmatch? I think they'd need "refs/heads/**" to actually implement what we usually specify in refspecs as "refs/heads/*". Or does the lack of
WM_PATHNAMEmake this work with just "*"?Do we anticipate that the client would left-anchor the refspec like "/refs/heads/*" so that in theory the server could avoid looking outside of /refs/heads/?
Quoted 7 lines
Quoted 2 lines
Yeah we may want to anchor it by providing the leading '/' instead of just "refs/<blah>".
I actually wonder if we should just specify that the patterns must _always_ be fully-qualified, but may end with a single "/*" to iterate over wildcards. Or even simpler, that "refs/heads/foo" would find that ref itself, and anything under it.
I agree that this is a very good trade-off for now, but I think having an escape hatch makes sense. It looks like the protocol is implicitly extendible since another parameter could be added, but maybe having such a parameter from the get-go would make sense:
pattern-type [simple|wildmatch|pcre|...]
ref-pattern <pattern>
E.g.:
pattern-type simple
ref-pattern refs/tags/*
ref-pattern refs/pull/*
pattern-type wildmatch
ref-pattern refs/**/2018
pattern-type pcre
ref-pattern ^refs/release/v-201[56789]-\d+$I.e. each ref-pattern is typed by the pattern-type in play, with just "simple" (with the behavior being discussed here) for now, anything else (wildmatch, pcre etc.) would be an error.
But it allows for adding more patterns down the line, and in e.g. in-house setups of git where you control both the server & clients to make the trade-off that we'd like a bit more work on the server (e.g. to match dated tags created in the last 3 months) by setting some config option.
The discussion upthread about:
Quoted 2 lines
The other problem with tail-matching is that it's inefficient on the server[...]
Is also something that's only true in the current implementation, but doesn't need to be, so it would be unfortunate to not work in an escape hatch for that limtiation.
E.g. if the refs were stored indexed using the method described at
https://swtch.com/~rsc/regexp/regexp4.html tail matching becomes no less efficient than prefix matching, but a function of how many trigrams in your index match the pattern given.
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Quoted 11 lines · Ævar Arnfjörð Bjarmason
On Sat, Feb 24 2018, Jeff King jotted:
Quoted 4 lines
I actually wonder if we should just specify that the patterns must _always_ be fully-qualified, but may end with a single "/*" to iterate over wildcards. Or even simpler, that "refs/heads/foo" would find that ref itself, and anything under it.
I agree that this is a very good trade-off for now, but I think having an escape hatch makes sense. It looks like the protocol is implicitly extendible since another parameter could be added, but maybe having such a parameter from the get-go would make sense:
I prefer to rely on the implicit extensibility (following the general YAGNI principle).
In other words, we can introduce a pattern-type later and make the current pattern-type the default.
Thanks for looking to the future.
[...]
Quoted 4 lines
E.g. if the refs were stored indexed using the method described at
https://swtch.com/~rsc/regexp/regexp4.html tail matching becomes no less efficient than prefix matching, but a function of how many trigrams in your index match the pattern given.
I think the nearest planned change to ref storage is [1], which is still optimized for prefix matching. Longer term, maybe some day we'll want a secondary index that supports infix matching, or maybe we'll never need it. :)
Sincerely,
Jonathan
[1] https://public-inbox.org/git/CAJo=hJsZcAM9sipdVr7TMD-FD2V2W6_pvMQ791EGCDsDkQ033w@mail.gmail.com/#t
Re: [PATCH v3 13/35] ls-refs: introduce ls-refs server command
Quoted 13 lines · Jonathan Nieder
Quoted 11 lines · Ævar Arnfjörð Bjarmason
On Sat, Feb 24 2018, Jeff King jotted:
Quoted 4 lines
I actually wonder if we should just specify that the patterns must _always_ be fully-qualified, but may end with a single "/*" to iterate over wildcards. Or even simpler, that "refs/heads/foo" would find that ref itself, and anything under it.
I agree that this is a very good trade-off for now, but I think having an escape hatch makes sense. It looks like the protocol is implicitly extendible since another parameter could be added, but maybe having such a parameter from the get-go would make sense:
Quoted 5 lines
I prefer to rely on the implicit extensibility (following the general YAGNI principle).
In other words, we can introduce a pattern-type later and make the current pattern-type the default.
Yeah this is what I'm going to do for the next re-roll of the series, make the pattern matching simple and later we can extend it if we want since we already have the ability to add new features to commands (you can see how I added shallow to fetch for an example).
Quoted 18 lines
Thanks for looking to the future.
[...]
Quoted 4 lines
E.g. if the refs were stored indexed using the method described at
https://swtch.com/~rsc/regexp/regexp4.html tail matching becomes no less efficient than prefix matching, but a function of how many trigrams in your index match the pattern given.I think the nearest planned change to ref storage is [1], which is still optimized for prefix matching. Longer term, maybe some day we'll want a secondary index that supports infix matching, or maybe we'll never need it. :)
Sincerely,
Jonathan[1] https://public-inbox.org/git/CAJo=hJsZcAM9sipdVr7TMD-FD2V2W6_pvMQ791EGCDsDkQ033w@mail.gmail.com/#t
Signature · 1 line
Brandon Williams
PATCH v3 14/35 connect: request remote refs using v2
In reply to Brandon WilliamsTeach the client to be able to request a remote's refs using protocol v2. This is done by having a client issue a 'ls-refs' request to a v2 server.
--- builtin/upload-pack.c | 10 ++-- connect.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++- connect.h | 2 + remote.h | 4 ++ t/t5702-protocol-v2.sh | 53 +++++++++++++++++++++ transport.c | 2 +- 6 files changed, 187 insertions(+), 7 deletions(-) create mode 100755 t/t5702-protocol-v2.sh
builtin/upload-pack.c +5 -5
@@ -5,6 +5,7 @@#include "parse-options.h" #include "protocol.h" #include "upload-pack.h" +#include "serve.h" static const char *const upload_pack_usage[] = {N_ ("git upload-pack [<options>] <dir>" ),
@@ -16,6 +17,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)const char *dir;int strict =0 ;struct upload_pack_options opts = {0 }; +struct serve_options serve_opts =SERVE_OPTIONS_INIT ;struct option options[] = {OPT_BOOL (0 ,"stateless-rpc" , &opts.stateless_rpc,N_ ("quit after a single request/response exchange" )),
@@ -48,11 +50,9 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)switch (determine_protocol_version_server ()) {case protocol_v2: -/* -* fetch support for protocol v2 has not been implemented yet, -* so ignore the request to use v2 and fallback to using v0. -*/ -upload_pack (&opts); + serve_opts.advertise_capabilities = opts.advertise_refs; + serve_opts.stateless_rpc = opts.stateless_rpc; +serve (&serve_opts);break ;case protocol_v1:/*
connect.c +122 -1
150 lines not on this page · raw message
connect.h +2 -0
@@ -16,4 +16,6 @@ extern int url_is_local_not_ssh(const char *url);struct packet_reader ;extern enum protocol_version discover_version (struct packet_reader *reader); +extern int server_supports_v2 (const char *c,int die_on_error); +#endif
remote.h +4 -0
@@ -151,10 +151,14 @@ void free_refs(struct ref *ref);struct oid_array ;struct packet_reader ; +struct argv_array ;extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have,struct oid_array *shallow_points); +extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);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);
t/t5702-protocol-v2.sh new file +53 -0
53 lines not on this page · raw message
transport.c +1 -1
@@ -204,7 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus data->version =discover_version (&reader);switch (data->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); +get_remote_refs (data->fd[1 ], &reader, &refs, for_push,NULL );break ;case protocol_v1:case protocol_v0:
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 14/35] connect: request remote refs using v2
remote.h void free_refs(struct ref *ref); · line 161
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:51 -0800 Brandon Williams <[email protected]> wrote:
remote.h +3
-0+extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);
I haven't looked at the rest of this patch in detail, but the type of ref_patterns is probably better as struct string_list, since this is not a true argument array (e.g. with flags starting with --). Same comment for the next few patches that deal with ref patterns.
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 11 lines · Jonathan Tan
remote.h void free_refs(struct ref *ref); · line 161
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:51 -0800 Brandon Williams <[email protected]> wrote:
remote.h +3
-0+extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);I haven't looked at the rest of this patch in detail, but the type of
ref_patternsis probably better as structstring_list, since this is not a true argument array (e.g. with flags starting with --). Same comment for the next few patches that deal with ref patterns.
Its just a list of strings which don't require having a util pointer hanging around so actually using an argv_array would be more memory efficient than a string_list. But either way I don't think it matters much.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 13 lines · Brandon Williams
Quoted 11 lines · Jonathan Tan
remote.h void free_refs(struct ref *ref); · line 161
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:51 -0800 Brandon Williams <[email protected]> wrote:
remote.h +3
-0+extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);I haven't looked at the rest of this patch in detail, but the type of
ref_patternsis probably better as structstring_list, since this is not a true argument array (e.g. with flags starting with --). Same comment for the next few patches that deal with ref patterns.
Quoted 4 lines
Its just a list of strings which don't require having a util pointer hanging around so actually using an
argv_arraywould be more memory efficient than astring_list. But either way I don't think it matters much.
I agree that it shouldn't matter much here. But if the name argv_array is standing in the way of using it, I think we should consider giving it a more general name. I picked that not to evoke "this must be arguments" but "this is terminated by a single NULL".
In general I think it should be the preferred structure for string lists, just because it actually converts for free to the "other" common format (whereas you can never pass string_list.items to a function that doesn't know about string lists).
-Peff
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 25 lines · On Thu, 22 Feb 2018 13:26:58 -0500 Jeff King <[email protected]> wrote:
Quoted 13 lines · Brandon Williams
Quoted 11 lines · Jonathan Tan
remote.h void free_refs(struct ref *ref); · line 161
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:51 -0800 Brandon Williams <[email protected]> wrote:
remote.h +3
-0+extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);I haven't looked at the rest of this patch in detail, but the type of
ref_patternsis probably better as structstring_list, since this is not a true argument array (e.g. with flags starting with --). Same comment for the next few patches that deal with ref patterns.Quoted 4 lines
Its just a list of strings which don't require having a util pointer hanging around so actually using an
argv_arraywould be more memory efficient than astring_list. But either way I don't think it matters much.I agree that it shouldn't matter much here. But if the name
argv_arrayis standing in the way of using it, I think we should consider giving it a more general name. I picked that not to evoke "this must be arguments" but "this is terminated by a single NULL".
Quoted 4 lines
In general I think it should be the preferred structure for string lists, just because it actually converts for free to the "other" common format (whereas you can never pass string_list.items to a function that doesn't know about string lists).
This sounds reasonable - I withdraw my comment about using struct string_list.
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 7 lines · Jonathan Tan
Quoted 4 lines · On Thu, 22 Feb 2018 13:26:58 -0500 Jeff King <[email protected]> wrote:
I agree that it shouldn't matter much here. But if the name
argv_arrayis standing in the way of using it, I think we should consider giving it a more general name. I picked that not to evoke "this must be arguments" but "this is terminated by a single NULL".
[...]
Quoted 2 lines
This sounds reasonable - I withdraw my comment about using struct
string_list.
Marking with #leftoverbits as a reminder to think about what such a more general name would be (or what kind of docs to put in argv-array.h) and make it so the next time I do a search for that keyword.
Thanks,
Jonathan
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 8 lines · Jonathan Nieder
Quoted 7 lines · Jonathan Tan
Quoted 4 lines · On Thu, 22 Feb 2018 13:26:58 -0500 Jeff King <[email protected]> wrote:
I agree that it shouldn't matter much here. But if the name
argv_arrayis standing in the way of using it, I think we should consider giving it a more general name. I picked that not to evoke "this must be arguments" but "this is terminated by a single NULL".
Quoted 8 lines
[...]
Quoted 2 lines
This sounds reasonable - I withdraw my comment about using struct
string_list.Marking with #leftoverbits as a reminder to think about what such a more general name would be (or what kind of docs to put in
argv-array.h) and make it so the next time I do a search for that keyword.
So are we looking for a natural name to call an array of trings? I personally do not mind argv_array too much, but perhaps we can call it a string_array and then everybody will be happy?
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 14 lines · Junio C Hamano
Quoted 12 lines · Jonathan Nieder
Quoted 7 lines · Jonathan Tan
Quoted 4 lines · On Thu, 22 Feb 2018 13:26:58 -0500 Jeff King <[email protected]> wrote:
I agree that it shouldn't matter much here. But if the name
argv_arrayis standing in the way of using it, I think we should consider giving it a more general name. I picked that not to evoke "this must be arguments" but "this is terminated by a single NULL".[...]
Quoted 2 lines
This sounds reasonable - I withdraw my comment about using struct
string_list.
Quoted 8 lines
Quoted 4 lines
Marking with #leftoverbits as a reminder to think about what such a more general name would be (or what kind of docs to put in
argv-array.h) and make it so the next time I do a search for that keyword.So are we looking for a natural name to call an array of trings? I personally do not mind
argv_arraytoo much, but perhaps we can call it astring_arrayand then everybody will be happy?
That would be fine with me. Though I would love it if we could find a shorter name for the associated functions. For example, argv_array_pushf() can make lines quite long, and something like argv_pushf() is easier to read (in my opinion). And that might work because "argv" is pretty unique by itself, but "string" is not.
Some one-word name like "strarray" might work, though I find that is not quite catchy. I guess "strv" is short if you assume that people know the "v" suffix means "vector".
It may not be worth worrying too much about, though. We already have 24-character monstrosities like string_list_append_nodup(). ;)
-Peff
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 11 lines · Jeff King
Quoted 3 lines · Junio C Hamano
So are we looking for a natural name to call an array of trings? I personally do not mind
argv_arraytoo much, but perhaps we can call it astring_arrayand then everybody will be happy?That would be fine with me. Though I would love it if we could find a shorter name for the associated functions. For example,
argv_array_pushf()can make lines quite long, and something likeargv_pushf()is easier to read (in my opinion). And that might work because "argv" is pretty unique by itself, but "string" is not.
Quoted 3 lines
Some one-word name like "strarray" might work, though I find that is not quite catchy. I guess "strv" is short if you assume that people know the "v" suffix means "vector".
struct strs {...};
void strs_init(struct strs *);
void strs_push(struct strs *, const char *); void strs_pushf(struct strs *, const char *fmt, ...); void strs_pushl(struct strs *, ...);
void strs_pushv(struct strs *, const char **); void strs_pop(struct strs *);
void strs_clear(struct strs *);
const char **strs_detach(struct strs *);
...is short, feels pretty natural, and doesn't require understanding "v" for "vector".
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 21 lines · Eric Sunshine
Quoted 9 lines
That would be fine with me. Though I would love it if we could find a shorter name for the associated functions. For example,
argv_array_pushf()can make lines quite long, and something likeargv_pushf()is easier to read (in my opinion). And that might work because "argv" is pretty unique by itself, but "string" is not.Some one-word name like "strarray" might work, though I find that is not quite catchy. I guess "strv" is short if you assume that people know the "v" suffix means "vector".
struct strs {...};
void
strs_init(struct strs *);
voidstrs_push(struct strs *, const char *); voidstrs_pushf(struct strs *, const char *fmt, ...); voidstrs_pushl(struct strs *, ...);
voidstrs_pushv(struct strs *, const char **); voidstrs_pop(struct strs *);
voidstrs_clear(struct strs *);
const char **strs_detach(struct strs *);
Quoted 2 lines
...is short, feels pretty natural, and doesn't require understanding "v" for "vector".
Not bad. The "v" carries the information that it _is_ a NULL-terminated vector and not some other list-like structure (and so is suitable for feeding to execv, etc). But that may just be obvious from looking at its uses and documentation.
-Peff
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 11 lines · Jeff King
Quoted 11 lines
struct strs {...};
void
strs_init(struct strs *);
voidstrs_push(struct strs *, const char *); voidstrs_pushf(struct strs *, const char *fmt, ...); voidstrs_pushl(struct strs *, ...);
voidstrs_pushv(struct strs *, const char **); voidstrs_pop(struct strs *);
voidstrs_clear(struct strs *);
const char **strs_detach(struct strs *);
Quoted 7 lines
Quoted 2 lines
...is short, feels pretty natural, and doesn't require understanding "v" for "vector".
Not bad. The "v" carries the information that it _is_ a NULL-terminated vector and not some other list-like structure (and so is suitable for feeding to execv, etc). But that may just be obvious from looking at its uses and documentation.
And with "v", it probably is obvious without looking at its uses and documentation, so... ;-)
Re: [PATCH v3 14/35] connect: request remote refs using v2
In reply to Brandon WilliamsQuoted 3 lines · Brandon Williams
Teach the client to be able to request a remote's refs using protocol v2. This is done by having a client issue a 'ls-refs' request to a v2 server.
Yay, ls-remote support!
[...]
builtin/upload-pack.c line 8
Quoted 7 lines
builtin/upload-pack.c +1
-0@@ -5,6 +5,7 @@#include "parse-options.h" #include "protocol.h" #include "upload-pack.h" +#include "serve.h"
nit, no change needed in this patch: What is a good logical order for the #includes here? Bonus points if there's a tool to make it happen automatically.
Asking since adding #includes like this at the end tends to result in a harder-to-read list of #includes, sometimes with duplicates, and often producing conflicts when multiple patches in flight add a #include to the same file.
[...]
builtin/upload-pack.c int cmd_upload_pack(int argc, const char **argv, const char *prefix) · line 55
Quoted 12 lines
builtin/upload-pack.c +3
-5@@ -48,11 +50,9 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)switch (determine_protocol_version_server ()) {case protocol_v2: -/* -* fetch support for protocol v2 has not been implemented yet, -* so ignore the request to use v2 and fallback to using v0. -*/ -upload_pack (&opts); + serve_opts.advertise_capabilities = opts.advertise_refs; + serve_opts.stateless_rpc = opts.stateless_rpc; +serve (&serve_opts);
Interesting! daemon.c has its own (file-local) serve() function; can one of the two be renamed?
I actually like both names: serve() is a traditional name for the function a server calls when it's done setting up and is ready to serve. But the clash is confusing.
[...]
builtin/upload-pack.c line 19
Quoted 10 lines
builtin/upload-pack.c +3
-0+++ b/connect.c@@ -12,9 +12,11 @@#include "sha1-array.h" #include "transport.h" #include "strbuf.h" +#include "version.h" #include "protocol.h" static char *server_capabilities; +static struct argv_array server_capabilities_v2 =ARGV_ARRAY_INIT ;
Can a quick doc comment describe these and how they relate?
Is only one of them set, based on which protocol version is in use? Should server_capabilities be renamed to server_capabilities_v1?
Quoted 14 lines
builtin/upload-pack.c +7
-0static const char *parse_feature_value (const char *,const char *,int *);static int check_ref (const char *name,unsigned int flags)@@ -62,6 +64,33 @@ static void die_initial_contact(int unexpected) "and the repository exists.")); } +/* Checks if the server supports the capability 'c' */ +int server_supports_v2 (const char *c,int die_on_error) +{ +int i; + +for (i =0 ; i < server_capabilities_v2.argc; i++) { +const char *out;
builtin/upload-pack.c static void die_initial_contact(int unexpected) · line 83
Quoted 10 lines
builtin/upload-pack.c +10
-0+if (skip_prefix (server_capabilities_v2.argv[i], c, &out) && + (!*out || *out =='=' )) +return 1 ; + } + +if (die_on_error) +die ("server doesn't support '%s'" , c); + +return 0 ; +}
Nice.
builtin/upload-pack.c static void die_initial_contact(int unexpected) · line 91
Quoted 8 lines
builtin/upload-pack.c +8
-0+ +static void process_capabilities_v2 (struct packet_reader *reader) +{ +while (packet_reader_read (reader) ==PACKET_READ_NORMAL ) +argv_array_push (&server_capabilities_v2, reader->line); + +if (reader->status !=PACKET_READ_FLUSH ) +die ("protocol error" );
Can this say more? E.g. "expected flush after capabilities, got <foo>"?
[...]
builtin/upload-pack.c enum protocol_version discover_version(struct packet_reader *reader) · line 114
Quoted 2 lines
builtin/upload-pack.c +0
-0@@ -85,7 +114,7 @@ enum protocol_version discover_version(struct packet_reader *reader)/* Maybe process capabilities here, at least for v2 */
Is this comment out of date now?
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 325
Quoted 12 lines
builtin/upload-pack.c +2
-1switch (version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); +process_capabilities_v2 (reader);break ;case protocol_v1:/* Read the peeked version line */ @@ -293,6 +322,98 @@ struct ref **get_remote_heads(struct packet_reader *reader,return list; } +static int process_ref_v2 (const char *line,struct ref ***list)
What does the return value represent?
Could it return the more typical 0 on success, -1 on error?
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 333
Quoted 8 lines
builtin/upload-pack.c +8
-0+{ +int ret =1 ; +int i =0 ; +struct object_id old_oid; +struct ref *ref; +struct string_list line_sections =STRING_LIST_INIT_DUP ; + +if (string_list_split (&line_sections, line,' ' ,-1 ) <2 ) {
Can there be a comment describing the expected format?
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 343
Quoted 10 lines
builtin/upload-pack.c +10
-0+ ret =0 ; + goto out; + } + +if (get_oid_hex (line_sections.items[i++].string, &old_oid)) { + ret =0 ; + goto out; + } + + ref =alloc_ref (line_sections.items[i++].string);
Ref names cannot contains a space, so this is safe. Good.
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 352
Quoted 9 lines
builtin/upload-pack.c +9
-0+ +oidcpy (&ref->old_oid, &old_oid); + **list = ref; + *list = &ref->next; + +for (; i < line_sections.nr; i++) { +const char *arg = line_sections.items[i].string; +if (skip_prefix (arg,"symref-target:" , &arg)) + ref->symref =xstrdup (arg);
Using space-delimited fields in a single pkt-line means that
- values cannot contains a space
- total length is limited by the size of a pkt-line
Given the context, I think that's fine. More generally it is tempting to use a pkt-line per field to avoid the trouble v1 had with capability lists crammed into a pkt-line, but I see why you used a pkt-line per ref to avoid having to have sections-within-a-section.
My only potential worry is the length part: do we have an explicit limit on the length of a ref name? git-check-ref-format(1) doesn't mention one. A 32k ref name would be a bit ridiculous, though.
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 361
Quoted 9 lines
builtin/upload-pack.c +9
-0+ +if (skip_prefix (arg,"peeled:" , &arg)) { +struct object_id peeled_oid; +char *peeled_name; +struct ref *peeled; +if (get_oid_hex (arg, &peeled_oid)) { + ret =0 ; + goto out; + }
Can this also check that there's no trailing garbage after the oid?
[...]
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 363
Quoted 2 lines
builtin/upload-pack.c +2
-0+ + peeled_name =xstrfmt ("%s^{}" , ref->name);
optional: can reuse a buffer to avoid allocation churn:
struct strbuf peeled_name = STRBUF_INIT;
strbuf_reset(&peeled_name);
strbuf_addf(&peeled_name, "%s^{}", ref->name);
// or strbuf_addstr(ref->name); strbuf_addstr("^{}"); out:
strbuf_release(&peeled_name);
[...]
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 374
Quoted 11 lines
builtin/upload-pack.c +11
-0+struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns) +{ +int i; + *list =NULL ; + +/* Check that the server supports the ls-refs command */ +/* Issue request for ls-refs */ +if (server_supports_v2 ("ls-refs" ,1 )) +packet_write_fmt (fd_out,"command=ls-refs\n" );
Since the code is so clear, I don't think the above two comments are helping.
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 377
Quoted 3 lines
builtin/upload-pack.c +3
-0+ +if (server_supports_v2 ("agent" ,0 )) +packet_write_fmt (fd_out,"agent=%s" ,git_user_agent_sanitized ());
whitespace nit: mixing tabs and spaces. Does "make style" catch this?
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 380
Quoted 3 lines
builtin/upload-pack.c +3
-0+ +packet_delim (fd_out); +/* When pushing we don't want to request the peeled tags */
Can you say more about this? In theory it would be nice to have the peeled tags since they name commits whose history can be excluded from the pack.
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 383
Quoted 3 lines
builtin/upload-pack.c +3
-0+if (!for_push) +packet_write_fmt (fd_out,"peel\n" ); +packet_write_fmt (fd_out,"symrefs\n" );
Are symrefs useful during push?
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 387
Quoted 4 lines
builtin/upload-pack.c +4
-0+for (i =0 ; ref_patterns && i < ref_patterns->argc; i++) { +packet_write_fmt (fd_out,"ref-pattern %s\n" , + ref_patterns->argv[i]); + }
The exciting part.
Why do these pkts end with \n? I would have expected the pkt-line framing to work to delimit them.
builtin/upload-pack.c struct ref **get_remote_heads(struct packet_reader *reader, · line 397
Quoted 10 lines
builtin/upload-pack.c +10
-0+packet_flush (fd_out); + +/* Process response from server */ +while (packet_reader_read (reader) ==PACKET_READ_NORMAL ) { +if (!process_ref_v2 (reader->line, &list)) +die ("invalid ls-refs response: %s" , reader->line); + } + +if (reader->status !=PACKET_READ_FLUSH ) +die ("protocol error" );
Can this protocol error give more detail? When diagnosing an error in servers, proxies, or lower-level networking issues, informative protocol errors can be very helpful (similar to syntax errors from a compiler).
[...]
connect.h extern int url_is_local_not_ssh(const char *url); · line 19
Quoted 7 lines
connect.h +1
-0@@ -16,4 +16,6 @@ extern int url_is_local_not_ssh(const char *url);struct packet_reader ;extern enum protocol_version discover_version (struct packet_reader *reader); +extern int server_supports_v2 (const char *c,int die_on_error);
const char *cap, maybe?
[...]
Quoted 4 lines
remote.h +0
-0@@ -151,10 +151,14 @@ void free_refs(struct ref *ref);
remote.h void free_refs(struct ref *ref); · line 161
Quoted 10 lines
remote.h +4
-0struct oid_array ;struct packet_reader ; +struct argv_array ;extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have,struct oid_array *shallow_points); +extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);
What is the difference between get_remote_heads and get_remote_refs? A comment might help. (BTW, thanks for making the new saner name to replace get_remote_heads!)
[...]
t/t5702-protocol-v2.sh line 3
Quoted 6 lines
t/t5702-protocol-v2.sh new file +3
-0@@ -0,0 +1,53 @@ +#!/bin/sh + +test_description='test git wire-protocol version 2'
Woot!
[...]
t/t5702-protocol-v2.sh line 27
Quoted 8 lines
t/t5702-protocol-v2.sh new file +8
-0+test_expect_success 'list refs with git:// using protocol v2' ' + GIT_TRACE_PACKET=1 git -c protocol.version=2 \ + ls-remote --symref "$GIT_DAEMON_URL/parent" >actual 2>log && + + # Client requested to use protocol v2 + grep "git> .*\\\0\\\0version=2\\\0$" log && + # Server responded using protocol v2 + grep "git< version 2" log &&
optional: Could anchor these greps to make the test tighter (e.g. to not match "version 20".
Thanks,
Jonathan
Re: [PATCH v3 14/35] connect: request remote refs using v2
Quoted 8 lines · Jonathan Nieder
Quoted 2 lines · Brandon Williams
static char
*server_capabilities;
+static structargv_arrayserver_capabilities_v2=ARGV_ARRAY_INIT;Can a quick doc comment describe these and how they relate?
Is only one of them set, based on which protocol version is in use? Should
server_capabilitiesbe renamed toserver_capabilities_v1?
yes that's correct. I can rename it.
Quoted 10 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 333
Quoted 8 lines
connect.c +8
-0+{ +int ret =1 ; +int i =0 ; +struct object_id old_oid; +struct ref *ref; +struct string_list line_sections =STRING_LIST_INIT_DUP ; + +if (string_list_split (&line_sections, line,' ' ,-1 ) <2 ) {Can there be a comment describing the expected format?
Yep I'll write a comment up.
Quoted 14 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 352
Quoted 9 lines
connect.c +9
-0+ +oidcpy (&ref->old_oid, &old_oid); + **list = ref; + *list = &ref->next; + +for (; i < line_sections.nr; i++) { +const char *arg = line_sections.items[i].string; +if (skip_prefix (arg,"symref-target:" , &arg)) + ref->symref =xstrdup (arg);Using space-delimited fields in a single pkt-line means that
- values cannot contains a space
- total length is limited by the size of a pkt-line
Quoted 8 lines
Given the context, I think that's fine. More generally it is tempting to use a pkt-line per field to avoid the trouble v1 had with capability lists crammed into a pkt-line, but I see why you used a pkt-line per ref to avoid having to have sections-within-a-section.
My only potential worry is the length part: do we have an explicit limit on the length of a ref name? git-check-ref-format(1) doesn't mention one. A 32k ref name would be a bit ridiculous, though.
Yeah I think we're fine for now, mostly because we're out of luck with the current protocol as it is.
Quoted 12 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 361
Quoted 10 lines
connect.c +9
-0+ +if (skip_prefix (arg,"peeled:" , &arg)) { +struct object_id peeled_oid; +char *peeled_name; +struct ref *peeled; +if (get_oid_hex (arg, &peeled_oid)) { + ret =0 ; + goto out; + }Can this also check that there's no trailing garbage after the oid?
Yeah I do that.
Quoted 8 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 395
Quoted 4 lines
connect.c +3
-0+ +packet_delim (fd_out); +/* When pushing we don't want to request the peeled tags */ Can you say more about this? In theory it would be nice to have the peeled tags since they name commits whose history can be excluded from the pack.
I don't believe peeled refs are sent now in v0 for push.
Quoted 6 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 398
Quoted 4 lines
connect.c +3
-0+if (!for_push) +packet_write_fmt (fd_out,"peel\n" ); +packet_write_fmt (fd_out,"symrefs\n" );Are symrefs useful during push?
They may be at a later point in time when you want to update a symref :)
Quoted 10 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 402
Quoted 5 lines
connect.c +4
-0+for (i =0 ; ref_patterns && i < ref_patterns->argc; i++) { +packet_write_fmt (fd_out,"ref-pattern %s\n" , + ref_patterns->argv[i]); + }The exciting part.
Why do these pkts end with \n? I would have expected the pkt-line framing to work to delimit them.
All pkts end with \n, that's just hows its been since v0. Though the server isn't supposed to complain if they don't contain newlines.
Quoted 15 lines
connect.c struct ref **get_remote_heads(struct packet_reader *reader, · line 412
Quoted 11 lines
connect.c +10
-0+packet_flush (fd_out); + +/* Process response from server */ +while (packet_reader_read (reader) ==PACKET_READ_NORMAL ) { +if (!process_ref_v2 (reader->line, &list)) +die ("invalid ls-refs response: %s" , reader->line); + } + +if (reader->status !=PACKET_READ_FLUSH ) +die ("protocol error" );Can this protocol error give more detail? When diagnosing an error in servers, proxies, or lower-level networking issues, informative protocol errors can be very helpful (similar to syntax errors from a compiler).
I'll update the error msg.
Quoted 5 lines
[...]
Quoted 4 lines
remote.h +0
-0@@ -151,10 +151,14 @@ void free_refs(struct ref *ref);
Quoted 14 lines
remote.h void free_refs(struct ref *ref); · line 161
Quoted 10 lines
remote.h +4
-0struct oid_array ;struct packet_reader ; +struct argv_array ;extern struct ref **get_remote_heads (struct packet_reader *reader,struct ref **list,unsigned int flags,struct oid_array *extra_have,struct oid_array *shallow_points); +extern struct ref **get_remote_refs (int fd_out,struct packet_reader *reader, +struct ref **list,int for_push, +const struct argv_array *ref_patterns);What is the difference between
get_remote_headsandget_remote_refs? A comment might help. (BTW, thanks for making the new saner name to replaceget_remote_heads!)
I'll add a comment saying its used in v2 to retrieve a list of refs from the remote.
Signature · 1 line
Brandon Williams
PATCH v3 15/35 transport: convert get_refs_list to take a list of ref patterns
In reply to Brandon WilliamsConvert the 'struct transport' virtual function 'get_refs_list()' to optionally take an argv_array of ref patterns. When communicating with a server using protocol v2 these ref patterns can be sent when requesting a listing of their refs allowing the server to filter the refs it sends based on the sent patterns.
--- transport-helper.c | 5 +++-- transport-internal.h | 4 +++- transport.c | 16 +++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-)
transport-helper.c +3 -2
@@ -1026,7 +1026,8 @@ static int has_attribute(const char *attrs, const char *attr) { } } -static struct ref *get_refs_list (struct transport *transport,int for_push) +static struct ref *get_refs_list (struct transport *transport,int for_push, +const struct argv_array *ref_patterns) {struct helper_data *data = transport->data;struct child_process *helper;
@@ -1039,7 +1040,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)if (process_connect (transport, for_push)) {do_take_over (transport); -return transport->vtable->get_refs_list (transport, for_push); +return transport->vtable->get_refs_list (transport, for_push, ref_patterns); }if (data->push && for_push)
transport-internal.h +3 -1
@@ -3,6 +3,7 @@struct ref ;struct transport ; +struct argv_array ;struct transport_vtable {/**
@@ -21,7 +22,8 @@ struct transport_vtable {* the ref without a huge amount of effort, it should store it * in the ref's old_sha1 field; otherwise it should be all 0. **/ -struct ref *(*get_refs_list)(struct transport *transport,int for_push); +struct ref *(*get_refs_list)(struct transport *transport,int for_push, +const struct argv_array *ref_patterns);/** * Fetch the objects for the given refs. Note that this gets
transport.c +9 -7
@@ -72,7 +72,7 @@ struct bundle_transport_data {struct bundle_header header; }; -static struct ref *get_refs_from_bundle (struct transport *transport,int for_push) +static struct ref *get_refs_from_bundle (struct transport *transport,int for_push,const struct argv_array *ref_patterns) {struct bundle_transport_data *data = transport->data;struct ref *result =NULL ;
@@ -189,7 +189,8 @@ static int connect_setup(struct transport *transport, int for_push)return 0 ; } -static struct ref *get_refs_via_connect (struct transport *transport,int for_push) +static struct ref *get_refs_via_connect (struct transport *transport,int for_push, +const struct argv_array *ref_patterns) {struct git_transport_data *data = transport->data;struct ref *refs =NULL ;
@@ -204,7 +205,8 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus data->version =discover_version (&reader);switch (data->version) {case protocol_v2: -get_remote_refs (data->fd[1 ], &reader, &refs, for_push,NULL ); +get_remote_refs (data->fd[1 ], &reader, &refs, for_push, + ref_patterns);break ;case protocol_v1:case protocol_v0:
@@ -250,7 +252,7 @@ static int fetch_refs_via_pack(struct transport *transport, args.update_shallow = data->options.update_shallow;if (!data->got_remote_heads) - refs_tmp =get_refs_via_connect (transport,0 ); + refs_tmp =get_refs_via_connect (transport,0 ,NULL );switch (data->version) {case protocol_v2:
@@ -568,7 +570,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_reint ret =0 ;if (!data->got_remote_heads) -get_refs_via_connect (transport,1 ); +get_refs_via_connect (transport,1 ,NULL );memset (&args,0 ,sizeof (args)); args.send_mirror = !!(flags &TRANSPORT_PUSH_MIRROR );
@@ -1028,7 +1030,7 @@ int transport_push(struct transport *transport,if (check_push_refs (local_refs, refspec_nr, refspec) <0 )return -1 ; - remote_refs = transport->vtable->get_refs_list (transport,1 ); + remote_refs = transport->vtable->get_refs_list (transport,1 ,NULL );if (flags &TRANSPORT_PUSH_ALL ) match_flags |=MATCH_REFS_ALL ;
@@ -1137,7 +1139,7 @@ int transport_push(struct transport *transport,const struct ref *transport_get_remote_refs (struct transport *transport) {if (!transport->got_remote_refs) { - transport->remote_refs = transport->vtable->get_refs_list (transport,0 ); + transport->remote_refs = transport->vtable->get_refs_list (transport,0 ,NULL ); transport->got_remote_refs =1 ; }
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 15/35] transport: convert get_refs_list to take a list of ref patterns
transport-internal.h struct transport_vtable { · line 26
Quoted 7 lines · On Tue, 6 Feb 2018 17:12:52 -0800 Brandon Williams <[email protected]> wrote:
transport-internal.h +2
-1@@ -21,7 +22,8 @@ struct transport_vtable {* the ref without a huge amount of effort, it should store it * in the ref's old_sha1 field; otherwise it should be all 0. **/ -struct ref *(*get_refs_list)(struct transport *transport,int for_push); +struct ref *(*get_refs_list)(struct transport *transport,int for_push, +const struct argv_array *ref_patterns);
Also mention in the documentation that this function is allowed to return refs that do not match the ref patterns.
Re: [PATCH v3 15/35] transport: convert get_refs_list to take a list of ref patterns
Quoted 13 lines · Jonathan Tan
transport-internal.h struct transport_vtable { · line 26
Quoted 7 lines · On Tue, 6 Feb 2018 17:12:52 -0800 Brandon Williams <[email protected]> wrote:
transport-internal.h +2
-1@@ -21,7 +22,8 @@ struct transport_vtable {* the ref without a huge amount of effort, it should store it * in the ref's old_sha1 field; otherwise it should be all 0. **/ -struct ref *(*get_refs_list)(struct transport *transport,int for_push); +struct ref *(*get_refs_list)(struct transport *transport,int for_push, +const struct argv_array *ref_patterns);Also mention in the documentation that this function is allowed to return refs that do not match the ref patterns.
I'll add a comment.
Signature · 1 line
Brandon Williams
PATCH v3 16/35 transport: convert transport_get_remote_refs to take a list of ref patterns
In reply to Brandon WilliamsConvert 'transport_get_remote_refs()' to optionally take a list of ref patterns.
--- builtin/clone.c | 2 +- builtin/fetch.c | 4 ++-- builtin/ls-remote.c | 2 +- builtin/remote.c | 2 +- transport.c | 7 +++++-- transport.h | 3 ++- 6 files changed, 12 insertions(+), 8 deletions(-)
builtin/clone.c +1 -1
@@ -1121,7 +1121,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)if (transport->smart_options && !deepen) transport->smart_options->check_self_contained_and_connected =1 ; - refs =transport_get_remote_refs (transport); + refs =transport_get_remote_refs (transport,NULL );if (refs) { mapped_refs =wanted_peer_refs (refs, refspec);
builtin/fetch.c +2 -2
@@ -250,7 +250,7 @@ static void find_non_local_tags(struct transport *transport,struct string_list_item *item =NULL ;for_each_ref (add_existing, &existing_refs); -for (ref =transport_get_remote_refs (transport); ref; ref = ref->next) { +for (ref =transport_get_remote_refs (transport,NULL ); ref; ref = ref->next) {if (!starts_with (ref->name,"refs/tags/" ))continue ;
@@ -336,7 +336,7 @@ static struct ref *get_ref_map(struct transport *transport,/* opportunistically-updated references: */ struct ref *orefs =NULL , **oref_tail = &orefs; -const struct ref *remote_refs =transport_get_remote_refs (transport); +const struct ref *remote_refs =transport_get_remote_refs (transport,NULL );if (refspec_count) {struct refspec *fetch_refspec;
builtin/ls-remote.c +1 -1
@@ -96,7 +96,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)if (uploadpack !=NULL )transport_set_option (transport,TRANS_OPT_UPLOADPACK , uploadpack); - ref =transport_get_remote_refs (transport); + ref =transport_get_remote_refs (transport,NULL );if (transport_disconnect (transport))return 1 ;
builtin/remote.c +1 -1
@@ -862,7 +862,7 @@ static int get_remote_ref_states(const char *name,if (query) { transport =transport_get (states->remote, states->remote->url_nr >0 ? states->remote->url[0 ] :NULL ); - remote_refs =transport_get_remote_refs (transport); + remote_refs =transport_get_remote_refs (transport,NULL );transport_disconnect (transport); states->queried =1 ;
transport.c +5 -2
@@ -1136,10 +1136,13 @@ int transport_push(struct transport *transport,return 1 ; } -const struct ref *transport_get_remote_refs (struct transport *transport) +const struct ref *transport_get_remote_refs (struct transport *transport, +const struct argv_array *ref_patterns) {if (!transport->got_remote_refs) { - transport->remote_refs = transport->vtable->get_refs_list (transport,0 ,NULL ); + transport->remote_refs = + transport->vtable->get_refs_list (transport,0 , + ref_patterns); transport->got_remote_refs =1 ; }
transport.h +2 -1
@@ -178,7 +178,8 @@ int transport_push(struct transport *connection, int refspec_nr, const char **refspec, int flags, unsigned int * reject_reasons); -const struct ref *transport_get_remote_refs (struct transport *transport); +const struct ref *transport_get_remote_refs (struct transport *transport, +const struct argv_array *ref_patterns);int transport_fetch_refs (struct transport *transport,struct ref *refs);void transport_unlock_pack (struct transport *transport);
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 16/35] transport: convert transport_get_remote_refs to take a list of ref patterns
transport.c int transport_push(struct transport *transport, · line 1147
Quoted 11 lines · On Tue, 6 Feb 2018 17:12:53 -0800 Brandon Williams <[email protected]> wrote:
transport.c +5
-2-const struct ref *transport_get_remote_refs (struct transport *transport) +const struct ref *transport_get_remote_refs (struct transport *transport, +const struct argv_array *ref_patterns) {if (!transport->got_remote_refs) { - transport->remote_refs = transport->vtable->get_refs_list (transport,0 ,NULL ); + transport->remote_refs = + transport->vtable->get_refs_list (transport,0 , + ref_patterns); transport->got_remote_refs =1 ; }
Should we do our own client-side filtering if the server side cannot do it for us (because it doesn't support protocol v2)? Either way, this decision should be mentioned in the commit message.
Re: [PATCH v3 16/35] transport: convert transport_get_remote_refs to take a list of ref patterns
Quoted 18 lines · Jonathan Tan
transport.c int transport_push(struct transport *transport, · line 1147
Quoted 11 lines · On Tue, 6 Feb 2018 17:12:53 -0800 Brandon Williams <[email protected]> wrote:
transport.c +5
-2-const struct ref *transport_get_remote_refs (struct transport *transport) +const struct ref *transport_get_remote_refs (struct transport *transport, +const struct argv_array *ref_patterns) {if (!transport->got_remote_refs) { - transport->remote_refs = transport->vtable->get_refs_list (transport,0 ,NULL ); + transport->remote_refs = + transport->vtable->get_refs_list (transport,0 , + ref_patterns); transport->got_remote_refs =1 ; }Should we do our own client-side filtering if the server side cannot do it for us (because it doesn't support protocol v2)? Either way, this decision should be mentioned in the commit message.
If someone wants to add this in the future they can, but that is outside the scope of this series.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 16/35] transport: convert transport_get_remote_refs to take a list of ref patterns
Quoted 20 lines · On Thu, 22 Feb 2018 10:26:47 -0800 Brandon Williams <[email protected]> wrote:
Quoted 18 lines · Jonathan Tan
transport.c int transport_push(struct transport *transport, · line 1147
Quoted 11 lines · On Tue, 6 Feb 2018 17:12:53 -0800 Brandon Williams <[email protected]> wrote:
transport.c +5
-2-const struct ref *transport_get_remote_refs (struct transport *transport) +const struct ref *transport_get_remote_refs (struct transport *transport, +const struct argv_array *ref_patterns) {if (!transport->got_remote_refs) { - transport->remote_refs = transport->vtable->get_refs_list (transport,0 ,NULL ); + transport->remote_refs = + transport->vtable->get_refs_list (transport,0 , + ref_patterns); transport->got_remote_refs =1 ; }Should we do our own client-side filtering if the server side cannot do it for us (because it doesn't support protocol v2)? Either way, this decision should be mentioned in the commit message.
Quoted 2 lines
If someone wants to add this in the future they can, but that is outside the scope of this series.
In that case, also document that this function is allowed to return refs that do not match the ref patterns.
Unlike in patch 15 (which deals with the interface between the transport code and transport vtables, which can be changed as long as the transport code is aware of it, as I wrote in [1]), this may result in user-visible differences depending on which protocol is used. But after more thinking, I don't think we're in a situation yet where having extra refs shown/written are harmful, and if it comes to that, we can tighten this code later without backwards incompatibility. So, OK, this is fine.
Re: [PATCH v3 16/35] transport: convert transport_get_remote_refs to take a list of ref patterns
Quoted 37 lines · Jonathan Tan
Quoted 20 lines · On Thu, 22 Feb 2018 10:26:47 -0800 Brandon Williams <[email protected]> wrote:
Quoted 18 lines · Jonathan Tan
transport.c int transport_push(struct transport *transport, · line 1147
Quoted 11 lines · On Tue, 6 Feb 2018 17:12:53 -0800 Brandon Williams <[email protected]> wrote:
transport.c +5
-2-const struct ref *transport_get_remote_refs (struct transport *transport) +const struct ref *transport_get_remote_refs (struct transport *transport, +const struct argv_array *ref_patterns) {if (!transport->got_remote_refs) { - transport->remote_refs = transport->vtable->get_refs_list (transport,0 ,NULL ); + transport->remote_refs = + transport->vtable->get_refs_list (transport,0 , + ref_patterns); transport->got_remote_refs =1 ; }Should we do our own client-side filtering if the server side cannot do it for us (because it doesn't support protocol v2)? Either way, this decision should be mentioned in the commit message.
Quoted 2 lines
If someone wants to add this in the future they can, but that is outside the scope of this series.
In that case, also document that this function is allowed to return refs that do not match the ref patterns.
Unlike in patch 15 (which deals with the interface between the transport code and transport vtables, which can be changed as long as the transport code is aware of it, as I wrote in [1]), this may result in user-visible differences depending on which protocol is used. But after more thinking, I don't think we're in a situation yet where having extra refs shown/written are harmful, and if it comes to that, we can tighten this code later without backwards incompatibility. So, OK, this is fine.
Quoted 1 line
I'll add the documentation.
Signature · 1 line
Brandon Williams
PATCH v3 17/35 ls-remote: pass ref patterns when requesting a remote's refs
In reply to Brandon WilliamsConstruct an argv_array of the ref patterns supplied via the command line and pass them to 'transport_get_remote_refs()' to be used when communicating protocol v2 so that the server can limit the ref advertisement based on the supplied patterns.
--- builtin/ls-remote.c | 7 +++++-- t/t5702-protocol-v2.sh | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-)
builtin/ls-remote.c +5 -2
@@ -43,6 +43,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)int show_symref_target =0 ;const char *uploadpack =NULL ;const char **pattern =NULL ; +struct argv_array ref_patterns =ARGV_ARRAY_INIT ;struct remote *remote;struct transport *transport;
@@ -74,8 +75,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)if (argc >1 ) {int i; pattern =xcalloc (argc,sizeof (const char *)); -for (i =1 ; i < argc; i++) +for (i =1 ; i < argc; i++) { pattern[i -1 ] =xstrfmt ("*/%s" , argv[i]); +argv_array_push (&ref_patterns, argv[i]); + } } remote =remote_get (dest);
@@ -96,7 +99,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)if (uploadpack !=NULL )transport_set_option (transport,TRANS_OPT_UPLOADPACK , uploadpack); - ref =transport_get_remote_refs (transport,NULL ); + ref =transport_get_remote_refs (transport, &ref_patterns);if (transport_disconnect (transport))return 1 ;
t/t5702-protocol-v2.sh +16 -0
@@ -30,6 +30,14 @@ test_expect_success 'list refs with git:// using protocol v2' ' test_cmp actual expect ' +test_expect_success 'ref advertisment is filtered with ls-remote using protocol v2' ' + GIT_TRACE_PACKET=1 git -c protocol.version=2 \ + ls-remote "$GIT_DAEMON_URL/parent" master 2>log && + + grep "ref-pattern master" log && + ! grep "refs/tags/" log +' + stop_git_daemon # Test protocol v2 with 'file://' transport
@@ -50,4 +58,12 @@ test_expect_success 'list refs with file:// using protocol v2' ' test_cmp actual expect ' +test_expect_success 'ref advertisment is filtered with ls-remote using protocol v2' ' + GIT_TRACE_PACKET=1 git -c protocol.version=2 \ + ls-remote "file://$(pwd)/file_parent" master 2>log && + + grep "ref-pattern master" log && + ! grep "refs/tags/" log +' + test_done
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v3 18/35 fetch: pass ref patterns when fetching
In reply to Brandon WilliamsConstruct a list of ref patterns to be passed to 'transport_get_remote_refs()' from the refspec to be used during the fetch. This list of ref patterns will be used to allow the server to filter the ref advertisement when communicating using protocol v2.
--- builtin/fetch.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
builtin/fetch.c +11 -1
@@ -332,11 +332,21 @@ static struct ref *get_ref_map(struct transport *transport,struct ref *rm;struct ref *ref_map =NULL ;struct ref **tail = &ref_map; +struct argv_array ref_patterns =ARGV_ARRAY_INIT ;/* opportunistically-updated references: */ struct ref *orefs =NULL , **oref_tail = &orefs; -const struct ref *remote_refs =transport_get_remote_refs (transport,NULL ); +const struct ref *remote_refs; + +for (i =0 ; i < refspec_count; i++) { +if (!refspecs[i].exact_sha1) +argv_array_push (&ref_patterns, refspecs[i].src); + } + + remote_refs =transport_get_remote_refs (transport, &ref_patterns); + +argv_array_clear (&ref_patterns);if (refspec_count) {struct refspec *fetch_refspec;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 18/35] fetch: pass ref patterns when fetching
Quoted 9 lines · Brandon Williams
Construct a list of ref patterns to be passed to '
transport_get_remote_refs()' from the refspec to be used during the fetch. This list of ref patterns will be used to allow the server to filter the ref advertisement when communicating using protocol v2.Signed-off-by Brandon Williams <[email protected]>--- builtin/fetch.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
Nice.
I take it that tests covering this come later in the series?
PATCH v3 19/35 push: pass ref patterns when pushing
In reply to Brandon WilliamsConstruct a list of ref patterns to be passed to 'get_refs_list()' from the refspec to be used during the push. This list of ref patterns will be used to allow the server to filter the ref advertisement when communicating using protocol v2.
--- transport.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
transport.c +16 -1
@@ -1026,11 +1026,26 @@ int transport_push(struct transport *transport,int porcelain = flags &TRANSPORT_PUSH_PORCELAIN ;int pretend = flags &TRANSPORT_PUSH_DRY_RUN ;int push_ret, ret, err; +struct refspec *tmp_rs; +struct argv_array ref_patterns =ARGV_ARRAY_INIT ; +int i;if (check_push_refs (local_refs, refspec_nr, refspec) <0 )return -1 ; - remote_refs = transport->vtable->get_refs_list (transport,1 ,NULL ); + tmp_rs =parse_push_refspec (refspec_nr, refspec); +for (i =0 ; i < refspec_nr; i++) { +if (tmp_rs[i].dst) +argv_array_push (&ref_patterns, tmp_rs[i].dst); +else if (tmp_rs[i].src && !tmp_rs[i].exact_sha1) +argv_array_push (&ref_patterns, tmp_rs[i].src); + } + + remote_refs = transport->vtable->get_refs_list (transport,1 , + &ref_patterns); + +argv_array_clear (&ref_patterns); +free_refspec (refspec_nr, tmp_rs);if (flags &TRANSPORT_PUSH_ALL ) match_flags |=MATCH_REFS_ALL ;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 19/35] push: pass ref patterns when pushing
Quoted 22 lines · Brandon Williams
Construct a list of ref patterns to be passed to '
get_refs_list()' from the refspec to be used during the push. This list of ref patterns will be used to allow the server to filter the ref advertisement when communicating using protocol v2.Signed-off-by Brandon Williams <[email protected]>--- transport.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)transport.c +3
-0@@ -1026,11 +1026,26 @@ int transport_push(struct transport *transport,int porcelain = flags &TRANSPORT_PUSH_PORCELAIN ;int pretend = flags &TRANSPORT_PUSH_DRY_RUN ;int push_ret, ret, err; +struct refspec *tmp_rs; +struct argv_array ref_patterns =ARGV_ARRAY_INIT ; +int i;
transport.c int transport_push(struct transport *transport, · line 1041
Quoted 10 lines
transport.c +6
-1if (check_push_refs (local_refs, refspec_nr, refspec) <0 )return -1 ; - remote_refs = transport->vtable->get_refs_list (transport,1 ,NULL ); + tmp_rs =parse_push_refspec (refspec_nr, refspec); +for (i =0 ; i < refspec_nr; i++) { +if (tmp_rs[i].dst) +argv_array_push (&ref_patterns, tmp_rs[i].dst); +else if (tmp_rs[i].src && !tmp_rs[i].exact_sha1) +argv_array_push (&ref_patterns, tmp_rs[i].src);
else /* !tmp_rs[i].dst && (!tmp_rs[i].src || tmp_rs[i].exact_sha1)
I would think the case of !dst && !src cannot happen, as then there is no refspec, but what about the !!exact_sha1 case ?
I'd think that is something like
git push origin $(git rev-parse HEAD)
for which I'd think we'd bail out anyway?
But that would happen at a different place, here we can ignore the exact hashes for listing refs purposes.
Can you add a comment or rather explain in the commit message to make this less confusing?
Stefan
PATCH v3 20/35 upload-pack: introduce fetch server command
In reply to Brandon WilliamsIntroduce the 'fetch' server command.
--- Documentation/technical/protocol-v2.txt | 127 +++++++++++++++ serve.c | 2 + t/t5701-git-serve.sh | 1 + upload-pack.c | 281 ++++++++++++++++++++++++++++++++ upload-pack.h | 5 + 5 files changed, 416 insertions(+)
Documentation/technical/protocol-v2.txt +127 -0
@@ -144,3 +144,130 @@ The output of ls-refs is as follows: ref-attribute = (symref | peeled) symref = "symref-target:" symref-target peeled = "peeled:" obj-id + + fetch +------- + +`fetch` is the command used to fetch a packfile in v2. It can be looked +at as a modified version of the v1 fetch where the ref-advertisement is +stripped out (since the `ls-refs` command fills that role) and the +message format is tweaked to eliminate redundancies and permit easy +addition of future extensions. + +Additional features not supported in the base command will be advertised +as the value of the command in the capability advertisement in the form +of a space separated list of features, e.g. "<command>=<feature 1> +<feature 2>". + +A `fetch` request can take the following parameters wrapped in +packet-lines: + + want <oid> + Indicates to the server an object which the client wants to + retrieve. + + have <oid> + Indicates to the server an object which the client has locally. + This allows the server to make a packfile which only contains + the objects that the client needs. Multiple 'have' lines can be + supplied. + + done + Indicates to the server that negotiation should terminate (or + not even begin if performing a clone) and that the server should + use the information supplied in the request to construct the + packfile. + + thin-pack + Request that a thin pack be sent, which is a pack with deltas + which reference base objects not contained within the pack (but + are known to exist at the receiving end). This can reduce the + network traffic significantly, but it requires the receiving end + to know how to "thicken" these packs by adding the missing bases + to the pack. + + no-progress + Request that progress information that would normally be sent on + side-band channel 2, during the packfile transfer, should not be + sent. However, the side-band channel 3 is still used for error + responses. + + include-tag + Request that annotated tags should be sent if the objects they + point to are being sent. + + ofs-delta + Indicate that the client understands PACKv2 with delta referring + to its base by position in pack rather than by an oid. That is, + they can read OBJ_OFS_DELTA (ake type 6) in a packfile. + +The response of `fetch` is broken into a number of sections separated by +delimiter packets (0001), with each section beginning with its section +header. + + output = *section + section = (acknowledgments | packfile) + (flush-pkt | delim-pkt) + + acknowledgments = PKT-LINE("acknowledgments" LF) + *(ready | nak | ack) + ready = PKT-LINE("ready" LF) + nak = PKT-LINE("NAK" LF) + ack = PKT-LINE("ACK" SP obj-id LF) + + packfile = PKT-LINE("packfile" LF) + [PACKFILE] + +---- + acknowledgments section + * Always begins with the section header "acknowledgments" + + * The server will respond with "NAK" if none of the object ids sent + as have lines were common. + + * The server will respond with "ACK obj-id" for all of the + object ids sent as have lines which are common. + + * A response cannot have both "ACK" lines as well as a "NAK" + line. + + * The server will respond with a "ready" line indicating that + the server has found an acceptable common base and is ready to + make and send a packfile (which will be found in the packfile + section of the same response) + + * If the client determines that it is finished with negotiations + by sending a "done" line, the acknowledgments sections can be + omitted from the server's response as an optimization. + + * If the server has found a suitable cut point and has decided + to send a "ready" line, then the server can decide to (as an + optimization) omit any "ACK" lines it would have sent during + its response. This is because the server will have already + determined the objects it plans to send to the client and no + further negotiation is needed. + +---- + packfile section + * Always begins with the section header "packfile" + + * The transmission of the packfile begins immediately after the + section header + + * The data transfer of the packfile is always multiplexed, using + the same semantics of the 'side-band-64k' capability from + protocol version 1. This means that each packet, during the + packfile data stream, is made up of a leading 4-byte pkt-line + length (typical of the pkt-line format), followed by a 1-byte + stream code, followed by the actual data. + + The stream code can be one of: + 1 - pack data + 2 - progress messages + 3 - fatal error message just before stream aborts + + * This section is only included if the client has sent 'want' + lines in its request and either requested that no more + negotiation be done by sending 'done' or if the server has + decided it has found a sufficient cut point to produce a + packfile.
serve.c +2 -0
@@ -6,6 +6,7 @@#include "argv-array.h" #include "ls-refs.h" #include "serve.h" +#include "upload-pack.h" static int always_advertise (struct repository *r,struct strbuf *value)
@@ -52,6 +53,7 @@ struct protocol_capability {static struct protocol_capability capabilities[] = { {"agent" , agent_advertise,NULL }, {"ls-refs" , always_advertise, ls_refs }, + {"fetch" , always_advertise, upload_pack_v2 }, };static void advertise_capabilities (void )
t/t5701-git-serve.sh +1 -0
@@ -9,6 +9,7 @@ test_expect_success 'test capability advertisement' ' version 2 agent=git/$(git version | cut -d" " -f3) ls-refs + fetch 0000 EOF
upload-pack.c +281 -0
290 lines not on this page · raw message
upload-pack.h +5 -0
@@ -10,4 +10,9 @@ struct upload_pack_options {void upload_pack (struct upload_pack_options *options); +struct repository ; +struct argv_array ; +extern int upload_pack_v2 (struct repository *r,struct argv_array *keys, +struct argv_array *args); +#endif /* UPLOAD_PACK_H */
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 20/35] upload-pack: introduce fetch server command
Documentation/technical/protocol-v2.txt The output of ls-refs is as follows: · line 167
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:57 -0800 Brandon Williams <[email protected]> wrote:
Documentation/technical/protocol-v2.txt +3
-0+ want <oid> + Indicates to the server an object which the client wants to + retrieve.
Mention that the client can "want" anything even if not advertised by the server (like uploadpack.allowanysha1inwant).
Documentation/technical/protocol-v2.txt The output of ls-refs is as follows: · line 213
Quoted 6 lines
Documentation/technical/protocol-v2.txt +6
-0+ output = *section + section = (acknowledgments | packfile) + (flush-pkt | delim-pkt) + + acknowledgments = PKT-LINE("acknowledgments" LF) + *(ready | nak | ack)
Can this part be described more precisely in the BNF section? I see that you describe later that there can be multiple ACKs or one NAK (but not both), and "ready" can be sent regardless of whether ACKs or a NAK is sent.
Quoted 18 lines
Documentation/technical/protocol-v2.txt +18
-0+ ready = PKT-LINE("ready" LF) + nak = PKT-LINE("NAK" LF) + ack = PKT-LINE("ACK" SP obj-id LF) + + packfile = PKT-LINE("packfile" LF) + [PACKFILE] + +---- + acknowledgments section + * Always begins with the section header "acknowledgments" + + * The server will respond with "NAK" if none of the object ids sent + as have lines were common. + + * The server will respond with "ACK obj-id" for all of the + object ids sent as have lines which are common. + + * A response cannot have both "ACK" lines as well as a "NAK"
Documentation/technical/protocol-v2.txt The output of ls-refs is as follows: · line 241
Quoted 10 lines
Documentation/technical/protocol-v2.txt +10
-0+ line. + + * The server will respond with a "ready" line indicating that + the server has found an acceptable common base and is ready to + make and send a packfile (which will be found in the packfile + section of the same response) + + * If the client determines that it is finished with negotiations + by sending a "done" line, the acknowledgments sections can be + omitted from the server's response as an optimization.
Should this be changed to "must"? The current implementation does not support it (on the client side).
Quoted 1 line
+#define
UPLOAD_PACK_DATA_INIT{OBJECT_ARRAY_INIT,OID_ARRAY_INIT, 0, 0, 0, 0, 0, 0 }
Optional: the trailing zeroes can be omitted. (That's shorter, and also easier to maintain when we add new fields.)
Quoted 25 lines
upload-pack.c +25
-0+int upload_pack_v2 (struct repository *r,struct argv_array *keys, +struct argv_array *args) +{ +enum fetch_state state =FETCH_PROCESS_ARGS ; +struct upload_pack_data data =UPLOAD_PACK_DATA_INIT ; + use_sideband =LARGE_PACKET_MAX ; + +while (state !=FETCH_DONE ) { +switch (state) { +case FETCH_PROCESS_ARGS : +process_args (args, &data); + +if (!want_obj.nr) { +/* +* Request didn't contain any 'want' lines, +* guess they didn't want anything. +*/ + state =FETCH_DONE ; + }else if (data.haves.nr) { +/* +* Request had 'have' lines, so lets ACK them. +*/ + state =FETCH_SEND_ACKS ; + }else { +/*
upload-pack.c void upload_pack(struct upload_pack_options *options) · line 1329
Quoted 10 lines
upload-pack.c +10
-0+* Request had 'want's but no 'have's so we can +* immedietly go to construct and send a pack. +*/ + state =FETCH_SEND_PACK ; + } +break ; +case FETCH_READ_HAVES : +read_haves (&data); + state =FETCH_SEND_ACKS ; +break ;
This branch seems to never be taken?
Quoted 19 lines
upload-pack.c +19
-0+case FETCH_SEND_ACKS : +if (process_haves_and_send_acks (&data)) + state =FETCH_SEND_PACK ; +else + state =FETCH_DONE ; +break ; +case FETCH_SEND_PACK : +packet_write_fmt (1 ,"packfile\n" ); +create_pack_file (); + state =FETCH_DONE ; +break ; +case FETCH_DONE : +continue ; + } + } + +upload_pack_data_clear (&data); +return 0 ; +}
Re: [PATCH v3 20/35] upload-pack: introduce fetch server command
Quoted 9 lines · Jonathan Tan
Documentation/technical/protocol-v2.txt The output of ls-refs is as follows: · line 167
Quoted 3 lines · On Tue, 6 Feb 2018 17:12:57 -0800 Brandon Williams <[email protected]> wrote:
Documentation/technical/protocol-v2.txt +3
-0+ want <oid> + Indicates to the server an object which the client wants to + retrieve.Mention that the client can "want" anything even if not advertised by the server (like uploadpack.allowanysha1inwant).
Will do.
Quoted 12 lines
Documentation/technical/protocol-v2.txt The output of ls-refs is as follows: · line 213
Quoted 7 lines
Documentation/technical/protocol-v2.txt +6
-0+ output = *section + section = (acknowledgments | packfile) + (flush-pkt | delim-pkt) + + acknowledgments = PKT-LINE("acknowledgments" LF) + *(ready | nak | ack)Can this part be described more precisely in the BNF section? I see that you describe later that there can be multiple ACKs or one NAK (but not both), and "ready" can be sent regardless of whether ACKs or a NAK is sent.
Yep I'll fix that.
Quoted 24 lines
Quoted 24 lines
Documentation/technical/protocol-v2.txt +23
-0+ ready = PKT-LINE("ready" LF) + nak = PKT-LINE("NAK" LF) + ack = PKT-LINE("ACK" SP obj-id LF) + + packfile = PKT-LINE("packfile" LF) + [PACKFILE] + +---- + acknowledgments section + * Always begins with the section header "acknowledgments" + + * The server will respond with "NAK" if none of the object ids sent + as have lines were common. + + * The server will respond with "ACK obj-id" for all of the + object ids sent as have lines which are common. + + * A response cannot have both "ACK" lines as well as a "NAK" + line. + + * The server will respond with a "ready" line indicating that + the server has found an acceptable common base and is ready to + make and send a packfile (which will be found in the packfile
Quoted 8 lines
Documentation/technical/protocol-v2.txt The output of ls-refs is as follows: · line 241
Quoted 5 lines
Documentation/technical/protocol-v2.txt +5
-0+ section of the same response) + + * If the client determines that it is finished with negotiations + by sending a "done" line, the acknowledgments sections can be + omitted from the server's response as an optimization.Should this be changed to "must"? The current implementation does not support it (on the client side).
This is actually a great question and one which may need to be thought about in terms of its application to future extensions to the fetch command. Since fetch's response is now broken up into sections we may want the client to cope with sections being in any order and maybe even skipping sections it doesn't know about. Not sure if its necessary but its an idea.
Quoted 35 lines
Quoted 2 lines
+#define
UPLOAD_PACK_DATA_INIT{OBJECT_ARRAY_INIT,OID_ARRAY_INIT, 0, 0, 0, 0, 0, 0 }Optional: the trailing zeroes can be omitted. (That's shorter, and also easier to maintain when we add new fields.)
Quoted 29 lines
upload-pack.c +29
-0+int upload_pack_v2 (struct repository *r,struct argv_array *keys, +struct argv_array *args) +{ +enum fetch_state state =FETCH_PROCESS_ARGS ; +struct upload_pack_data data =UPLOAD_PACK_DATA_INIT ; + use_sideband =LARGE_PACKET_MAX ; + +while (state !=FETCH_DONE ) { +switch (state) { +case FETCH_PROCESS_ARGS : +process_args (args, &data); + +if (!want_obj.nr) { +/* +* Request didn't contain any 'want' lines, +* guess they didn't want anything. +*/ + state =FETCH_DONE ; + }else if (data.haves.nr) { +/* +* Request had 'have' lines, so lets ACK them. +*/ + state =FETCH_SEND_ACKS ; + }else { +/* +* Request had 'want's but no 'have's so we can +* immedietly go to construct and send a pack. +*/ + state =FETCH_SEND_PACK ;
Quoted 8 lines
upload-pack.c void upload_pack(struct upload_pack_options *options) · line 1329
Quoted 6 lines
upload-pack.c +6
-0+ } +break ; +case FETCH_READ_HAVES : +read_haves (&data); + state =FETCH_SEND_ACKS ; +break ;This branch seems to never be taken?
Must be left over from another version, I'll remove it.
Quoted 20 lines
Quoted 20 lines
upload-pack.c +19
-0+case FETCH_SEND_ACKS : +if (process_haves_and_send_acks (&data)) + state =FETCH_SEND_PACK ; +else + state =FETCH_DONE ; +break ; +case FETCH_SEND_PACK : +packet_write_fmt (1 ,"packfile\n" ); +create_pack_file (); + state =FETCH_DONE ; +break ; +case FETCH_DONE : +continue ; + } + } + +upload_pack_data_clear (&data); +return 0 ; +}
Signature · 1 line
Brandon Williams
PATCH v3 21/35 fetch-pack: perform a fetch using v2
In reply to Brandon WilliamsWhen communicating with a v2 server, perform a fetch by requesting the 'fetch' command.
--- builtin/fetch-pack.c | 2 +- fetch-pack.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++- fetch-pack.h | 4 +- t/t5702-protocol-v2.sh | 84 +++++++++++++++++ transport.c | 7 +- 5 files changed, 342 insertions(+), 7 deletions(-)
builtin/fetch-pack.c +1 -1
@@ -213,7 +213,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) } ref =fetch_pack (&args, fd, conn, ref, dest, sought, nr_sought, - &shallow, pack_lockfile_ptr); + &shallow, pack_lockfile_ptr, protocol_v0);if (pack_lockfile) {printf ("lock %s\n" , pack_lockfile);fflush (stdout);
fetch-pack.c +249 -3
270 lines not on this page · raw message
fetch-pack.h +3 -1
@@ -3,6 +3,7 @@#include "string-list.h" #include "run-command.h" +#include "protocol.h" struct oid_array ;
@@ -43,7 +44,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args,struct ref **sought, int nr_sought, struct oid_array *shallow, - char **pack_lockfile); + char **pack_lockfile, + enum protocol_version version);/* * Print an appropriate error message for each sought ref that wasn't
t/t5702-protocol-v2.sh +84 -0
94 lines not on this page · raw message
transport.c +5 -2
@@ -256,14 +256,17 @@ static int fetch_refs_via_pack(struct transport *transport,switch (data->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); + refs =fetch_pack (&args, data->fd, data->conn, + refs_tmp ? refs_tmp : transport->remote_refs, + dest, to_fetch, nr_heads, &data->shallow, + &transport->pack_lockfile, data->version);break ;case protocol_v1:case protocol_v0: refs =fetch_pack (&args, data->fd, data->conn, refs_tmp ? refs_tmp : transport->remote_refs, dest, to_fetch, nr_heads, &data->shallow, - &transport->pack_lockfile); + &transport->pack_lockfile, data->version);break ;case protocol_unknown_version:BUG ("unknown protocol version" );
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 21/35] fetch-pack: perform a fetch using v2
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1059
Quoted 5 lines · On Tue, 6 Feb 2018 17:12:58 -0800 Brandon Williams <[email protected]> wrote:
fetch-pack.c +5
-0+while ((oid =get_rev ())) { +packet_buf_write (req_buf,"have %s\n" ,oid_to_hex (oid)); +if (++haves_added >=INITIAL_FLUSH ) +break ; + };
Unnecessary semicolon after closing brace.
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1214
Quoted 6 lines
fetch-pack.c +6
-0+/* Filter 'ref' by 'sought' and those that aren't local */ +if (everything_local (args, &ref, sought, nr_sought)) + state =FETCH_DONE ; +else + state =FETCH_SEND_REQUEST ; +break ;
I haven't looked at this patch in detail, but I found a bug that can be reproduced if you patch the following onto this patch:
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -124,6 +124,7 @@ test_expect_success 'clone with file:// using protocol v2' '
test_expect_success 'fetch with file:// using protocol v2' '
test_commit -C file_parent two &&
+ git -C file_parent tag -d one &&
GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=2 \
fetch origin 2>log &&
@@ -133,7 +134,8 @@ test_expect_success 'fetch with file:// using protocol v2' '
test_cmp expect actual &&
# Server responded using protocol v2
- grep "fetch< version 2" log
+ grep "fetch< version 2" log &&
+ grep "have " log
'Merely including the second hunk (the one with 'grep "have "') does not make the test fail, but including both the first and second hunks does. That is, fetch v2 emits "have" only for remote refs that point to objects we already have, not for local refs.
Everything still appears to work, except that packfiles are usually much larger than they need to be.
I did some digging in the code and found out that the equivalent of find_common() (which calls for_each_ref(rev_list_insert_ref_oid, NULL)) was not called in v2. In v1, find_common() is called immediately after everything_local(), but there is no equivalent in v2. (I quoted the invocation of everything_local() in v2 above.)
Re: [PATCH v3 21/35] fetch-pack: perform a fetch using v2
Quoted 10 lines · Jonathan Tan
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1059
Quoted 5 lines · On Tue, 6 Feb 2018 17:12:58 -0800 Brandon Williams <[email protected]> wrote:
fetch-pack.c +5
-0+while ((oid =get_rev ())) { +packet_buf_write (req_buf,"have %s\n" ,oid_to_hex (oid)); +if (++haves_added >=INITIAL_FLUSH ) +break ; + };Unnecessary semicolon after closing brace.
Thanks, I'll remove that.
Quoted 35 lines
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1214
Quoted 7 lines
fetch-pack.c +6
-0+/* Filter 'ref' by 'sought' and those that aren't local */ +if (everything_local (args, &ref, sought, nr_sought)) + state =FETCH_DONE ; +else + state =FETCH_SEND_REQUEST ; +break ;I haven't looked at this patch in detail, but I found a bug that can be reproduced if you patch the following onto this patch:
--- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -124,6 +124,7 @@ test_expect_success 'clone with file:// using protocol v2' ' test_expect_success 'fetch with file:// using protocol v2' ' test_commit -C file_parent two && + git -C file_parent tag -d one && GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=2 \ fetch origin 2>log && @@ -133,7 +134,8 @@ test_expect_success 'fetch with file:// using protocol v2' ' test_cmp expect actual && # Server responded using protocol v2 - grep "fetch< version 2" log + grep "fetch< version 2" log && + grep "have " log 'Merely including the second hunk (the one with 'grep "have "') does not make the test fail, but including both the first and second hunks does. That is, fetch v2 emits "have" only for remote refs that point to objects we already have, not for local refs.
Quoted 8 lines
Everything still appears to work, except that packfiles are usually much larger than they need to be.
I did some digging in the code and found out that the equivalent of
find_common()(which callsfor_each_ref(rev_list_insert_ref_oid, NULL)) was not called in v2. In v1,find_common()is called immediately aftereverything_local(), but there is no equivalent in v2. (I quoted the invocation ofeverything_local()in v2 above.)
I actually caught this Friday morning when I realized that fetching from a referenced repository would replicated objects instead of using them from the referenced repository. Thanks for pointing this out :)
Signature · 1 line
Brandon Williams
Re: [PATCH v3 21/35] fetch-pack: perform a fetch using v2
In reply to Brandon WilliamsQuoted 107 lines · Brandon Williams
When communicating with a v2 server, perform a fetch by requesting the 'fetch' command.
Signed-off-by Brandon Williams <[email protected]>--- builtin/fetch-pack.c | 2 +- fetch-pack.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++- fetch-pack.h | 4 +- t/t5702-protocol-v2.sh | 84 +++++++++++++++++ transport.c | 7 +- 5 files changed, 342 insertions(+), 7 deletions(-)builtin/fetch-pack.c +1
-1@@ -213,7 +213,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) } ref =fetch_pack (&args, fd, conn, ref, dest, sought, nr_sought, - &shallow, pack_lockfile_ptr); + &shallow, pack_lockfile_ptr, protocol_v0);if (pack_lockfile) {printf ("lock %s\n" , pack_lockfile);fflush (stdout);fetch-pack.c +74
-0@@ -1008,6 +1008,247 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,return ref; } +static void add_wants (const struct ref *wants,struct strbuf *req_buf) +{ +for ( ; wants ; wants = wants->next) { +const struct object_id *remote = &wants->old_oid; +const char *remote_hex; +struct object *o; + +/* +* If that object is complete (i.e. it is an ancestor of a +* local ref), we tell them we have it but do not have to +* tell them about its ancestors, which they already know +* about. +* +* We use lookup_object here because we are only +* interested in the case we *know* the object is +* reachable and we have already scanned it. +*/ +if (((o =lookup_object (remote->hash)) !=NULL ) && + (o->flags &COMPLETE )) { +continue ; + } + + remote_hex =oid_to_hex (remote); +packet_buf_write (req_buf,"want %s\n" , remote_hex); + } +} + +static void add_common (struct strbuf *req_buf,struct oidset *common) +{ +struct oidset_iter iter; +const struct object_id *oid; +oidset_iter_init (common, &iter); + +while ((oid =oidset_iter_next (&iter))) { +packet_buf_write (req_buf,"have %s\n" ,oid_to_hex (oid)); + } +} + +static int add_haves (struct strbuf *req_buf,int *in_vain) +{ +int ret =0 ; +int haves_added =0 ; +const struct object_id *oid; + +while ((oid =get_rev ())) { +packet_buf_write (req_buf,"have %s\n" ,oid_to_hex (oid)); +if (++haves_added >=INITIAL_FLUSH ) +break ; + }; + + *in_vain += haves_added; +if (!haves_added || *in_vain >=MAX_IN_VAIN ) { +/* Send Done */ +packet_buf_write (req_buf,"done\n" ); + ret =1 ; + } + +return ret; +} + +static int send_fetch_request (int fd_out,const struct fetch_pack_args *args, +const struct ref *wants,struct oidset *common, +int *in_vain) +{ +int ret =0 ; +struct strbuf req_buf =STRBUF_INIT ; + +if (server_supports_v2 ("fetch" ,1 )) +packet_buf_write (&req_buf,"command=fetch" ); +if (server_supports_v2 ("agent" ,0 )) +packet_buf_write (&req_buf,"agent=%s" ,git_user_agent_sanitized ()); + +packet_buf_delim (&req_buf); +if (args->use_thin_pack)
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1094
Quoted 10 lines
fetch-pack.c +10
-0+packet_buf_write (&req_buf,"thin-pack" ); +if (args->no_progress) +packet_buf_write (&req_buf,"no-progress" ); +if (args->include_tag) +packet_buf_write (&req_buf,"include-tag" ); +if (prefer_ofs_delta) +packet_buf_write (&req_buf,"ofs-delta" ); + +/* add wants */ +add_wants (wants, &req_buf);
The comment might convey too much redundant information instead of helping the reader gain more understanding. ;)
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1097
Quoted 3 lines
fetch-pack.c +3
-0+ +/* Add all of the common commits we've found in previous rounds */ +add_common (&req_buf, common);
nit:
Maybe s/add_common/add_common_haves/ oradd_previous_haves ?
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1100
Quoted 3 lines
fetch-pack.c +3
-0+ +/* Add initial haves */ + ret =add_haves (&req_buf, in_vain);
I like the shortness and conciseness of this send_fetch_request function as it makes clear what is happening over the wire, however I wonder if we can improve on that, still.
The functions 'add_common' and 'add_haves' seem like they do the same (sending haves), except for different sets of oids.
So I would imagine that a structure like
{
struct set haves = compute_haves_from(in_vain, common, ...);
struct set wants = compute_wants&wants); request_capabilities(args)
send_haves(&haves);
send_wants(&wants);
flush();}
That way we would have an even more concise way of writing one request, and factoring out the business logic. (Coming up with the "right" haves is a heuristic that we plan on changing in the future, so we'd want to have that encapsulated into one function that computes all the haves?
Quoted 48 lines
fetch-pack.c +48
-0+ +/* +* Processes a section header in a server's response and checks if it matches +* `section`. If the value of `peek` is 1, the header line will be peeked (and +* not consumed); if 0, the line will be consumed and the function will die if +* the section header doesn't match what was expected. +*/ +static int process_section_header (struct packet_reader *reader, +const char *section,int peek) +{ +int ret; + +if (packet_reader_peek (reader) !=PACKET_READ_NORMAL ) +die ("error reading packet" ); + + ret = !strcmp (reader->line, section); + +if (!peek) { +if (!ret) +die ("expected '%s', received '%s'" , + section, reader->line); +packet_reader_read (reader); + } + +return ret; +} + +static int process_acks (struct packet_reader *reader,struct oidset *common) +{ +/* received */ +int received_ready =0 ; +int received_ack =0 ; + +process_section_header (reader,"acknowledgments" ,0 ); +while (packet_reader_read (reader) ==PACKET_READ_NORMAL ) { +const char *arg; + +if (!strcmp (reader->line,"NAK" )) +continue ; + +if (skip_prefix (reader->line,"ACK " , &arg)) { +struct object_id oid; +if (!get_oid_hex (arg, &oid)) { +struct commit *commit; +oidset_insert (common, &oid); + commit =lookup_commit (&oid); +mark_common (commit,0 ,1 ); + }
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1166
Quoted 10 lines
fetch-pack.c +10
-0+continue ; + } + +if (!strcmp (reader->line,"ready" )) { +clear_prio_queue (&rev_list); + received_ready =1 ; +continue ; + } + +die (_ ("git fetch-pack: expected ACK/NAK, got '%s'" ), reader->line);
This is slightly misleading, it could also expect "ready" ?
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1171
Quoted 5 lines
fetch-pack.c +5
-0+ } + +if (reader->status !=PACKET_READ_FLUSH && + reader->status !=PACKET_READ_DELIM ) +die ("Error during processing acks: %d" , reader->status);
Why is this not translated unlike the one 5 lines prior to this? Do we expect these conditions to come up due to different root causes?
Quoted 11 lines
fetch-pack.c +11
-0+static struct ref *do_fetch_pack_v2 (struct fetch_pack_args *args, +int fd[2 ], +const struct ref *orig_ref, +struct ref **sought,int nr_sought, +char **pack_lockfile) +{ +struct ref *ref =copy_ref_list (orig_ref); +enum fetch_state state =FETCH_CHECK_LOCAL ; +struct oidset common =OIDSET_INIT ; +struct packet_reader reader; +int in_vain =0 ;
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1205
Quoted 10 lines
fetch-pack.c +10
-0+packet_reader_init (&reader, fd[0 ],NULL ,0 , +PACKET_READ_CHOMP_NEWLINE ); + +while (state !=FETCH_DONE ) { +switch (state) { +case FETCH_CHECK_LOCAL : +sort_ref_list (&ref, ref_compare_name); +QSORT (sought, nr_sought, cmp_ref_by_name); + +/* v2 supports these by default */
Is there a doc that says what is all on by default?
Thanks,
Stefan
Re: [PATCH v3 21/35] fetch-pack: perform a fetch using v2
Quoted 7 lines · Stefan Beller
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1100
Quoted 3 lines
fetch-pack.c +3
-0+ +/* Add initial haves */ + ret =add_haves (&req_buf, in_vain);I like the shortness and conciseness of this
send_fetch_requestfunction as it makes clear what is happening over the wire, however I wonder if we can improve on that, still.
I'm sure there is more that can be micro optimized but I don't really want to get distracted by redesigning this logic another time right now.
Quoted 74 lines
The functions '
add_common' and 'add_haves' seem like they do the same (sending haves), except for different sets of oids.So I would imagine that a structure like
{ struct set haves = compute_haves_from(in_vain, common, ...); struct set wants = compute_wants&wants);request_capabilities(args) send_haves(&haves); send_wants(&wants); flush();}
That way we would have an even more concise way of writing one request, and factoring out the business logic. (Coming up with the "right" haves is a heuristic that we plan on changing in the future, so we'd want to have that encapsulated into one function that computes all the haves?
Quoted 52 lines
fetch-pack.c +52
-0+ +/* +* Processes a section header in a server's response and checks if it matches +* `section`. If the value of `peek` is 1, the header line will be peeked (and +* not consumed); if 0, the line will be consumed and the function will die if +* the section header doesn't match what was expected. +*/ +static int process_section_header (struct packet_reader *reader, +const char *section,int peek) +{ +int ret; + +if (packet_reader_peek (reader) !=PACKET_READ_NORMAL ) +die ("error reading packet" ); + + ret = !strcmp (reader->line, section); + +if (!peek) { +if (!ret) +die ("expected '%s', received '%s'" , + section, reader->line); +packet_reader_read (reader); + } + +return ret; +} + +static int process_acks (struct packet_reader *reader,struct oidset *common) +{ +/* received */ +int received_ready =0 ; +int received_ack =0 ; + +process_section_header (reader,"acknowledgments" ,0 ); +while (packet_reader_read (reader) ==PACKET_READ_NORMAL ) { +const char *arg; + +if (!strcmp (reader->line,"NAK" )) +continue ; + +if (skip_prefix (reader->line,"ACK " , &arg)) { +struct object_id oid; +if (!get_oid_hex (arg, &oid)) { +struct commit *commit; +oidset_insert (common, &oid); + commit =lookup_commit (&oid); +mark_common (commit,0 ,1 ); + } +continue ; + } + +if (!strcmp (reader->line,"ready" )) {
Quoted 8 lines
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1166
Quoted 6 lines
fetch-pack.c +6
-0+clear_prio_queue (&rev_list); + received_ready =1 ; +continue ; + } + +die (_ ("git fetch-pack: expected ACK/NAK, got '%s'" ), reader->line);This is slightly misleading, it could also expect "ready" ?
I'll update this.
Quoted 27 lines
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1171
Quoted 7 lines
fetch-pack.c +5
-0+ } + +if (reader->status !=PACKET_READ_FLUSH && + reader->status !=PACKET_READ_DELIM ) +die ("Error during processing acks: %d" , reader->status);Why is this not translated unlike the one 5 lines prior to this? Do we expect these conditions to come up due to different root causes?
Quoted 15 lines
fetch-pack.c +15
-0+static struct ref *do_fetch_pack_v2 (struct fetch_pack_args *args, +int fd[2 ], +const struct ref *orig_ref, +struct ref **sought,int nr_sought, +char **pack_lockfile) +{ +struct ref *ref =copy_ref_list (orig_ref); +enum fetch_state state =FETCH_CHECK_LOCAL ; +struct oidset common =OIDSET_INIT ; +struct packet_reader reader; +int in_vain =0 ; +packet_reader_init (&reader, fd[0 ],NULL ,0 , +PACKET_READ_CHOMP_NEWLINE ); + +while (state !=FETCH_DONE ) {
Quoted 8 lines
fetch-pack.c static struct ref *do_fetch_pack(struct fetch_pack_args *args, · line 1205
Quoted 6 lines
fetch-pack.c +6
-0+switch (state) { +case FETCH_CHECK_LOCAL : +sort_ref_list (&ref, ref_compare_name); +QSORT (sought, nr_sought, cmp_ref_by_name); + +/* v2 supports these by default */ Is there a doc that says what is all on by default?
Yeah protocol-v2.txt should say all of that.
Quoted 3 lines
Thanks,
Stefan
Signature · 1 line
Brandon Williams
PATCH v3 22/35 upload-pack: support shallow requests
In reply to Brandon WilliamsAdd the 'shallow' feature to the protocol version 2 command 'fetch' which indicates that the server supports shallow clients and deepen requets.
--- Documentation/technical/protocol-v2.txt | 67 +++++++++++++++- serve.c | 2 +- t/t5701-git-serve.sh | 2 +- upload-pack.c | 138 +++++++++++++++++++++++--------- upload-pack.h | 3 + 5 files changed, 173 insertions(+), 39 deletions(-)
Documentation/technical/protocol-v2.txt +66 -1
@@ -201,12 +201,42 @@ packet-lines: to its base by position in pack rather than by an oid. That is, they can read OBJ_OFS_DELTA (ake type 6) in a packfile. + shallow <oid> + A client must notify the server of all objects for which it only + has shallow copies of (meaning that it doesn't have the parents + of a commit) by supplying a 'shallow <oid>' line for each such + object so that the serve is aware of the limitations of the + client's history. + + deepen <depth> + Request that the fetch/clone should be shallow having a commit depth of + <depth> relative to the remote side. + + deepen-relative + Requests that the semantics of the "deepen" command be changed + to indicate that the depth requested is relative to the clients + current shallow boundary, instead of relative to the remote + refs. + + deepen-since <timestamp> + Requests that the shallow clone/fetch should be cut at a + specific time, instead of depth. Internally it's equivalent of + doing "rev-list --max-age=<timestamp>". Cannot be used with + "deepen". + + deepen-not <rev> + Requests that the shallow clone/fetch should be cut at a + specific revision specified by '<rev>', instead of a depth. + Internally it's equivalent of doing "rev-list --not <rev>". + Cannot be used with "deepen", but can be used with + "deepen-since". + The response of `fetch` is broken into a number of sections separated by delimiter packets (0001), with each section beginning with its section header. output = *section - section = (acknowledgments | packfile) + section = (acknowledgments | shallow-info | packfile) (flush-pkt | delim-pkt) acknowledgments = PKT-LINE("acknowledgments" LF)
@@ -215,6 +245,11 @@ header. nak = PKT-LINE("NAK" LF) ack = PKT-LINE("ACK" SP obj-id LF) + shallow-info = PKT-LINE("shallow-info" LF) + *PKT-LINE((shallow | unshallow) LF) + shallow = "shallow" SP obj-id + unshallow = "unshallow" SP obj-id + packfile = PKT-LINE("packfile" LF) [PACKFILE]
@@ -247,6 +282,36 @@ header. determined the objects it plans to send to the client and no further negotiation is needed. +---- + shallow-info section + If the client has requested a shallow fetch/clone, a shallow + client requests a fetch or the server is shallow then the + server's response may include a shallow-info section. The + shallow-info section will be include if (due to one of the above + conditions) the server needs to inform the client of any shallow + boundaries or adjustments to the clients already existing + shallow boundaries. + + * Always begins with the section header "shallow-info" + + * If a positive depth is requested, the server will compute the + set of commits which are no deeper than the desired depth. + + * The server sends a "shallow obj-id" line for each commit whose + parents will not be sent in the following packfile. + + * The server sends an "unshallow obj-id" line for each commit + which the client has indicated is shallow, but is no longer + shallow as a result of the fetch (due to its parents being + sent in the following packfile). + + * The server MUST NOT send any "unshallow" lines for anything + which the client has not indicated was shallow as a part of + its request. + + * This section is only included if a packfile section is also + included in the response. + ---- packfile section * Always begins with the section header "packfile"
serve.c +1 -1
@@ -53,7 +53,7 @@ struct protocol_capability {static struct protocol_capability capabilities[] = { {"agent" , agent_advertise,NULL }, {"ls-refs" , always_advertise, ls_refs }, - {"fetch" , always_advertise, upload_pack_v2 }, + {"fetch" , upload_pack_advertise, upload_pack_v2 }, };static void advertise_capabilities (void )
t/t5701-git-serve.sh +1 -1
@@ -9,7 +9,7 @@ test_expect_success 'test capability advertisement' ' version 2 agent=git/$(git version | cut -d" " -f3) ls-refs - fetch + fetch=shallow 0000 EOF
upload-pack.c +102 -36
195 lines not on this page · raw message
upload-pack.h +3 -0
@@ -14,5 +14,8 @@ struct repository;struct argv_array ;extern int upload_pack_v2 (struct repository *r,struct argv_array *keys,struct argv_array *args); +struct strbuf ; +extern int upload_pack_advertise (struct repository *r, +struct strbuf *value);#endif /* UPLOAD_PACK_H */
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 22/35] upload-pack: support shallow requests
Quoted 18 lines · Brandon Williams
Add the 'shallow' feature to the protocol version 2 command 'fetch' which indicates that the server supports shallow clients and deepen requets.
Signed-off-by Brandon Williams <[email protected]>--- Documentation/technical/protocol-v2.txt | 67 +++++++++++++++- serve.c | 2 +- t/t5701-git-serve.sh | 2 +- upload-pack.c | 138 +++++++++++++++++++++++--------- upload-pack.h | 3 + 5 files changed, 173 insertions(+), 39 deletions(-)Documentation/technical/protocol-v2.txt +0
-0
Documentation/technical/protocol-v2.txt packet-lines: · line 205
Quoted 5 lines
Documentation/technical/protocol-v2.txt +2
-0@@ -201,12 +201,42 @@ packet-lines: to its base by position in pack rather than by an oid. That is, they can read OBJ_OFS_DELTA (ake type 6) in a packfile. + shallow <oid> + A client must notify the server of all objects for which it only
s/all objects/all commits/ for preciseness
Documentation/technical/protocol-v2.txt packet-lines: · line 213
Quoted 8 lines
Documentation/technical/protocol-v2.txt +8
-0+ has shallow copies of (meaning that it doesn't have the parents + of a commit) by supplying a 'shallow <oid>' line for each such + object so that the serve is aware of the limitations of the + client's history. + + deepen <depth> + Request that the fetch/clone should be shallow having a commit depth of + <depth> relative to the remote side.
What does depth mean? number of commits, or number of edges? Are there any special numbers (-1, 0, 1, max int) ?
From reading ahead: "Cannot be used with deepen-since, but can be combined with deepen-relative" ?
Quoted 9 lines
Documentation/technical/protocol-v2.txt +9
-0+ + deepen-relative + Requests that the semantics of the "deepen" command be changed + to indicate that the depth requested is relative to the clients + current shallow boundary, instead of relative to the remote + refs. + + deepen-since <timestamp> + Requests that the shallow clone/fetch should be cut at a
Documentation/technical/protocol-v2.txt packet-lines: · line 232
Quoted 10 lines
Documentation/technical/protocol-v2.txt +10
-0+ specific time, instead of depth. Internally it's equivalent of + doing "rev-list --max-age=<timestamp>". Cannot be used with + "deepen". + + deepen-not <rev> + Requests that the shallow clone/fetch should be cut at a + specific revision specified by '<rev>', instead of a depth. + Internally it's equivalent of doing "rev-list --not <rev>". + Cannot be used with "deepen", but can be used with + "deepen-since".
What happens if those are given in combination?
Re: [PATCH v3 22/35] upload-pack: support shallow requests
Quoted 13 lines · Stefan Beller
Quoted 13 lines
Documentation/technical/protocol-v2.txt +13
-0+ + deepen-relative + Requests that the semantics of the "deepen" command be changed + to indicate that the depth requested is relative to the clients + current shallow boundary, instead of relative to the remote + refs. + + deepen-since <timestamp> + Requests that the shallow clone/fetch should be cut at a + specific time, instead of depth. Internally it's equivalent of + doing "rev-list --max-age=<timestamp>". Cannot be used with + "deepen". +
Quoted 8 lines
Documentation/technical/protocol-v2.txt packet-lines: · line 232
Quoted 6 lines
Documentation/technical/protocol-v2.txt +6
-0+ deepen-not <rev> + Requests that the shallow clone/fetch should be cut at a + specific revision specified by '<rev>', instead of a depth. + Internally it's equivalent of doing "rev-list --not <rev>". + Cannot be used with "deepen", but can be used with + "deepen-since".What happens if those are given in combination?
It should be described in the old protocol document or I did a bad job documenting it. Some of these can be combined (I think it's AND logic from rev-list point of view), with the exception of --depth which does not use rev-list underneath and cannot be combined with the others.
Signature · 1 line
Duy
Re: [PATCH v3 22/35] upload-pack: support shallow requests
In reply to Stefan BellerQuoted 36 lines · Stefan Beller
Quoted 18 lines · Brandon Williams
Add the 'shallow' feature to the protocol version 2 command 'fetch' which indicates that the server supports shallow clients and deepen requets.
Signed-off-by Brandon Williams <[email protected]>--- Documentation/technical/protocol-v2.txt | 67 +++++++++++++++- serve.c | 2 +- t/t5701-git-serve.sh | 2 +- upload-pack.c | 138 +++++++++++++++++++++++--------- upload-pack.h | 3 + 5 files changed, 173 insertions(+), 39 deletions(-)Documentation/technical/protocol-v2.txt +0
-0Documentation/technical/protocol-v2.txt packet-lines: · line 205
Quoted 5 lines
Documentation/technical/protocol-v2.txt +2
-0@@ -201,12 +201,42 @@ packet-lines: to its base by position in pack rather than by an oid. That is, they can read OBJ_OFS_DELTA (ake type 6) in a packfile. + shallow <oid> + A client must notify the server of all objects for which it onlys/all objects/all commits/ for preciseness
Documentation/technical/protocol-v2.txt packet-lines: · line 213
Quoted 8 lines
Documentation/technical/protocol-v2.txt +8
-0+ has shallow copies of (meaning that it doesn't have the parents + of a commit) by supplying a 'shallow <oid>' line for each such + object so that the serve is aware of the limitations of the + client's history. + + deepen <depth> + Request that the fetch/clone should be shallow having a commit depth of + <depth> relative to the remote side.
Quoted 5 lines
What does depth mean? number of commits, or number of edges? Are there any special numbers (-1, 0, 1, max int) ?
From reading ahead: "Cannot be used with deepen-since, but can be combined with deepen-relative" ?
It just uses the current logic, which has no documentation on any of that so...I'm not really sure?
Quoted 15 lines
Quoted 15 lines
Documentation/technical/protocol-v2.txt +13
-0+ + deepen-relative + Requests that the semantics of the "deepen" command be changed + to indicate that the depth requested is relative to the clients + current shallow boundary, instead of relative to the remote + refs. + + deepen-since <timestamp> + Requests that the shallow clone/fetch should be cut at a + specific time, instead of depth. Internally it's equivalent of + doing "rev-list --max-age=<timestamp>". Cannot be used with + "deepen". +
Quoted 8 lines
Documentation/technical/protocol-v2.txt packet-lines: · line 232
Quoted 6 lines
Documentation/technical/protocol-v2.txt +6
-0+ deepen-not <rev> + Requests that the shallow clone/fetch should be cut at a + specific revision specified by '<rev>', instead of a depth. + Internally it's equivalent of doing "rev-list --not <rev>". + Cannot be used with "deepen", but can be used with + "deepen-since".What happens if those are given in combination?
Should act as an AND, it uses the old logic and there isn't very much documentation on that...
Signature · 1 line
Brandon Williams
Re: [PATCH v3 22/35] upload-pack: support shallow requests
In reply to Brandon WilliamsHi,
Quoted 4 lines · Brandon Williams
Add the 'shallow' feature to the protocol version 2 command 'fetch' which indicates that the server supports shallow clients and deepen requets.
Quoted 8 lines
Signed-off-by Brandon Williams <[email protected]>--- Documentation/technical/protocol-v2.txt | 67 +++++++++++++++- serve.c | 2 +- t/t5701-git-serve.sh | 2 +- upload-pack.c | 138 +++++++++++++++++++++++--------- upload-pack.h | 3 + 5 files changed, 173 insertions(+), 39 deletions(-)
Yay! We've been running with this for a while at Google (for file:// fetches, at least) and it's been working well.
[...]
Documentation/technical/protocol-v2.txt packet-lines: · line 206
Quoted 9 lines
Documentation/technical/protocol-v2.txt +3
-0@@ -201,12 +201,42 @@ packet-lines: to its base by position in pack rather than by an oid. That is, they can read OBJ_OFS_DELTA (ake type 6) in a packfile. + shallow <oid> + A client must notify the server of all objects for which it only + has shallow copies of (meaning that it doesn't have the parents
Grammar nit: "for which it only has shallow copies of" should be e.g. "for which it only has shallow copies" or "that it only has shallow copies of" or "that it only has shallow copies for".
I think s/objects/commits/ would also help clarify.
Documentation/technical/protocol-v2.txt packet-lines: · line 208
Quoted 2 lines
Documentation/technical/protocol-v2.txt +2
-0+ of a commit) by supplying a 'shallow <oid>' line for each such + object so that the serve is aware of the limitations of the
s/serve/server/
Documentation/technical/protocol-v2.txt packet-lines: · line 209
Quoted 1 line
Documentation/technical/protocol-v2.txt +1
-0+ client's history.
Is it worth mentioning that this is about negotiation? E.g. "so that the server is aware that the client may not have all objects reachable from such commits".
Documentation/technical/protocol-v2.txt packet-lines: · line 212
Quoted 3 lines
Documentation/technical/protocol-v2.txt +3
-0+ + deepen <depth> + Request that the fetch/clone should be shallow having a commit depth of
nit: s/Request/Requests/, for consistency with the others?
Documentation/technical/protocol-v2.txt packet-lines: · line 213
Quoted 1 line
Documentation/technical/protocol-v2.txt +1
-0+ <depth> relative to the remote side.
What does the value of <depth> mean? E.g. does a depth of 1 mean to fetch only the commits named in "have", 2 to fetch those commits plus their parents, etc, or am I off by one?
Is <depth> always a positive number?
What happens if <depth> starts with a 0? Is that a client error?
Documentation/technical/protocol-v2.txt packet-lines: · line 219
Quoted 6 lines
Documentation/technical/protocol-v2.txt +6
-0+ + deepen-relative + Requests that the semantics of the "deepen" command be changed + to indicate that the depth requested is relative to the clients + current shallow boundary, instead of relative to the remote + refs.
s/clients/client's/
s/remote refs/requested commits/ or "wants" or something.
Documentation/technical/protocol-v2.txt packet-lines: · line 225
Quoted 6 lines
Documentation/technical/protocol-v2.txt +6
-0+ + deepen-since <timestamp> + Requests that the shallow clone/fetch should be cut at a + specific time, instead of depth. Internally it's equivalent of + doing "rev-list --max-age=<timestamp>". Cannot be used with + "deepen".
Nits:
s/rev-list/git rev-list/
s/equivalent of/equivalent to/ or 'the equivalent of'.
Since the git-rev-list(1) manpage doesn't tell me: what is the format of <timestamp>? And is the requested time interval inclusive of exclusive?
Documentation/technical/protocol-v2.txt packet-lines: · line 232
Quoted 7 lines
Documentation/technical/protocol-v2.txt +7
-0+ + deepen-not <rev> + Requests that the shallow clone/fetch should be cut at a + specific revision specified by '<rev>', instead of a depth. + Internally it's equivalent of doing "rev-list --not <rev>". + Cannot be used with "deepen", but can be used with + "deepen-since".
Interesting.
nit: s/rev-list/git rev-list/
What is the format of <rev>? E.g. can it be an arbitrary revision specifier or is it an oid?
[...]
Documentation/technical/protocol-v2.txt packet-lines: · line 240
Quoted 4 lines
Documentation/technical/protocol-v2.txt +1
-1output = *section - section = (acknowledgments | packfile) + section = (acknowledgments | shallow-info | packfile) (flush-pkt | delim-pkt)
It looks like sections can go in an arbitrary order. Are there tests to make sure the server can cope with reordering? (I ask not because I mistrust the server but because I have some vague hope that other server implementations might be inspired by our tests.)
[...]
Documentation/technical/protocol-v2.txt header. · line 251
Quoted 8 lines
Documentation/technical/protocol-v2.txt +4
-0@@ -215,6 +245,11 @@ header. nak = PKT-LINE("NAK" LF) ack = PKT-LINE("ACK" SP obj-id LF) + shallow-info = PKT-LINE("shallow-info" LF) + *PKT-LINE((shallow | unshallow) LF) + shallow = "shallow" SP obj-id + unshallow = "unshallow" SP obj-id
Likewise: it looks like shallows and unshallows can be intermixed; can this be either (a) tightened or (b) covered by tests to make sure a later refactoring doesn't accidentally tighten it?
[...]
Documentation/technical/protocol-v2.txt header. · line 289
Quoted 9 lines
Documentation/technical/protocol-v2.txt +5
-0@@ -247,6 +282,36 @@ header. determined the objects it plans to send to the client and no further negotiation is needed. +---- + shallow-info section + If the client has requested a shallow fetch/clone, a shallow + client requests a fetch or the server is shallow then the + server's response may include a shallow-info section.
I'm having trouble parsing this sentence.
Documentation/technical/protocol-v2.txt header. · line 291
Quoted 2 lines
Documentation/technical/protocol-v2.txt +1
-0The + shallow-info section will be include if (due to one of the above
nit: s/include/included/
Quoted 13 lines
Documentation/technical/protocol-v2.txt +13
-0+ conditions) the server needs to inform the client of any shallow + boundaries or adjustments to the clients already existing + shallow boundaries. + + * Always begins with the section header "shallow-info" + + * If a positive depth is requested, the server will compute the + set of commits which are no deeper than the desired depth. + + * The server sends a "shallow obj-id" line for each commit whose + parents will not be sent in the following packfile. + + * The server sends an "unshallow obj-id" line for each commit
Documentation/technical/protocol-v2.txt header. · line 313
Quoted 10 lines
Documentation/technical/protocol-v2.txt +10
-0+ which the client has indicated is shallow, but is no longer + shallow as a result of the fetch (due to its parents being + sent in the following packfile). + + * The server MUST NOT send any "unshallow" lines for anything + which the client has not indicated was shallow as a part of + its request. + + * This section is only included if a packfile section is also + included in the response.
Neat.
I wonder if shallow information is also useful for negotiation. I guess mostly not today, since the client is the only one that suggests "have"s for negotiation and although it could accidentally exclude too many "have"s by going down a path from a server's ack, that feels like a rare case.
[...]
upload-pack.c static void deepen(int depth, int deepen_relative, · line 713
Quoted 7 lines
upload-pack.c +0
-1@@ -710,7 +710,6 @@ static void deepen(int depth, int deepen_relative, }send_unshallow (shallows); -packet_flush (1 );
What does this part do?
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 725
Quoted 8 lines
upload-pack.c +0
-1} static void deepen_by_rev_list(int ac, const char **av,@@ -722,7 +721,52 @@ static void deepen_by_rev_list(int ac, const char **av,send_shallow (result);free_commit_list (result);send_unshallow (shallows); -packet_flush (1 );
Same question.
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 729
Quoted 6 lines
upload-pack.c +6
-0+} + +static int send_shallow_list (int depth,int deepen_rev_list, +timestamp_t deepen_since, +struct string_list *deepen_not, +struct object_array *shallows)
What does the return value from this function represent? It doesn't appear to be the usual "0 means success, -1 means failure" so a comment would help.
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 734
Quoted 5 lines
upload-pack.c +5
-0+{ +int ret =0 ; + +if (depth >0 && deepen_rev_list) +die ("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together" );
nit: long line (can/should "make style" find these?)
The error message is pretty long, longer than a typical 80-column terminal, so probably best to find a way to make the message shorter. E.g.
die("upload-pack: deepen cannot be combined with other deepen-* options");That still would be >80 columns with the indent, so the usual style would be to break it into multiple strings and use C preprocessor concatenation (yuck):
die("upload-pack: "
"deepen cannot be combined with other deepen-* options");[...]
Quoted 5 lines
upload-pack.c +5
-0+if (depth >0 ) { +deepen (depth, deepen_relative, shallows); + ret =1 ; + }else if (deepen_rev_list) { +struct argv_array av =ARGV_ARRAY_INIT ;
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 749
Quoted 10 lines
upload-pack.c +10
-0+int i; + +argv_array_push (&av,"rev-list" ); +if (deepen_since) +argv_array_pushf (&av,"--max-age=%" PRItime, deepen_since); +if (deepen_not->nr) { +argv_array_push (&av,"--not" ); +for (i =0 ; i < deepen_not->nr; i++) { +struct string_list_item *s = deepen_not->items + i; +argv_array_push (&av, s->string);
This accepts arbitrary rev-list arguments, which feels dangerous (could end up doing an expensive operation or reading arbitrary files or finding a way to execute arbitrary code).
[...]
upload-pack.c static void receive_needs(void) · line 902
Quoted 5 lines
upload-pack.c +0
-5-if (deepen_not.nr) { -argv_array_push (&av,"--not" ); -for (i =0 ; i < deepen_not.nr; i++) { -struct string_list_item *s = deepen_not.items + i; -argv_array_push (&av, s->string);
Huh. Looks like some of the above comments are better addressed to an earlier patch.
[...]
upload-pack.c struct upload_pack_data { · line 1093
Quoted 10 lines
upload-pack.c +6
-0@@ -1071,6 +1085,13 @@ struct upload_pack_data {struct object_array wants;struct oid_array haves; +struct object_array shallows; +struct string_list deepen_not; +int depth; +timestamp_t deepen_since; +int deepen_rev_list; +int deepen_relative;
Nice.
Comments describing deepen_Rev_list and deepen_relative would be nice.
Are those boolean? Can they be unsigned:1 to make that self-explanatory?
[...]
upload-pack.c struct upload_pack_data { · line 1104
Quoted 6 lines
upload-pack.c +1
-1@@ -1080,12 +1101,14 @@ struct upload_pack_data { unsigned done : 1; }; -#define UPLOAD_PACK_DATA_INIT { OBJECT_ARRAY_INIT, OID_ARRAY_INIT, 0, 0, 0, 0, 0, 0 } +#define UPLOAD_PACK_DATA_INIT { OBJECT_ARRAY_INIT, OID_ARRAY_INIT, OBJECT_ARRAY_INIT, STRING_LIST_INIT_DUP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
Long line, "make style" should be able to fix it.
[...]
upload-pack.c struct upload_pack_data { · line 1111
Quoted 7 lines
upload-pack.c +2
-0static void upload_pack_data_clear (struct upload_pack_data *data) {object_array_clear (&data->wants);oid_array_clear (&data->haves); +object_array_clear (&data->shallows); +string_list_clear (&data->deepen_not,0 );
Thanks for remembering to clean up.
[...]
Quoted 10 lines
upload-pack.c +6
-0@@ -1284,6 +1323,23 @@ static int process_haves_and_send_acks(struct upload_pack_data *data)return ret; } +static void send_shallow_info (struct upload_pack_data *data) +{ +/* No shallow info needs to be sent */ +if (!data->depth && !data->deepen_rev_list && !data->shallows.nr && + !is_repository_shallow ()) +return ;
upload-pack.c static int process_haves_and_send_acks(struct upload_pack_data *data) · line 1341
Quoted 10 lines
upload-pack.c +10
-0+ +packet_write_fmt (1 ,"shallow-info\n" ); + +if (!send_shallow_list (data->depth, data->deepen_rev_list, + data->deepen_since, &data->deepen_not, + &data->shallows) &&is_repository_shallow ()) +deepen (INFINITE_DEPTH , data->deepen_relative, &data->shallows); + +packet_delim (1 ); +}
Nice.
[...]
upload-pack.c int upload_pack_v2(struct repository *r, struct argv_array *keys, · line 1414
Quoted 12 lines
upload-pack.c +8
-0@@ -1346,3 +1404,11 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,upload_pack_data_clear (&data);return 0 ; } + +int upload_pack_advertise (struct repository *r, +struct strbuf *value) +{ +if (value) +strbuf_addstr (value,"shallow" ); +return 1 ; +}
This is about capabilities?
Maybe a doc comment in the header file would help.
Thanks,
Jonathan
Re: [PATCH v3 22/35] upload-pack: support shallow requests
On 02/27, Jonathan Nieder wrote:
I'll make the documentation changes you suggested.
Quoted 10 lines
Documentation/technical/protocol-v2.txt packet-lines: · line 212
Quoted 2 lines
Documentation/technical/protocol-v2.txt +2
-0+ deepen <depth> + Request that the fetch/clone should be shallow having a commit depth ofnit: s/Request/Requests/, for consistency with the others?
Documentation/technical/protocol-v2.txt packet-lines: · line 213
Quoted 1 line
Documentation/technical/protocol-v2.txt +1
-0+ <depth> relative to the remote side.What does the value of <depth> mean? E.g. does a depth of 1 mean to fetch only the commits named in "have", 2 to fetch those commits plus their parents, etc, or am I off by one?
Honestly I have no clue, what does the current protocol do? There isn't any documentation about it and this just reuses the logic from that.
Quoted 11 lines
Is <depth> always a positive number?
What happens if <depth> starts with a 0? Is that a client error?
Documentation/technical/protocol-v2.txt packet-lines: · line 240
Quoted 4 lines
output = *section
Documentation/technical/protocol-v2.txt +1
-1- section = (acknowledgments | packfile) + section = (acknowledgments | shallow-info | packfile) (flush-pkt | delim-pkt)
Quoted 5 lines
It looks like sections can go in an arbitrary order. Are there tests to make sure the server can cope with reordering? (I ask not because I mistrust the server but because I have some vague hope that other server implementations might be inspired by our tests.)
I'll fix this so that they don't come in arbitrary order
Quoted 6 lines
[...]
Quoted 4 lines
Documentation/technical/protocol-v2.txt +0
-0@@ -215,6 +245,11 @@ header. nak = PKT-LINE("NAK" LF) ack = PKT-LINE("ACK" SP obj-id LF)
Quoted 8 lines
Documentation/technical/protocol-v2.txt header. · line 251
Quoted 4 lines
Documentation/technical/protocol-v2.txt +4
-0+ shallow-info = PKT-LINE("shallow-info" LF) + *PKT-LINE((shallow | unshallow) LF) + shallow = "shallow" SP obj-id + unshallow = "unshallow" SP obj-idLikewise: it looks like shallows and unshallows can be intermixed; can this be either (a) tightened or (b) covered by tests to make sure a later refactoring doesn't accidentally tighten it?
This reuses the existing logic from v0 so its due to that spec.
Quoted 11 lines
upload-pack.c static void deepen(int depth, int deepen_relative, · line 713
Quoted 7 lines
upload-pack.c +0
-1@@ -710,7 +710,6 @@ static void deepen(int depth, int deepen_relative, }send_unshallow (shallows); -packet_flush (1 );What does this part do?
Quoted 2 lines
upload-pack.c +0
-0}
Quoted 8 lines
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 725
Quoted 6 lines
upload-pack.c +0
-1static void deepen_by_rev_list(int ac, const char **av,@@ -722,7 +721,52 @@ static void deepen_by_rev_list(int ac, const char **av,send_shallow (result);free_commit_list (result);send_unshallow (shallows); -packet_flush (1 );Same question.
Pulling out the flush packet so that the logic can be reused for v2, the flush is added back in for the v0 case but not for the v2 case.
Quoted 11 lines
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 729
Quoted 7 lines
upload-pack.c +6
-0+} + +static int send_shallow_list (int depth,int deepen_rev_list, +timestamp_t deepen_since, +struct string_list *deepen_not, +struct object_array *shallows)What does the return value from this function represent? It doesn't appear to be the usual "0 means success, -1 means failure" so a comment would help.
I'll add a comment.
Quoted 45 lines
upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 734
Quoted 6 lines
upload-pack.c +5
-0+{ +int ret =0 ; + +if (depth >0 && deepen_rev_list) +die ("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together" );nit: long line (can/should "make style" find these?)
The error message is pretty long, longer than a typical 80-column terminal, so probably best to find a way to make the message shorter. E.g.
die("upload-pack: deepen cannot be combined with other deepen-* options");That still would be >80 columns with the indent, so the usual style would be to break it into multiple strings and use C preprocessor concatenation (yuck):
die("upload-pack: " "deepen cannot be combined with other deepen-* options");[...]
Quoted 5 lines
upload-pack.c +5
-0+if (depth >0 ) { +deepen (depth, deepen_relative, shallows); + ret =1 ; + }else if (deepen_rev_list) { +struct argv_array av =ARGV_ARRAY_INIT ;upload-pack.c static void deepen_by_rev_list(int ac, const char **av, · line 749
Quoted 10 lines
upload-pack.c +10
-0+int i; + +argv_array_push (&av,"rev-list" ); +if (deepen_since) +argv_array_pushf (&av,"--max-age=%" PRItime, deepen_since); +if (deepen_not->nr) { +argv_array_push (&av,"--not" ); +for (i =0 ; i < deepen_not->nr; i++) { +struct string_list_item *s = deepen_not->items + i; +argv_array_push (&av, s->string);This accepts arbitrary rev-list arguments, which feels dangerous (could end up doing an expensive operation or reading arbitrary files or finding a way to execute arbitrary code).
[...]
Quoted 8 lines
upload-pack.c static void receive_needs(void) · line 902
Quoted 5 lines
upload-pack.c +0
-5-if (deepen_not.nr) { -argv_array_push (&av,"--not" ); -for (i =0 ; i < deepen_not.nr; i++) { -struct string_list_item *s = deepen_not.items + i; -argv_array_push (&av, s->string);Huh. Looks like some of the above comments are better addressed to an earlier patch.
If someone wants to fix this after the fact they can, I just moved this logic, I didn't add it.
Quoted 15 lines
[...]
upload-pack.c struct upload_pack_data { · line 1093
Quoted 10 lines
upload-pack.c +6
-0@@ -1071,6 +1085,13 @@ struct upload_pack_data {struct object_array wants;struct oid_array haves; +struct object_array shallows; +struct string_list deepen_not; +int depth; +timestamp_t deepen_since; +int deepen_rev_list; +int deepen_relative;Nice.
Quoted 4 lines
Comments describing
deepen_Rev_listanddeepen_relativewould be nice.Are those boolean? Can they be unsigned:1 to make that self-explanatory?
They are boolean but are passed via reference at some points so they can't be bit flags.
Quoted 11 lines
[...]
upload-pack.c struct upload_pack_data { · line 1104
Quoted 6 lines
upload-pack.c +1
-1@@ -1080,12 +1101,14 @@ struct upload_pack_data { unsigned done : 1; }; -#define UPLOAD_PACK_DATA_INIT { OBJECT_ARRAY_INIT, OID_ARRAY_INIT, 0, 0, 0, 0, 0, 0 } +#define UPLOAD_PACK_DATA_INIT { OBJECT_ARRAY_INIT, OID_ARRAY_INIT, OBJECT_ARRAY_INIT, STRING_LIST_INIT_DUP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }Long line, "make style" should be able to fix it.
I'll fix this.
Signature · 1 line
Brandon Williams
PATCH v3 23/35 fetch-pack: support shallow requests
In reply to Brandon WilliamsEnable shallow clones and deepen requests using protocol version 2 if the server 'fetch' command supports the 'shallow' feature.
--- connect.c | 22 +++++++++++++++++++ connect.h | 2 ++ fetch-pack.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-)
connect.c +22 -0
@@ -82,6 +82,28 @@ int server_supports_v2(const char *c, int die_on_error)return 0 ; } +int server_supports_feature (const char *c,const char *feature, +int die_on_error) +{ +int i; + +for (i =0 ; i < server_capabilities_v2.argc; i++) { +const char *out; +if (skip_prefix (server_capabilities_v2.argv[i], c, &out) && + (!*out || *(out++) =='=' )) { +if (parse_feature_request (out, feature)) +return 1 ; +else +break ; + } + } + +if (die_on_error) +die ("server doesn't support feature '%s'" , feature); + +return 0 ; +} +static void process_capabilities_v2 (struct packet_reader *reader) {while (packet_reader_read (reader) ==PACKET_READ_NORMAL )
connect.h +2 -0
@@ -17,5 +17,7 @@ struct packet_reader;extern enum protocol_version discover_version (struct packet_reader *reader);extern int server_supports_v2 (const char *c,int die_on_error); +extern int server_supports_feature (const char *c,const char *feature, +int die_on_error);#endif
fetch-pack.c +68 -1
105 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 23/35] fetch-pack: support shallow requests
fetch-pack.c static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, · line 1115
Quoted 7 lines · On Tue, 6 Feb 2018 17:13:00 -0800 Brandon Williams <[email protected]> wrote:
fetch-pack.c +3
-0@@ -1090,6 +1110,10 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,if (prefer_ofs_delta)packet_buf_write (&req_buf,"ofs-delta" ); +/* Add shallow-info and deepen request */ +if (server_supports_feature ("fetch" ,"shallow" ,1 )) +add_shallow_requests (&req_buf, args);
One more thing I observed when trying to implement the server side in JGit - the last argument should be 0, not 1, right? I don't think that "shallow" should be required on the server unless we really need it.
Re: [PATCH v3 23/35] fetch-pack: support shallow requests
Quoted 7 lines · Jonathan Tan
Quoted 4 lines · On Tue, 6 Feb 2018 17:13:00 -0800 Brandon Williams <[email protected]> wrote:
fetch-pack.c +0
-0@@ -1090,6 +1110,10 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,if (prefer_ofs_delta)packet_buf_write (&req_buf,"ofs-delta" );
Quoted 7 lines
fetch-pack.c static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, · line 1115
Quoted 3 lines
fetch-pack.c +3
-0+/* Add shallow-info and deepen request */ +if (server_supports_feature ("fetch" ,"shallow" ,1 )) +add_shallow_requests (&req_buf, args);One more thing I observed when trying to implement the server side in JGit - the last argument should be 0, not 1, right? I don't think that "shallow" should be required on the server unless we really need it.
Good catch, I'll fix that.
Signature · 1 line
Brandon Williams
PATCH v3 24/35 connect: refactor git_connect to only get the protocol version once
In reply to Brandon WilliamsInstead of having each builtin transport asking for which protocol version the user has configured in 'protocol.version' by calling get_protocol_version_config() multiple times, factor this logic out so there is just a single call at the beginning of git_connect().
This will be helpful in the next patch where we can have centralized logic which determines if we need to request a different protocol version than what the user has configured.
--- connect.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-)
connect.c +15 -12
@@ -1029,6 +1029,7 @@ static enum ssh_variant determine_ssh_variant(const char *ssh_command,*/ static struct child_process *git_connect_git (int fd[2 ],char *hostandport,const char *path,const char *prog, +enum protocol_version version,int flags) {struct child_process *conn;
@@ -1067,10 +1068,10 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport, target_host, 0);/* If using a new version put that stuff here after a second null byte */ -if (get_protocol_version_config () >0 ) { +if (version >0 ) {strbuf_addch (&request,'\0' );strbuf_addf (&request,"version=%d%c" , -get_protocol_version_config (),'\0' ); + version,'\0' ); }packet_write (fd[1 ], request.buf, request.len);
@@ -1086,14 +1087,14 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport,*/ static void push_ssh_options (struct argv_array *args,struct argv_array *env,enum ssh_variant variant,const char *port, -int flags) +enum protocol_version version,int flags) {if (variant ==VARIANT_SSH && -get_protocol_version_config () >0 ) { + version >0 ) {argv_array_push (args,"-o" );argv_array_push (args,"SendEnv=" GIT_PROTOCOL_ENVIRONMENT );argv_array_pushf (env,GIT_PROTOCOL_ENVIRONMENT "=version=%d" , -get_protocol_version_config ()); + version); }if (flags &CONNECT_IPV4 ) {
@@ -1146,7 +1147,8 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env,/* Prepare a child_process for use by Git's SSH-tunneled transport. */ static void fill_ssh_args (struct child_process *conn,const char *ssh_host, -const char *port,int flags) +const char *port,enum protocol_version version, +int flags) {const char *ssh;enum ssh_variant variant;
@@ -1180,14 +1182,14 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,argv_array_push (&detect.args, ssh);argv_array_push (&detect.args,"-G" );push_ssh_options (&detect.args, &detect.env_array, -VARIANT_SSH , port, flags); +VARIANT_SSH , port, version, flags);argv_array_push (&detect.args, ssh_host); variant =run_command (&detect) ?VARIANT_SIMPLE :VARIANT_SSH ; }argv_array_push (&conn->args, ssh); -push_ssh_options (&conn->args, &conn->env_array, variant, port, flags); +push_ssh_options (&conn->args, &conn->env_array, variant, port, version, flags);argv_array_push (&conn->args, ssh_host); }
@@ -1208,6 +1210,7 @@ struct child_process *git_connect(int fd[2], const char *url,char *hostandport, *path;struct child_process *conn;enum protocol protocol; +enum protocol_version version =get_protocol_version_config ();/* Without this we cannot rely on waitpid() to tell * what happened to our children.
@@ -1222,7 +1225,7 @@ struct child_process *git_connect(int fd[2], const char *url,printf ("Diag: path=%s\n" , path ? path :"NULL" ); conn =NULL ; }else if (protocol ==PROTO_GIT ) { - conn =git_connect_git (fd, hostandport, path, prog, flags); + conn =git_connect_git (fd, hostandport, path, prog, version, flags); }else {struct strbuf cmd =STRBUF_INIT ;const char *const *var;
@@ -1265,12 +1268,12 @@ struct child_process *git_connect(int fd[2], const char *url,strbuf_release (&cmd);return NULL ; } -fill_ssh_args (conn, ssh_host, port, flags); +fill_ssh_args (conn, ssh_host, port, version, flags); }else {transport_check_allowed ("file" ); -if (get_protocol_version_config () >0 ) { +if (version >0 ) {argv_array_pushf (&conn->env_array,GIT_PROTOCOL_ENVIRONMENT "=version=%d" , -get_protocol_version_config ()); + version); } }argv_array_push (&conn->args, cmd.buf);
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 24/35] connect: refactor git_connect to only get the protocol version once
Quoted 10 lines · On Tue, 6 Feb 2018 17:13:01 -0800 Brandon Williams <[email protected]> wrote:
Instead of having each builtin transport asking for which protocol version the user has configured in 'protocol.version' by calling
get_protocol_version_config()multiple times, factor this logic out so there is just a single call at the beginning ofgit_connect().This will be helpful in the next patch where we can have centralized logic which determines if we need to request a different protocol version than what the user has configured.
Signed-off-by Brandon Williams <[email protected]>
PATCH v3 25/35 connect: don't request v2 when pushing
In reply to Brandon WilliamsIn order to be able to ship protocol v2 with only supporting fetch, we need clients to not issue a request to use protocol v2 when pushing (since the client currently doesn't know how to push using protocol v2). This allows a client to have protocol v2 configured in protocol.version and take advantage of using v2 for fetch and falling back to using v0 when pushing while v2 for push is being designed.
We could run into issues if we didn't fall back to protocol v2 when pushing right now. This is because currently a server will ignore a request to use v2 when contacting the 'receive-pack' endpoint and fall back to using v0, but when push v2 is rolled out to servers, the 'receive-pack' endpoint will start responding using v2. So we don't want to get into a state where a client is requesting to push with v2 before they actually know how to push using v2.
--- connect.c | 8 ++++++++ t/t5702-protocol-v2.sh | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+)
connect.c +8 -0
@@ -1212,6 +1212,14 @@ struct child_process *git_connect(int fd[2], const char *url,enum protocol protocol;enum protocol_version version =get_protocol_version_config (); +/* +* NEEDSWORK: If we are trying to use protocol v2 and we are planning +* to perform a push, then fallback to v0 since the client doesn't know +* how to push yet using v2. +*/ +if (version == protocol_v2 && !strcmp ("git-receive-pack" , prog)) + version = protocol_v0; +/* Without this we cannot rely on waitpid() to tell * what happened to our children. */
t/t5702-protocol-v2.sh +22 -0
@@ -82,6 +82,28 @@ test_expect_success 'pull with git:// using protocol v2' ' grep "fetch< version 2" log ' +test_expect_success 'push with git:// and a config of v2 does not request v2' ' + # Till v2 for push is designed, make sure that if a client has + # protocol.version configured to use v2, that the client instead falls + # back and uses v0. + + test_commit -C daemon_child three && + + # Push to another branch, as the target repository has the + # master branch checked out and we cannot push into it. + GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=2 \ + push origin HEAD:client_branch 2>log && + + git -C daemon_child log -1 --format=%s >actual && + git -C "$daemon_parent" log -1 --format=%s client_branch >expect && + test_cmp expect actual && + + # Client requested to use protocol v2 + ! grep "push> .*\\\0\\\0version=2\\\0$" log && + # Server responded using protocol v2 + ! grep "push< version 2" log +' + stop_git_daemon # Test protocol v2 with 'file://' transport
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v3 26/35 transport-helper: remove name parameter
In reply to Brandon WilliamsCommit 266f1fdfa (transport-helper: be quiet on read errors from helpers, 2013-06-21) removed a call to 'die()' which printed the name of the remote helper passed in to the 'recvline_fh()' function using the 'name' parameter. Once the call to 'die()' was removed the parameter was no longer necessary but wasn't removed. Clean up 'recvline_fh()' parameter list by removing the 'name' parameter.
--- transport-helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
transport-helper.c +3 -3
@@ -49,7 +49,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)die_errno ("Full write to remote helper failed" ); } -static int recvline_fh (FILE *helper,struct strbuf *buffer,const char *name) +static int recvline_fh (FILE *helper,struct strbuf *buffer) {strbuf_reset (buffer);if (debug)
@@ -67,7 +67,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name)static int recvline (struct helper_data *helper,struct strbuf *buffer) { -return recvline_fh (helper->out, buffer, helper->name); +return recvline_fh (helper->out, buffer); }static void write_constant (int fd,const char *str)
@@ -586,7 +586,7 @@ static int process_connect_service(struct transport *transport, goto exit;sendline (data, &cmdbuf); -if (recvline_fh (input, &cmdbuf, name)) +if (recvline_fh (input, &cmdbuf))exit (128 );if (!strcmp (cmdbuf.buf,"" )) {
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 26/35] transport-helper: remove name parameter
Quoted 11 lines · Brandon Williams
Commit
266f1fdfa(transport-helper: be quiet on read errors from helpers, 2013-06-21) removed a call to 'die()' which printed the name of the remote helper passed in to the 'recvline_fh()' function using the 'name' parameter. Once the call to 'die()' was removed the parameter was no longer necessary but wasn't removed. Clean up 'recvline_fh()' parameter list by removing the 'name' parameter.Signed-off-by Brandon Williams <[email protected]>--- transport-helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
Nice.
PATCH v3 27/35 transport-helper: refactor process_connect_service
In reply to Brandon WilliamsA future patch will need to take advantage of the logic which runs and processes the response of the connect command on a remote helper so factor out this logic from 'process_connect_service()' and place it into a helper function 'run_connect()'.
--- transport-helper.c | 67 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 29 deletions(-)
transport-helper.c +38 -29
@@ -545,14 +545,13 @@ static int fetch_with_import(struct transport *transport,return 0 ; } -static int process_connect_service (struct transport *transport, -const char *name,const char *exec) +static int run_connect (struct transport *transport,struct strbuf *cmdbuf) {struct helper_data *data = transport->data; -struct strbuf cmdbuf =STRBUF_INIT ; -struct child_process *helper; -int r, duped, ret =0 ; +int ret =0 ; +int duped;FILE *input; +struct child_process *helper; helper =get_helper (transport);
@@ -568,44 +567,54 @@ static int process_connect_service(struct transport *transport, input =xfdopen (duped,"r" );setvbuf (input,NULL , _IONBF,0 ); +sendline (data, cmdbuf); +if (recvline_fh (input, cmdbuf)) +exit (128 ); + +if (!strcmp (cmdbuf->buf,"" )) { + data->no_disconnect_req =1 ; +if (debug) +fprintf (stderr,"Debug: Smart transport connection " +"ready.\n" ); + ret =1 ; + }else if (!strcmp (cmdbuf->buf,"fallback" )) { +if (debug) +fprintf (stderr,"Debug: Falling back to dumb " +"transport.\n" ); + }else { +die ("Unknown response to connect: %s" , + cmdbuf->buf); + } + +fclose (input); +return ret; +} + +static int process_connect_service (struct transport *transport, +const char *name,const char *exec) +{ +struct helper_data *data = transport->data; +struct strbuf cmdbuf =STRBUF_INIT ; +int ret =0 ; +/* * Handle --upload-pack and friends. This is fire and forget... * just warn if it fails. */ if (strcmp (name, exec)) { - r =set_helper_option (transport,"servpath" , exec); +int r =set_helper_option (transport,"servpath" , exec);if (r >0 )warning ("Setting remote service path not supported by protocol." );else if (r <0 )warning ("Invalid remote service path." ); } -if (data->connect) +if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); -else - goto exit; - -sendline (data, &cmdbuf); -if (recvline_fh (input, &cmdbuf)) -exit (128 ); - -if (!strcmp (cmdbuf.buf,"" )) { - data->no_disconnect_req =1 ; -if (debug) -fprintf (stderr,"Debug: Smart transport connection " -"ready.\n" ); - ret =1 ; - }else if (!strcmp (cmdbuf.buf,"fallback" )) { -if (debug) -fprintf (stderr,"Debug: Falling back to dumb " -"transport.\n" ); - }else -die ("Unknown response to connect: %s" , - cmdbuf.buf); + ret =run_connect (transport, &cmdbuf); + } -exit:strbuf_release (&cmdbuf); -fclose (input);return ret; }
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v3 28/35 transport-helper: introduce stateless-connect
In reply to Brandon WilliamsIntroduce the transport-helper capability 'stateless-connect'. This capability indicates that the transport-helper can be requested to run the 'stateless-connect' command which should attempt to make a stateless connection with a remote end. Once established, the connection can be used by the git client to communicate with the remote end natively in a stateless-rpc manner as supported by protocol v2. This means that the client must send everything the server needs in a single request as the client must not assume any state-storing on the part of the server or transport.
If a stateless connection cannot be established then the remote-helper will respond in the same manner as the 'connect' command indicating that the client should fallback to using the dumb remote-helper commands.
--- transport-helper.c | 8 ++++++++ transport.c | 1 + transport.h | 6 ++++++ 3 files changed, 15 insertions(+)
transport-helper.c +8 -0
@@ -26,6 +26,7 @@ struct helper_data { option : 1, push : 1, connect : 1, + stateless_connect : 1, signed_tags : 1, check_connectivity : 1, no_disconnect_req : 1,
@@ -188,6 +189,8 @@ static struct child_process *get_helper(struct transport *transport) refspecs[refspec_nr++] =xstrdup (arg); }else if (!strcmp (capname,"connect" )) { data->connect =1 ; + }else if (!strcmp (capname,"stateless-connect" )) { + data->stateless_connect =1 ; }else if (!strcmp (capname,"signed-tags" )) { data->signed_tags =1 ; }else if (skip_prefix (capname,"export-marks " , &arg)) {
@@ -612,6 +615,11 @@ static int process_connect_service(struct transport *transport,if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); ret =run_connect (transport, &cmdbuf); + }else if (data->stateless_connect) { +strbuf_addf (&cmdbuf,"stateless-connect %s\n" , name); + ret =run_connect (transport, &cmdbuf); +if (ret) + transport->stateless_rpc =1 ; }strbuf_release (&cmdbuf);
transport.c +1 -0
@@ -250,6 +250,7 @@ static int fetch_refs_via_pack(struct transport *transport, data->options.check_self_contained_and_connected; args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; + args.stateless_rpc = transport->stateless_rpc;if (!data->got_remote_heads) refs_tmp =get_refs_via_connect (transport,0 ,NULL );
transport.h +6 -0
@@ -55,6 +55,12 @@ struct transport {*/ unsigned cloning : 1; +/* +* Indicates that the transport is connected via a half-duplex +* connection and should operate in stateless-rpc mode. +*/ + unsigned stateless_rpc : 1; +/* * These strings will be passed to the {pre, post}-receive hook, * on the remote side, if both sides support the push options capability.
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 28/35] transport-helper: introduce stateless-connect
Quoted 9 lines · On Tue, 6 Feb 2018 17:13:05 -0800 Brandon Williams <[email protected]> wrote:
Introduce the transport-helper capability 'stateless-connect'. This capability indicates that the transport-helper can be requested to run the 'stateless-connect' command which should attempt to make a stateless connection with a remote end. Once established, the connection can be used by the git client to communicate with the remote end natively in a stateless-rpc manner as supported by protocol v2. This means that the client must send everything the server needs in a single request as the client must not assume any state-storing on the part of the server or transport.
Maybe it's worth mentioning that support in the actual remote helpers will be added in a subsequent patch.
Quoted 3 lines
If a stateless connection cannot be established then the remote-helper will respond in the same manner as the 'connect' command indicating that the client should fallback to using the dumb remote-helper commands.
This makes sense, but there doesn't seem to be any code in this patch that implements this.
transport-helper.c static int process_connect_service(struct transport *transport, · line 622
Quoted 9 lines
transport-helper.c +5
-0@@ -612,6 +615,11 @@ static int process_connect_service(struct transport *transport,if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); ret =run_connect (transport, &cmdbuf); + }else if (data->stateless_connect) { +strbuf_addf (&cmdbuf,"stateless-connect %s\n" , name); + ret =run_connect (transport, &cmdbuf); +if (ret) + transport->stateless_rpc =1 ;
Why is process_connect_service() falling back to stateless_connect if connect doesn't work? I don't think this fallback would work, as a client that needs "connect" might need its full capabilities.
Re: [PATCH v3 28/35] transport-helper: introduce stateless-connect
Quoted 13 lines · Jonathan Tan
Quoted 9 lines · On Tue, 6 Feb 2018 17:13:05 -0800 Brandon Williams <[email protected]> wrote:
Introduce the transport-helper capability 'stateless-connect'. This capability indicates that the transport-helper can be requested to run the 'stateless-connect' command which should attempt to make a stateless connection with a remote end. Once established, the connection can be used by the git client to communicate with the remote end natively in a stateless-rpc manner as supported by protocol v2. This means that the client must send everything the server needs in a single request as the client must not assume any state-storing on the part of the server or transport.
Quoted 2 lines
Maybe it's worth mentioning that support in the actual remote helpers will be added in a subsequent patch.
I can mention that.
Quoted 8 lines
Quoted 4 lines
If a stateless connection cannot be established then the remote-helper will respond in the same manner as the 'connect' command indicating that the client should fallback to using the dumb remote-helper commands.
This makes sense, but there doesn't seem to be any code in this patch that implements this.
Quoted 13 lines
transport-helper.c static int process_connect_service(struct transport *transport, · line 622
Quoted 9 lines
transport-helper.c +5
-0@@ -612,6 +615,11 @@ static int process_connect_service(struct transport *transport,if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); ret =run_connect (transport, &cmdbuf); + }else if (data->stateless_connect) { +strbuf_addf (&cmdbuf,"stateless-connect %s\n" , name); + ret =run_connect (transport, &cmdbuf); +if (ret) + transport->stateless_rpc =1 ;Why is
process_connect_service()falling back tostateless_connectif connect doesn't work? I don't think this fallback would work, as a client that needs "connect" might need its full capabilities.
Right now there isn't really a notion of "needing" connect since if connect fails then you need to fallback to doing the dumb thing. Also note that there isn't all fallback from connect to stateless-connect here. If the remote helper advertises connect, only connect will be tried even if stateless-connect is advertised. So this only really works in the case where stateless-connect is advertised and connect isn't, as is with our http remote-helper.
Signature · 1 line
Brandon Williams
Re: [PATCH v3 28/35] transport-helper: introduce stateless-connect
Quoted 14 lines · On Thu, 22 Feb 2018 10:53:53 -0800 Brandon Williams <[email protected]> wrote:
Quoted 5 lines
Quoted 5 lines
transport-helper.c +1
-0@@ -612,6 +615,11 @@ static int process_connect_service(struct transport *transport,if (data->connect) {strbuf_addf (&cmdbuf,"connect %s\n" , name); ret =run_connect (transport, &cmdbuf); + }else if (data->stateless_connect) {Quoted 8 lines
transport-helper.c static int process_connect_service(struct transport *transport, · line 622
Quoted 4 lines
transport-helper.c +4
-0+strbuf_addf (&cmdbuf,"stateless-connect %s\n" , name); + ret =run_connect (transport, &cmdbuf); +if (ret) + transport->stateless_rpc =1 ;Why is
process_connect_service()falling back tostateless_connectif connect doesn't work? I don't think this fallback would work, as a client that needs "connect" might need its full capabilities.
Quoted 7 lines
Right now there isn't really a notion of "needing" connect since if connect fails then you need to fallback to doing the dumb thing. Also note that there isn't all fallback from connect to stateless-connect here. If the remote helper advertises connect, only connect will be tried even if stateless-connect is advertised. So this only really works in the case where stateless-connect is advertised and connect isn't, as is with our http remote-helper.
After some in-office discussion, I think I understand how this works. Assuming a HTTP server that supports protocol v2 (at least for ls-refs/fetch):
1. Fetch, which supports protocol v2, will (indirectly) call process_connect_service. If it learns that it supports v2, it must know that what's returned may not be a fully bidirectional channel, but may only be a stateless-connect channel (and it does know).
2. Archive/upload-archive, which does not support protocol v2, will (indirectly) call process_connect_service. stateless_connect checks info/refs and observes that the server supports protocol v2, so it returns a stateless-connect channel. The user, being unaware of protocol versions, tries to use it, and it doesn't work. (This is a slight regression in that previously, it would fail more quickly - archive/upload-archive has always not supported HTTP because HTTP doesn't support connect.)
I still think that it's too confusing for process_connect_service() to attempt to fallback to stateless-connect, at least because the user must remember that process_connect_service() returns such a channel if protocol v2 is used (and existing code must be updated to know this). It's probably better to have a new API that can return either a connect channel or a stateless-connect channel, and the user will always use it as if it was a stateless-connect channel. The old API then can be separately deprecated and removed, if desired.
Re: [PATCH v3 28/35] transport-helper: introduce stateless-connect
In reply to Brandon WilliamsQuoted 14 lines · Brandon Williams
Introduce the transport-helper capability 'stateless-connect'. This capability indicates that the transport-helper can be requested to run the 'stateless-connect' command which should attempt to make a stateless connection with a remote end. Once established, the connection can be used by the git client to communicate with the remote end natively in a stateless-rpc manner as supported by protocol v2. This means that the client must send everything the server needs in a single request as the client must not assume any state-storing on the part of the server or transport.
If a stateless connection cannot be established then the remote-helper will respond in the same manner as the 'connect' command indicating that the client should fallback to using the dumb remote-helper commands.
Quoted 6 lines
Signed-off-by Brandon Williams <[email protected]>--- transport-helper.c | 8 ++++++++ transport.c | 1 + transport.h | 6 ++++++ 3 files changed, 15 insertions(+)
Please add documentation for this command toDocumentation/gitremote-helpers.txt.
That helps reviewers, since it means reviewers can get a sense of what the interface is meant to be. It helps remote helper implementers as
well: it tells them what they can rely on and what can't rely on in this interface. For the same reason it helpers remote helper callers as well.
Thanks,
Jonathan
Re: [PATCH v3 28/35] transport-helper: introduce stateless-connect
Quoted 26 lines · Jonathan Nieder
Quoted 14 lines · Brandon Williams
Introduce the transport-helper capability 'stateless-connect'. This capability indicates that the transport-helper can be requested to run the 'stateless-connect' command which should attempt to make a stateless connection with a remote end. Once established, the connection can be used by the git client to communicate with the remote end natively in a stateless-rpc manner as supported by protocol v2. This means that the client must send everything the server needs in a single request as the client must not assume any state-storing on the part of the server or transport.
If a stateless connection cannot be established then the remote-helper will respond in the same manner as the 'connect' command indicating that the client should fallback to using the dumb remote-helper commands.
Quoted 6 lines
Signed-off-by Brandon Williams <[email protected]>--- transport-helper.c | 8 ++++++++ transport.c | 1 + transport.h | 6 ++++++ 3 files changed, 15 insertions(+)Please add documentation for this command to
Documentation/gitremote-helpers.txt.
Quoted 8 lines
That helps reviewers, since it means reviewers can get a sense of what the interface is meant to be. It helps remote helper implementers as
well: it tells them what they can rely on and what can't rely on in this interface. For the same reason it helpers remote helper callers as well.Thanks,
Jonathan
Thanks for reminding me. I had intended to at some point but had forgotten to do so. I'm going to mark this it as experimental and for internal use only so that we can still tweak the interface if we want before it becomes stable.
Signature · 1 line
Brandon Williams
PATCH v3 29/35 pkt-line: add packet_buf_write_len function
In reply to Brandon WilliamsAdd the 'packet_buf_write_len()' function which allows for writing an arbitrary length buffer into a 'struct strbuf' and formatting it in packet-line format.
--- pkt-line.c | 16 ++++++++++++++++ pkt-line.h | 1 + 2 files changed, 17 insertions(+)
pkt-line.c +16 -0
@@ -215,6 +215,22 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)va_end (args); } +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len) +{ +size_t orig_len, n; + + orig_len = buf->len; +strbuf_addstr (buf,"0000" ); +strbuf_add (buf, data, len); + n = buf->len - orig_len; + +if (n >LARGE_PACKET_MAX ) +die ("protocol error: impossibly long line" ); + +set_packet_header (&buf->buf[orig_len], n); +packet_trace (buf->buf + orig_len +4 , n -4 ,1 ); +} +int write_packetized_from_fd (int fd_in,int fd_out) {static char buf[LARGE_PACKET_DATA_MAX ];
pkt-line.h +1 -0
@@ -26,6 +26,7 @@ void packet_buf_flush(struct strbuf *buf);void packet_buf_delim (struct strbuf *buf);void packet_write (int fd_out,const char *buf,size_t size);void packet_buf_write (struct strbuf *buf,const char *fmt, ...) __attribute__((format (printf,2 ,3 ))); +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len);int packet_flush_gently (int fd);int packet_write_fmt_gently (int fd,const char *fmt, ...) __attribute__((format (printf,2 ,3 )));int write_packetized_from_fd (int fd_in,int fd_out);
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 29/35] pkt-line: add packet_buf_write_len function
Quoted 3 lines · Brandon Williams
Add the '
packet_buf_write_len()' function which allows for writing an arbitrary length buffer into a 'struct strbuf' and formatting it in packet-line format.
Makes sense.
[...]
pkt-line.h void packet_buf_flush(struct strbuf *buf); · line 29
Quoted 7 lines
pkt-line.h +1
-0@@ -26,6 +26,7 @@ void packet_buf_flush(struct strbuf *buf);void packet_buf_delim (struct strbuf *buf);void packet_write (int fd_out,const char *buf,size_t size);void packet_buf_write (struct strbuf *buf,const char *fmt, ...) __attribute__((format (printf,2 ,3 ))); +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len);
I wonder if we should rename packet_buf_write to something like packet_buf_writef. Right now there's a kind of confusing collection of functions without much symmetry.
Alternatively, the _buf_ ones could become strbuf_* functions:
strbuf_add_packet(&buf, data, len); strbuf_addf_packet(&buf, fmt, ...);
That would make it clearer that these append to buf.
I'm just thinking out loud. For this series, the API you have here looks fine, even if it is a bit inconsistent. (In other words, even if you agree with me, this would probably be best addressed as a patch on top.)
[...]
Quoted 7 lines
pkt-line.c +1
-0@@ -215,6 +215,22 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)va_end (args); } +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len)
pkt-line.c void packet_buf_write(struct strbuf *buf, const char *fmt, ...) · line 228
Quoted 10 lines
pkt-line.c +10
-0+{ +size_t orig_len, n; + + orig_len = buf->len; +strbuf_addstr (buf,"0000" ); +strbuf_add (buf, data, len); + n = buf->len - orig_len; + +if (n >LARGE_PACKET_MAX ) +die ("protocol error: impossibly long line" );
Could the error message describe the long line (e.g.
...impossibly long line %.*s...", 256, data);
)?
pkt-line.c void packet_buf_write(struct strbuf *buf, const char *fmt, ...) · line 231
Quoted 3 lines
pkt-line.c +3
-0+ +set_packet_header (&buf->buf[orig_len], n); +packet_trace (buf->buf + orig_len +4 , n -4 ,1 );
Could do, more simply:
packet_trace(data, len, 1);
Thanks,
Jonathan
Re: [PATCH v3 29/35] pkt-line: add packet_buf_write_len function
Quoted 26 lines · Jonathan Nieder
Quoted 3 lines · Brandon Williams
Add the '
packet_buf_write_len()' function which allows for writing an arbitrary length buffer into a 'struct strbuf' and formatting it in packet-line format.Makes sense.
[...]
pkt-line.h void packet_buf_flush(struct strbuf *buf); · line 29
Quoted 7 lines
pkt-line.h +1
-0@@ -26,6 +26,7 @@ void packet_buf_flush(struct strbuf *buf);void packet_buf_delim (struct strbuf *buf);void packet_write (int fd_out,const char *buf,size_t size);void packet_buf_write (struct strbuf *buf,const char *fmt, ...) __attribute__((format (printf,2 ,3 ))); +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len);I wonder if we should rename
packet_buf_writeto something likepacket_buf_writef. Right now there's a kind of confusing collection of functions without much symmetry.Alternatively, the _buf_ ones could become strbuf_* functions:
strbuf_add_packet(&buf, data, len); strbuf_addf_packet(&buf, fmt, ...);
Quoted 6 lines
That would make it clearer that these append to buf.
I'm just thinking out loud. For this series, the API you have here looks fine, even if it is a bit inconsistent. (In other words, even if you agree with me, this would probably be best addressed as a patch on top.)
Yeah I agree that an api change is needed, but yeah it can be done on top of this series.
Quoted 22 lines
[...]
Quoted 7 lines
pkt-line.c +1
-0@@ -215,6 +215,22 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)va_end (args); } +void packet_buf_write_len (struct strbuf *buf,const char *data,size_t len)pkt-line.c void packet_buf_write(struct strbuf *buf, const char *fmt, ...) · line 228
Quoted 10 lines
pkt-line.c +10
-0+{ +size_t orig_len, n; + + orig_len = buf->len; +strbuf_addstr (buf,"0000" ); +strbuf_add (buf, data, len); + n = buf->len - orig_len; + +if (n >LARGE_PACKET_MAX ) +die ("protocol error: impossibly long line" );Could the error message describe the long line (e.g.
Quoted 2 lines
...impossibly long line %.*s...", 256, data);
I was reusing the error msg as it appears in another part of this file.
Quoted 9 lines
)?
pkt-line.c void packet_buf_write(struct strbuf *buf, const char *fmt, ...) · line 231
Quoted 3 lines
pkt-line.c +3
-0+ +set_packet_header (&buf->buf[orig_len], n); +packet_trace (buf->buf + orig_len +4 , n -4 ,1 );Could do, more simply:
packet_trace(data, len, 1);
I'll change this.
Quoted 3 lines
Thanks,
Jonathan
Signature · 1 line
Brandon Williams
PATCH v3 30/35 remote-curl: create copy of the service name
In reply to Brandon WilliamsMake a copy of the service name being requested instead of relying on the buffer pointed to by the passed in 'const char *' to remain unchanged.
--- remote-curl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
remote-curl.c +3 -2
@@ -165,7 +165,7 @@ static int set_option(const char *name, const char *value) }struct discovery { -const char *service; +char *service;char *buf_alloc;char *buf;size_t len;
@@ -257,6 +257,7 @@ static void free_discovery(struct discovery *d)free (d->shallow.oid);free (d->buf_alloc);free_refs (d->refs); +free (d->service);free (d); } }
@@ -343,7 +344,7 @@ static struct discovery *discover_refs(const char *service, int for_push)warning (_ ("redirecting to %s" ), url.buf); last=xcalloc (1 ,sizeof (*last_discovery)); - last->service = service; + last->service =xstrdup (service); last->buf_alloc =strbuf_detach (&buffer, &last->len); last->buf = last->buf_alloc;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 30/35] remote-curl: create copy of the service name
Quoted 5 lines · On Tue, 6 Feb 2018 17:13:07 -0800 Brandon Williams <[email protected]> wrote:
Make a copy of the service name being requested instead of relying on the buffer pointed to by the passed in 'const char *' to remain unchanged.
Signed-off-by Brandon Williams <[email protected]>
Probably worth mentioning in the commit message:
Currently, all service names are string constants, but a subsequent patch will introduce service names from external sources.
Other than that,
Re: [PATCH v3 30/35] remote-curl: create copy of the service name
Quoted 9 lines · Jonathan Tan
Quoted 5 lines · On Tue, 6 Feb 2018 17:13:07 -0800 Brandon Williams <[email protected]> wrote:
Make a copy of the service name being requested instead of relying on the buffer pointed to by the passed in 'const char *' to remain unchanged.
Signed-off-by Brandon Williams <[email protected]>
Quoted 8 lines
Probably worth mentioning in the commit message:
Currently, all service names are string constants, but a subsequent patch will introduce service names from external sources.
Other than that,
Reviewed-by Jonathan Tan <[email protected]>
I'll add that.
Signature · 1 line
Brandon Williams
PATCH v3 31/35 remote-curl: store the protocol version the server responded with
In reply to Brandon WilliamsStore the protocol version the server responded with when performing discovery. This will be used in a future patch to either change the 'Git-Protocol' header sent in subsequent requests or to determine if a client needs to fallback to using a different protocol version.
--- remote-curl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
remote-curl.c +3 -1
@@ -171,6 +171,7 @@ struct discovery {size_t len;struct ref *refs;struct oid_array shallow; +enum protocol_version version; unsigned proto_git : 1; };static struct discovery *last_discovery;
@@ -184,7 +185,8 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)PACKET_READ_CHOMP_NEWLINE | PACKET_READ_GENTLE_ON_EOF); -switch (discover_version (&reader)) { + heads->version =discover_version (&reader); +switch (heads->version) {case protocol_v2:die ("support for protocol v2 not implemented yet" );break ;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 31/35] remote-curl: store the protocol version the server responded with
Quoted 4 lines · Brandon Williams
Store the protocol version the server responded with when performing discovery. This will be used in a future patch to either change the 'Git-Protocol' header sent in subsequent requests or to determine if a client needs to fallback to using a different protocol version.
nit: s/fallback/fall back/ (fallback is the noun/adjective, fall back the verb)
Quoted 1 line
Signed-off-by Brandon Williams <[email protected]>
With or without that tweak,
Thanks.
PATCH v3 32/35 http: allow providing extra headers for http requests
In reply to Brandon WilliamsAdd a way for callers to request that extra headers be included when making http requests.
--- http.c | 8 ++++++++ http.h | 2 ++ 2 files changed, 10 insertions(+)
http.c +8 -0
@@ -1723,6 +1723,14 @@ static int http_request(const char *url, headers =curl_slist_append (headers, buf.buf); +/* Add additional headers here */ +if (options && options->extra_headers) { +const struct string_list_item *item; +for_each_string_list_item (item, options->extra_headers) { + headers =curl_slist_append (headers, item->string); + } + } +curl_easy_setopt (slot->curl,CURLOPT_URL , url);curl_easy_setopt (slot->curl,CURLOPT_HTTPHEADER , headers);curl_easy_setopt (slot->curl,CURLOPT_ENCODING ,"gzip" );
http.h +2 -0
@@ -172,6 +172,8 @@ struct http_get_options {* for details. */ struct strbuf *base_url; + +struct string_list *extra_headers; };/* Return values for http_get_*() */
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 32/35] http: allow providing extra headers for http requests
http.h struct http_get_options { · line 176
Quoted 6 lines · On Tue, 6 Feb 2018 17:13:09 -0800 Brandon Williams <[email protected]> wrote:
http.h +2
-0@@ -172,6 +172,8 @@ struct http_get_options {* for details. */ struct strbuf *base_url; + +struct string_list *extra_headers;
Document this? For example:
If not NULL, additional HTTP headers to be sent with the request. The strings in the list must not be freed until after the request.
Re: [PATCH v3 32/35] http: allow providing extra headers for http requests
Quoted 12 lines · Jonathan Tan
http.h struct http_get_options { · line 176
Quoted 6 lines · On Tue, 6 Feb 2018 17:13:09 -0800 Brandon Williams <[email protected]> wrote:
http.h +2
-0@@ -172,6 +172,8 @@ struct http_get_options {* for details. */ struct strbuf *base_url; + +struct string_list *extra_headers;Document this? For example:
Quoted 2 lines
If not NULL, additional HTTP headers to be sent with the request. The strings in the list must not be freed until after the request.
I'll add that.
Signature · 1 line
Brandon Williams
PATCH v3 33/35 http: don't always add Git-Protocol header
In reply to Brandon WilliamsInstead of always sending the Git-Protocol header with the configured version with every http request, explicitly send it when discovering refs and then only send it on subsequent http requests if the server understood the version requested.
--- http.c | 17 ----------------- remote-curl.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 17 deletions(-)
http.c +0 -17
@@ -904,21 +904,6 @@ static void set_from_env(const char **var, const char *envname) *var = val; } -static void protocol_http_header (void ) -{ -if (get_protocol_version_config () >0 ) { -struct strbuf protocol_header =STRBUF_INIT ; - -strbuf_addf (&protocol_header,GIT_PROTOCOL_HEADER ": version=%d" , -get_protocol_version_config ()); - - - extra_http_headers =curl_slist_append (extra_http_headers, - protocol_header.buf); -strbuf_release (&protocol_header); - } -} -void http_init (struct remote *remote,const char *url,int proactive_auth) {char *low_speed_limit;
@@ -949,8 +934,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)if (remote)var_override (&http_proxy_authmethod, remote->http_proxy_authmethod); -protocol_http_header (); - pragma_header =curl_slist_append (http_copy_default_headers (),"Pragma: no-cache" ); no_pragma_header = curl_slist_append(http_copy_default_headers(),
remote-curl.c +33 -0
@@ -291,6 +291,19 @@ static int show_http_message(struct strbuf *type, struct strbuf *charset,return 0 ; } +static int get_protocol_http_header (enum protocol_version version, +struct strbuf *header) +{ +if (version >0 ) { +strbuf_addf (header,GIT_PROTOCOL_HEADER ": version=%d" , + version); + +return 1 ; + } + +return 0 ; +} +static struct discovery *discover_refs (const char *service,int for_push) {struct strbuf exp =STRBUF_INIT ;
@@ -299,6 +312,8 @@ static struct discovery *discover_refs(const char *service, int for_push)struct strbuf buffer =STRBUF_INIT ;struct strbuf refs_url =STRBUF_INIT ;struct strbuf effective_url =STRBUF_INIT ; +struct strbuf protocol_header =STRBUF_INIT ; +struct string_list extra_headers =STRING_LIST_INIT_DUP ;struct discovery *last = last_discovery;int http_ret, maybe_smart =0 ;struct http_get_options http_options;
@@ -318,11 +333,16 @@ static struct discovery *discover_refs(const char *service, int for_push)strbuf_addf (&refs_url,"service=%s" , service); } +/* Add the extra Git-Protocol header */ +if (get_protocol_http_header (get_protocol_version_config (), &protocol_header)) +string_list_append (&extra_headers, protocol_header.buf); +memset (&http_options,0 ,sizeof (http_options)); http_options.content_type = &type; http_options.charset = &charset; http_options.effective_url = &effective_url; http_options.base_url = &url; + http_options.extra_headers = &extra_headers; http_options.initial_request =1 ; http_options.no_cache =1 ; http_options.keep_error =1 ;
@@ -389,6 +409,8 @@ static struct discovery *discover_refs(const char *service, int for_push)strbuf_release (&charset);strbuf_release (&effective_url);strbuf_release (&buffer); +strbuf_release (&protocol_header); +string_list_clear (&extra_headers,0 ); last_discovery = last;return last; }
@@ -425,6 +447,7 @@ struct rpc_state {char *service_url;char *hdr_content_type;char *hdr_accept; +char *protocol_header;char *buf;size_t alloc;size_t len;
@@ -611,6 +634,10 @@ static int post_rpc(struct rpc_state *rpc) headers =curl_slist_append (headers, needs_100_continue ?"Expect: 100-continue" :"Expect:" ); +/* Add the extra Git-Protocol header */ +if (rpc->protocol_header) + headers =curl_slist_append (headers, rpc->protocol_header); + retry: slot =get_active_slot ();
@@ -751,6 +778,11 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)strbuf_addf (&buf,"Accept: application/x-%s-result" , svc); rpc->hdr_accept =strbuf_detach (&buf,NULL ); +if (get_protocol_http_header (heads->version, &buf)) + rpc->protocol_header =strbuf_detach (&buf,NULL ); +else + rpc->protocol_header =NULL ; +while (!err) {int n =packet_read (rpc->out,NULL ,NULL , rpc->buf, rpc->alloc,0 );if (!n)
@@ -778,6 +810,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)free (rpc->service_url);free (rpc->hdr_content_type);free (rpc->hdr_accept); +free (rpc->protocol_header);free (rpc->buf);strbuf_release (&buf);return err;
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
PATCH v3 34/35 remote-curl: implement stateless-connect command
In reply to Brandon WilliamsTeach remote-curl the 'stateless-connect' command which is used to establish a stateless connection with servers which support protocol version 2. This allows remote-curl to act as a proxy, allowing the git client to communicate natively with a remote end, simply using remote-curl as a pass through to convert requests to http.
--- remote-curl.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++- t/t5702-protocol-v2.sh | 41 +++++++++++ 2 files changed, 227 insertions(+), 1 deletion(-)
remote-curl.c +186 -1
@@ -188,7 +188,10 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push) heads->version =discover_version (&reader);switch (heads->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); +/* +* Do nothing. Client should run 'stateless-connect' and +* request the refs themselves. +*/ break ;case protocol_v1:case protocol_v0:
@@ -1082,6 +1085,184 @@ static void parse_push(struct strbuf *buf)free (specs); } +struct proxy_state { +char *service_name; +char *service_url; +struct curl_slist *headers; +struct strbuf request_buffer; +int in; +int out; +struct packet_reader reader; +size_t pos; +int seen_flush; +}; + +static void proxy_state_init (struct proxy_state *p,const char *service_name, +enum protocol_version version) +{ +struct strbuf buf =STRBUF_INIT ; + +memset (p,0 ,sizeof (*p)); + p->service_name =xstrdup (service_name); + + p->in =0 ; + p->out =1 ; +strbuf_init (&p->request_buffer,0 ); + +strbuf_addf (&buf,"%s%s" , url.buf, p->service_name); + p->service_url =strbuf_detach (&buf,NULL ); + + p->headers =http_copy_default_headers (); + +strbuf_addf (&buf,"Content-Type: application/x-%s-request" , p->service_name); + p->headers =curl_slist_append (p->headers, buf.buf); +strbuf_reset (&buf); + +strbuf_addf (&buf,"Accept: application/x-%s-result" , p->service_name); + p->headers =curl_slist_append (p->headers, buf.buf); +strbuf_reset (&buf); + + p->headers =curl_slist_append (p->headers,"Transfer-Encoding: chunked" ); + +/* Add the Git-Protocol header */ +if (get_protocol_http_header (version, &buf)) + p->headers =curl_slist_append (p->headers, buf.buf); + +packet_reader_init (&p->reader, p->in,NULL ,0 , +PACKET_READ_GENTLE_ON_EOF ); + +strbuf_release (&buf); +} + +static void proxy_state_clear (struct proxy_state *p) +{ +free (p->service_name); +free (p->service_url); +curl_slist_free_all (p->headers); +strbuf_release (&p->request_buffer); +} + +static size_t proxy_in (char *buffer,size_t eltsize, +size_t nmemb,void *userdata) +{ +size_t max = eltsize * nmemb; +struct proxy_state *p = userdata; +size_t avail = p->request_buffer.len - p->pos; + +if (!avail) { +if (p->seen_flush) { + p->seen_flush =0 ; +return 0 ; + } + +strbuf_reset (&p->request_buffer); +switch (packet_reader_read (&p->reader)) { +case PACKET_READ_EOF : +die ("unexpected EOF when reading from parent process" ); +case PACKET_READ_NORMAL : +packet_buf_write_len (&p->request_buffer, p->reader.line, + p->reader.pktlen); +break ; +case PACKET_READ_DELIM : +packet_buf_delim (&p->request_buffer); +break ; +case PACKET_READ_FLUSH : +packet_buf_flush (&p->request_buffer); + p->seen_flush =1 ; +break ; + } + p->pos =0 ; + avail = p->request_buffer.len; + } + +if (max < avail) + avail = max; +memcpy (buffer, p->request_buffer.buf + p->pos, avail); + p->pos += avail; +return avail; +} + +static size_t proxy_out (char *buffer,size_t eltsize, +size_t nmemb,void *userdata) +{ +size_t size = eltsize * nmemb; +struct proxy_state *p = userdata; + +write_or_die (p->out, buffer, size); +return size; +} + +static int proxy_post (struct proxy_state *p) +{ +struct active_request_slot *slot; +int err; + + slot =get_active_slot (); + +curl_easy_setopt (slot->curl,CURLOPT_NOBODY ,0 ); +curl_easy_setopt (slot->curl,CURLOPT_POST ,1 ); +curl_easy_setopt (slot->curl,CURLOPT_URL , p->service_url); +curl_easy_setopt (slot->curl,CURLOPT_HTTPHEADER , p->headers); + +/* Setup function to read request from client */ +curl_easy_setopt (slot->curl,CURLOPT_READFUNCTION , proxy_in); +curl_easy_setopt (slot->curl,CURLOPT_READDATA , p); + +/* Setup function to write server response to client */ +curl_easy_setopt (slot->curl,CURLOPT_WRITEFUNCTION , proxy_out); +curl_easy_setopt (slot->curl,CURLOPT_WRITEDATA , p); + + err =run_slot (slot,NULL ); + +if (err !=HTTP_OK ) + err =-1 ; + +return err; +} + +static int stateless_connect (const char *service_name) +{ +struct discovery *discover; +struct proxy_state p; + +/* +* Run the info/refs request and see if the server supports protocol +* v2. If and only if the server supports v2 can we successfully +* establish a stateless connection, otherwise we need to tell the +* client to fallback to using other transport helper functions to +* complete their request. +*/ + discover =discover_refs (service_name,0 ); +if (discover->version != protocol_v2) { +printf ("fallback\n" ); +fflush (stdout); +return -1 ; + }else { +/* Stateless Connection established */ +printf ("\n" ); +fflush (stdout); + } + +proxy_state_init (&p, service_name, discover->version); + +/* +* Dump the capability listing that we got from the server earlier +* during the info/refs request. +*/ +write_or_die (p.out, discover->buf, discover->len); + +/* Peek the next packet line. Until we see EOF keep sending POSTs */ +while (packet_reader_peek (&p.reader) !=PACKET_READ_EOF ) { +if (proxy_post (&p)) { +/* We would have an err here */ +break ; + } + } + +proxy_state_clear (&p); +return 0 ; +} +int cmd_main (int argc,const char **argv) {struct strbuf buf =STRBUF_INIT ;
@@ -1150,12 +1331,16 @@ int cmd_main(int argc, const char **argv)fflush (stdout); }else if (!strcmp (buf.buf,"capabilities" )) { +printf ("stateless-connect\n" );printf ("fetch\n" );printf ("option\n" );printf ("push\n" );printf ("check-connectivity\n" );printf ("\n" );fflush (stdout); + }else if (skip_prefix (buf.buf,"stateless-connect " , &arg)) { +if (!stateless_connect (arg)) +break ; }else {error ("remote-curl: unknown command '%s' from git" , buf.buf);return 1 ;
t/t5702-protocol-v2.sh +41 -0
45 lines not on this page · raw message
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 34/35] remote-curl: implement stateless-connect command
Hi,
Quoted 5 lines · Brandon Williams
Teach remote-curl the 'stateless-connect' command which is used to establish a stateless connection with servers which support protocol version 2. This allows remote-curl to act as a proxy, allowing the git client to communicate natively with a remote end, simply using remote-curl as a pass through to convert requests to http.
Cool! I better look at the spec for that first.
*looks at the previous patch*
Oh, there is no documented spec. :/ I'll muddle through this instead, then.
[...]
remote-curl.c static struct ref *parse_git_refs(struct discovery *heads, int for_push) · line 195
Quoted 12 lines
remote-curl.c +4
-1@@ -188,7 +188,10 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push) heads->version =discover_version (&reader);switch (heads->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); +/* +* Do nothing. Client should run 'stateless-connect' and +* request the refs themselves. +*/ break ;
This is the 'list' command, right? Since we expect the client to run 'stateless-connect' instead, can we make it error out?
[...]
Quoted 5 lines
remote-curl.c +1
-0@@ -1082,6 +1085,184 @@ static void parse_push(struct strbuf *buf)free (specs); } +struct proxy_state {
remote-curl.c static void parse_push(struct strbuf *buf) · line 1098
Quoted 10 lines
remote-curl.c +10
-0+char *service_name; +char *service_url; +struct curl_slist *headers; +struct strbuf request_buffer; +int in; +int out; +struct packet_reader reader; +size_t pos; +int seen_flush; +};
Can this have a comment describing what it is/does? It's not obvious to me at first glance.
It doesn't have to go in a lot of detail since this is an internal implementation detail, but something saying e.g. that this represents a connection to an HTTP server (that's an example; I'm not saying that's what it represents :)) would help.
Quoted 3 lines
remote-curl.c +3
-0+ +static void proxy_state_init (struct proxy_state *p,const char *service_name, +enum protocol_version version)
[...]
Quoted 1 line
+static void
proxy_state_clear(struct proxy_state *p)
Looks sensible.
[...]
remote-curl.c
Quoted 2 lines
remote-curl.c +2
-0+static size_t proxy_in (char *buffer,size_t eltsize, +size_t nmemb,void *userdata)
Can this have a comment describing the interface? (E.g. does it read a single pkt_line? How is the caller expected to use it? Does it satisfy the interface of some callback?)
libcurl's example https://curl.haxx.se/libcurl/c/ftpupload.html just calls this read_callback. Such a name plus a pointer to CURLOPT_READFUNCTION should do the trick; bonus points if the comment says what our implementation of the callback does.
Is this about having peek ability?
remote-curl.c
Quoted 2 lines
remote-curl.c +2
-0+{ +size_t max = eltsize * nmemb;
Can this overflow? st_mult can avoid having to worry about that.
Quoted 24 lines
remote-curl.c +24
-0+struct proxy_state *p = userdata; +size_t avail = p->request_buffer.len - p->pos; + +if (!avail) { +if (p->seen_flush) { + p->seen_flush =0 ; +return 0 ; + } + +strbuf_reset (&p->request_buffer); +switch (packet_reader_read (&p->reader)) { +case PACKET_READ_EOF : +die ("unexpected EOF when reading from parent process" ); +case PACKET_READ_NORMAL : +packet_buf_write_len (&p->request_buffer, p->reader.line, + p->reader.pktlen); +break ; +case PACKET_READ_DELIM : +packet_buf_delim (&p->request_buffer); +break ; +case PACKET_READ_FLUSH : +packet_buf_flush (&p->request_buffer); + p->seen_flush =1 ; +break ;
remote-curl.c static void parse_push(struct strbuf *buf) · line 1182
Quoted 10 lines
remote-curl.c +10
-0+ } + p->pos =0 ; + avail = p->request_buffer.len; + } + +if (max < avail) + avail = max; +memcpy (buffer, p->request_buffer.buf + p->pos, avail); + p->pos += avail; +return avail;
This is a number of bytes, but CURLOPT_READFUNCTION expects a number of items, fread-style. That is:
if (avail < eltsize) ... handle somehow, maybe by reading in more? ... avail_memb = avail / eltsize; memcpy(buffer, p->request_buffer.buf + p->pos, st_mult(avail_memb, eltsize)); p->pos += st_mult(avail_memb, eltsize); return avail_memb;
But https://curl.haxx.se/libcurl/c/CURLOPT_READFUNCTION.html says
Your function must then return the actual number of bytes that it stored in that memory area.
Does this mean eltsize is always 1? This is super confusing...
... ok, a quick grep for fread_func in libcurl reveals that eltsize is indeed always 1. Can we add an assertion so we notice if that changes?
if (eltsize != 1)
BUG("curl read callback called with size = %zu != 1", eltsize);
max = nmemb;[...]
remote-curl.c static void parse_push(struct strbuf *buf) · line 1193
Quoted 9 lines
remote-curl.c +9
-0+static size_t proxy_out (char *buffer,size_t eltsize, +size_t nmemb,void *userdata) +{ +size_t size = eltsize * nmemb; +struct proxy_state *p = userdata; + +write_or_die (p->out, buffer, size); +return size; +}
Nice. Same questions about st_mult or just asserting on eltsize apply here, too.
[...]
Quoted 1 line
+static int
proxy_post(struct proxy_state *p)
What does this function do? Can it get a brief comment?
Quoted 15 lines
remote-curl.c +15
-0+{ +struct active_request_slot *slot; +int err; + + slot =get_active_slot (); + +curl_easy_setopt (slot->curl,CURLOPT_NOBODY ,0 ); +curl_easy_setopt (slot->curl,CURLOPT_POST ,1 ); +curl_easy_setopt (slot->curl,CURLOPT_URL , p->service_url); +curl_easy_setopt (slot->curl,CURLOPT_HTTPHEADER , p->headers); + +/* Setup function to read request from client */ +curl_easy_setopt (slot->curl,CURLOPT_READFUNCTION , proxy_in); +curl_easy_setopt (slot->curl,CURLOPT_READDATA , p); +
remote-curl.c static void parse_push(struct strbuf *buf) · line 1220
Quoted 10 lines
remote-curl.c +10
-0+/* Setup function to write server response to client */ +curl_easy_setopt (slot->curl,CURLOPT_WRITEFUNCTION , proxy_out); +curl_easy_setopt (slot->curl,CURLOPT_WRITEDATA , p); + + err =run_slot (slot,NULL ); + +if (err !=HTTP_OK ) + err =-1 ; + +return err;
HTTP_OK is 0 but kind of obscures that. How about something like the following?
if (run_slot(slot, NULL)) return -1; return 0;
or
if (run_slot(slot, NULL) != HTTP_OK) return -1; return 0;
That way it's clearer that this always returns 0 or -1.
[...]
Quoted 7 lines
remote-curl.c +7
-0+static int stateless_connect (const char *service_name) +{ +struct discovery *discover; +struct proxy_state p; + +/* +* Run the info/refs request and see if the server supports protocol
remote-curl.c static void parse_push(struct strbuf *buf) · line 1239
Quoted 10 lines
remote-curl.c +10
-0+* v2. If and only if the server supports v2 can we successfully +* establish a stateless connection, otherwise we need to tell the +* client to fallback to using other transport helper functions to +* complete their request. +*/ + discover =discover_refs (service_name,0 ); +if (discover->version != protocol_v2) { +printf ("fallback\n" ); +fflush (stdout); +return -1 ;
Interesting. I wonder if we can make remote-curl less smart and drive this more from the caller.
E.g. if the caller could do a single stateless request, they could do:
option git-protocol version=2 stateless-request GET info/refs?service=git-upload-pack [pkt-lines, ending with a flush-pkt]
The git-protocol option in this hypothetical example is the value to be passed in the Git-Protocol header.
Then based on the response, the caller could decide to keep using stateless-request for further requests or fall back to "fetch".
That way, if we implement some protocol v3, the remote helper would not have to be changed at all to handle it: the caller would instead make the new v3-format request and remote-curl would be able to oblige without knowing why they're doing it.
What do you think?
Thanks,
Jonathan
Re: [PATCH v3 34/35] remote-curl: implement stateless-connect command
Quoted 10 lines · Jonathan Nieder
Hi,
Quoted 5 lines · Brandon Williams
Teach remote-curl the 'stateless-connect' command which is used to establish a stateless connection with servers which support protocol version 2. This allows remote-curl to act as a proxy, allowing the git client to communicate natively with a remote end, simply using remote-curl as a pass through to convert requests to http.
Quoted 6 lines
Cool! I better look at the spec for that first.
*looks at the previous patch*
Oh, there is no documented spec. :/ I'll muddle through this instead, then.
I'll make sure to add one :)
Quoted 17 lines
[...]
remote-curl.c static struct ref *parse_git_refs(struct discovery *heads, int for_push) · line 195
Quoted 12 lines
remote-curl.c +4
-1@@ -188,7 +188,10 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push) heads->version =discover_version (&reader);switch (heads->version) {case protocol_v2: -die ("support for protocol v2 not implemented yet" ); +/* +* Do nothing. Client should run 'stateless-connect' and +* request the refs themselves. +*/ break ;This is the 'list' command, right? Since we expect the client to run 'stateless-connect' instead, can we make it error out?
Yes and no. Remote-curl will run this when trying to establish a stateless-connection. If the response is v2 then this is a capability list and not refs. So the capabilities will be dumped to the client and they will be able to request the refs themselves at a later point. The comment here is just misleading, so i'll make sure to fix it.
Quoted 21 lines
[...]
Quoted 5 lines
remote-curl.c +1
-0@@ -1082,6 +1085,184 @@ static void parse_push(struct strbuf *buf)free (specs); } +struct proxy_state {remote-curl.c static void parse_push(struct strbuf *buf) · line 1098
Quoted 10 lines
remote-curl.c +10
-0+char *service_name; +char *service_url; +struct curl_slist *headers; +struct strbuf request_buffer; +int in; +int out; +struct packet_reader reader; +size_t pos; +int seen_flush; +};Can this have a comment describing what it is/does? It's not obvious to me at first glance.
Quoted 4 lines
It doesn't have to go in a lot of detail since this is an internal implementation detail, but something saying e.g. that this represents a connection to an HTTP server (that's an example; I'm not saying that's what it represents :)) would help.
Always making new code have higher standards than the existing code ;) Haha, I'll add a simple comment explaining it.
Quoted 9 lines
Quoted 4 lines
remote-curl.c +3
-0+ +static void proxy_state_init (struct proxy_state *p,const char *service_name, +enum protocol_version version)[...]
Quoted 1 line
+static void
proxy_state_clear(struct proxy_state *p)Looks sensible.
Quoted 7 lines
[...]
remote-curl.c
Quoted 2 lines
remote-curl.c +2
-0+static size_t proxy_in (char *buffer,size_t eltsize, +size_t nmemb,void *userdata)Can this have a comment describing the interface? (E.g. does it read a single
pkt_line? How is the caller expected to use it? Does it satisfy the interface of some callback?)
I'll add a comment that its used as a READFUNCTION callback for curl and that it tries to copy over a packet-line at a time.
Quoted 7 lines
libcurl's example https://curl.haxx.se/libcurl/c/ftpupload.html just calls this
read_callback. Such a name plus a pointer toCURLOPT_READFUNCTIONshould do the trick; bonus points if the comment says what our implementation of the callback does.Is this about having peek ability?
No its just that Curl only requests a set about of data at a time so you need to be able to buffer the data that can't be read yet.
Quoted 56 lines
Quoted 25 lines
remote-curl.c +24
-0+struct proxy_state *p = userdata; +size_t avail = p->request_buffer.len - p->pos; + +if (!avail) { +if (p->seen_flush) { + p->seen_flush =0 ; +return 0 ; + } + +strbuf_reset (&p->request_buffer); +switch (packet_reader_read (&p->reader)) { +case PACKET_READ_EOF : +die ("unexpected EOF when reading from parent process" ); +case PACKET_READ_NORMAL : +packet_buf_write_len (&p->request_buffer, p->reader.line, + p->reader.pktlen); +break ; +case PACKET_READ_DELIM : +packet_buf_delim (&p->request_buffer); +break ; +case PACKET_READ_FLUSH : +packet_buf_flush (&p->request_buffer); + p->seen_flush =1 ; +break ;remote-curl.c static void parse_push(struct strbuf *buf) · line 1182
Quoted 10 lines
remote-curl.c +10
-0+ } + p->pos =0 ; + avail = p->request_buffer.len; + } + +if (max < avail) + avail = max; +memcpy (buffer, p->request_buffer.buf + p->pos, avail); + p->pos += avail; +return avail;This is a number of bytes, but
CURLOPT_READFUNCTIONexpects a number of items, fread-style. That is:if (avail < eltsize) ... handle somehow, maybe by reading in more? ... avail_memb = avail / eltsize; memcpy(buffer, p->request_buffer.buf + p->pos, st_mult(avail_memb, eltsize)); p->pos += st_mult(avail_memb, eltsize); return avail_memb;But https://curl.haxx.se/libcurl/c/CURLOPT_READFUNCTION.html says
Your function must then return the actual number of bytes that it stored in that memory area.
Does this mean eltsize is always 1? This is super confusing...
Quoted 7 lines
... ok, a quick grep for
fread_funcin libcurl reveals that eltsize is indeed always 1. Can we add an assertion so we notice if that changes?if (eltsize != 1) BUG("curl read callback called with size = %zu != 1", eltsize); max = nmemb;
Yeah i can go ahead and do this. Just note that the v1 path uses logic identical to this so it would be a problem there.
Quoted 15 lines
[...]
remote-curl.c static void parse_push(struct strbuf *buf) · line 1193
Quoted 9 lines
remote-curl.c +9
-0+static size_t proxy_out (char *buffer,size_t eltsize, +size_t nmemb,void *userdata) +{ +size_t size = eltsize * nmemb; +struct proxy_state *p = userdata; + +write_or_die (p->out, buffer, size); +return size; +}Nice. Same questions about
st_multor just asserting on eltsize apply here, too.
Quoted 4 lines
[...]
Quoted 1 line
+static int
proxy_post(struct proxy_state *p)What does this function do? Can it get a brief comment?
Will do.
Quoted 34 lines
Quoted 16 lines
remote-curl.c +15
-0+{ +struct active_request_slot *slot; +int err; + + slot =get_active_slot (); + +curl_easy_setopt (slot->curl,CURLOPT_NOBODY ,0 ); +curl_easy_setopt (slot->curl,CURLOPT_POST ,1 ); +curl_easy_setopt (slot->curl,CURLOPT_URL , p->service_url); +curl_easy_setopt (slot->curl,CURLOPT_HTTPHEADER , p->headers); + +/* Setup function to read request from client */ +curl_easy_setopt (slot->curl,CURLOPT_READFUNCTION , proxy_in); +curl_easy_setopt (slot->curl,CURLOPT_READDATA , p); +remote-curl.c static void parse_push(struct strbuf *buf) · line 1220
Quoted 10 lines
remote-curl.c +10
-0+/* Setup function to write server response to client */ +curl_easy_setopt (slot->curl,CURLOPT_WRITEFUNCTION , proxy_out); +curl_easy_setopt (slot->curl,CURLOPT_WRITEDATA , p); + + err =run_slot (slot,NULL ); + +if (err !=HTTP_OK ) + err =-1 ; + +return err;
HTTP_OKis 0 but kind of obscures that. How about something like the following?if (run_slot(slot, NULL)) return -1; return 0;
Quoted 7 lines
or
if (run_slot(slot, NULL) != HTTP_OK) return -1; return 0;That way it's clearer that this always returns 0 or -1.
Sounds good.
Quoted 35 lines
[...]
Quoted 7 lines
remote-curl.c +7
-0+static int stateless_connect (const char *service_name) +{ +struct discovery *discover; +struct proxy_state p; + +/* +* Run the info/refs request and see if the server supports protocol remote-curl.c static void parse_push(struct strbuf *buf) · line 1239
Quoted 10 lines
remote-curl.c +10
-0+* v2. If and only if the server supports v2 can we successfully +* establish a stateless connection, otherwise we need to tell the +* client to fallback to using other transport helper functions to +* complete their request. +*/ + discover =discover_refs (service_name,0 ); +if (discover->version != protocol_v2) { +printf ("fallback\n" ); +fflush (stdout); +return -1 ;Interesting. I wonder if we can make remote-curl less smart and drive this more from the caller.
E.g. if the caller could do a single stateless request, they could do:
option git-protocol version=2 stateless-request GET info/refs?service=git-upload-pack [pkt-lines, ending with a flush-pkt]
The git-protocol option in this hypothetical example is the value to be passed in the Git-Protocol header.
Then based on the response, the caller could decide to keep using stateless-request for further requests or fall back to "fetch".
Quoted 6 lines
That way, if we implement some protocol v3, the remote helper would not have to be changed at all to handle it: the caller would instead make the new v3-format request and remote-curl would be able to oblige without knowing why they're doing it.
What do you think?
I do see the draw for wanting this. I think a change like this requires a lot more refactoring, simply because with the current setup the fetch/ls-refs logic doesn't care that its talking through a remote-helper where if we went down that route it would need to be aware of that.
Signature · 1 line
Brandon Williams
PATCH v3 35/35 remote-curl: don't request v2 when pushing
In reply to Brandon WilliamsIn order to be able to ship protocol v2 with only supporting fetch, we need clients to not issue a request to use protocol v2 when pushing (since the client currently doesn't know how to push using protocol v2). This allows a client to have protocol v2 configured in protocol.version and take advantage of using v2 for fetch and falling back to using v0 when pushing while v2 for push is being designed.
We could run into issues if we didn't fall back to protocol v2 when pushing right now. This is because currently a server will ignore a request to use v2 when contacting the 'receive-pack' endpoint and fall back to using v0, but when push v2 is rolled out to servers, the 'receive-pack' endpoint will start responding using v2. So we don't want to get into a state where a client is requesting to push with v2 before they actually know how to push using v2.
--- remote-curl.c | 11 ++++++++++- t/t5702-protocol-v2.sh | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-)
remote-curl.c +10 -1
@@ -320,6 +320,7 @@ static struct discovery *discover_refs(const char *service, int for_push)struct discovery *last = last_discovery;int http_ret, maybe_smart =0 ;struct http_get_options http_options; +enum protocol_version version =get_protocol_version_config ();if (last && !strcmp (service, last->service))return last;
@@ -336,8 +337,16 @@ static struct discovery *discover_refs(const char *service, int for_push)strbuf_addf (&refs_url,"service=%s" , service); } +/* +* NEEDSWORK: If we are trying to use protocol v2 and we are planning +* to perform a push, then fallback to v0 since the client doesn't know +* how to push yet using v2. +*/ +if (version == protocol_v2 && !strcmp ("git-receive-pack" , service)) + version = protocol_v0; +/* Add the extra Git-Protocol header */ -if (get_protocol_http_header (get_protocol_version_config (), &protocol_header)) +if (get_protocol_http_header (version, &protocol_header))string_list_append (&extra_headers, protocol_header.buf);memset (&http_options,0 ,sizeof (http_options));
t/t5702-protocol-v2.sh +23 -0
@@ -211,6 +211,29 @@ test_expect_success 'fetch with http:// using protocol v2' ' grep "git< version 2" log ' +test_expect_success 'push with http:// and a config of v2 does not request v2' ' + # Till v2 for push is designed, make sure that if a client has + # protocol.version configured to use v2, that the client instead falls + # back and uses v0. + + test_commit -C http_child three && + + # Push to another branch, as the target repository has the + # master branch checked out and we cannot push into it. + GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \ + push origin HEAD:client_branch && 2>log && + + git -C http_child log -1 --format=%s >actual && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s client_branch >expect && + test_cmp expect actual && + + # Client didnt request to use protocol v2 + ! grep "Git-Protocol: version=2" log && + # Server didnt respond using protocol v2 + ! grep "git< version 2" log +' + + stop_httpd test_done
Signature · 1 line
2.16.0.rc1.238.g530d649a79-goog
Re: [PATCH v3 35/35] remote-curl: don't request v2 when pushing
t/t5702-protocol-v2.sh test_expect_success 'fetch with http:// using protocol v2' ' · line 224
Quoted 11 lines · On Tue, 6 Feb 2018 17:13:12 -0800 Brandon Williams <[email protected]> wrote:
t/t5702-protocol-v2.sh +11
-0+test_expect_success 'push with http:// and a config of v2 does not request v2' ' + # Till v2 for push is designed, make sure that if a client has + # protocol.version configured to use v2, that the client instead falls + # back and uses v0. + + test_commit -C http_child three && + + # Push to another branch, as the target repository has the + # master branch checked out and we cannot push into it. + GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \ + push origin HEAD:client_branch && 2>log &&
Should it be protocol.version=2? Also, two double ampersands?
Also, optionally, it might be better to do GIT_TRACE_PACKET="$(pwd)/log", so that it does not get mixed with other stderr output.