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. Signed-off-by: Brandon Williams <bmwill@xxxxxxxxxx> --- connect.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++- remote.h | 2 ++ t/t5701-protocol-v2.sh | 28 +++++++++++++++++ transport.c | 2 +- upload-pack.c | 3 +- 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100755 t/t5701-protocol-v2.sh diff --git a/connect.c b/connect.c index 433f08649..b3c933fc1 100644 --- a/connect.c +++ b/connect.c @@ -15,6 +15,7 @@ #include "protocol.h" static char *server_capabilities; +static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT; static const char *parse_feature_value(const char *, const char *, int *); static int check_ref(const char *name, unsigned int flags) @@ -62,6 +63,30 @@ static void die_initial_contact(int unexpected) "and the repository exists.")); } +static int server_supports_v2(const char *c) +{ + 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 == '=')) + return 1; + } + + return 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"); +} + enum protocol_version discover_version(struct packet_reader *reader) { enum protocol_version version = protocol_unknown_version; @@ -85,7 +110,7 @@ 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"); + process_capabilities_v2(reader); break; case protocol_v1: /* Read the peeked version line */ @@ -292,6 +317,64 @@ struct ref **get_remote_heads(struct packet_reader *reader, return list; } +static int process_ref_v2(const char *line, struct ref ***list) +{ + struct object_id old_oid; + const char *end_of_name; + struct ref *ref; + + if (parse_oid_hex(line, &old_oid, &line)) + return 0; + if (*line != ' ') + return 0; + line++; + + end_of_name = strchr(line, ' '); + + if (!end_of_name) + ref = alloc_ref(line); + else { + struct strbuf name = STRBUF_INIT; + /* symref info */ + strbuf_add(&name, line, end_of_name - line); + ref = alloc_ref(name.buf); + ref->symref = xstrdup(end_of_name + 1); + + strbuf_release(&name); + } + + oidcpy(&ref->old_oid, &old_oid); + **list = ref; + *list = &ref->next; + + return 1; +} +struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, + struct ref **list, unsigned int flags) +{ + *list = NULL; + /* Check that the server supports the ls-refs command */ + if (!server_supports_v2("ls-refs")) + die("server doesn't support 'ls-refs' command"); + + /* Issue request for ls-refs */ + packet_write_fmt(fd_out, "command=ls-refs"); + packet_delim(fd_out); + packet_write_fmt(fd_out, "--symrefs"); + 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"); + + return list; +} + static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp) { int len; diff --git a/remote.h b/remote.h index af34e44a4..64d87bf5b 100644 --- a/remote.h +++ b/remote.h @@ -155,6 +155,8 @@ 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, unsigned int flags); int resolve_remote_symref(struct ref *ref, struct ref *list); int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid); diff --git a/t/t5701-protocol-v2.sh b/t/t5701-protocol-v2.sh new file mode 100755 index 000000000..4bf4d61ac --- /dev/null +++ b/t/t5701-protocol-v2.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +test_description='test git wire-protocol version 2' + +TEST_NO_CREATE_REPO=1 + +. ./test-lib.sh + +# Test protocol v2 with 'file://' transport +# +test_expect_success 'create repo to be served by file:// transport' ' + git init file_parent && + test_commit -C file_parent one +' + +test_expect_success 'list refs with file:// using protocol v2' ' + GIT_TRACE_PACKET=1 git -c protocol.version=2 \ + ls-remote --symref "file://$(pwd)/file_parent" >actual 2>log && + + # Server responded using protocol v2 + cat log && + grep "git< version 2" log && + + git ls-remote --symref "file://$(pwd)/file_parent" >expect && + test_cmp actual expect +' + +test_done diff --git a/transport.c b/transport.c index 8a3735cf5..ddd0d6174 100644 --- a/transport.c +++ b/transport.c @@ -202,7 +202,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 ? REF_NORMAL : 0); break; case protocol_v1: case protocol_v0: diff --git a/upload-pack.c b/upload-pack.c index d706175e4..3296831f8 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -19,6 +19,7 @@ #include "argv-array.h" #include "prio-queue.h" #include "protocol.h" +#include "serve.h" static const char * const upload_pack_usage[] = { N_("git upload-pack [<options>] <dir>"), @@ -1106,7 +1107,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) switch (determine_protocol_version_server()) { case protocol_v2: - die("support for protocol v2 not implemented yet"); + serve(); break; case protocol_v1: /* -- 2.15.1.424.g9478a66081-goog