connect: request remote refs using v2
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@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Brandon Williams committed
Mar 15, 2018 at 10:31 UTC
e52449b672210f4b49e116ca34dcd46657287f61
6 files changed
+204
-11
builtin/upload-pack.c
+5
-5
@@ -5,6 +5,7 @@
5
#include "parse-options.h"
6
#include "protocol.h"
7
#include "upload-pack.h"
8
+#include "serve.h"
9
10
static const char * const upload_pack_usage[] = {
11
N_("git upload-pack [<options>] <dir>"),
@@ -16,6 +17,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
17
const char *dir;
18
int strict = 0;
19
struct upload_pack_options opts = { 0 };
20
+ struct serve_options serve_opts = SERVE_OPTIONS_INIT;
21
struct option options[] = {
22
OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
23
N_("quit after a single request/response exchange")),
@@ -48,11 +50,9 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
50
51
switch (determine_protocol_version_server()) {
52
case protocol_v2:
51
- /*
52
- * fetch support for protocol v2 has not been implemented yet,
53
- * so ignore the request to use v2 and fallback to using v0.
54
- */
55
- upload_pack(&opts);
53
+ serve_opts.advertise_capabilities = opts.advertise_refs;
54
+ serve_opts.stateless_rpc = opts.stateless_rpc;
55
+ serve(&serve_opts);
56
break;
57
case protocol_v1:
58
/*
connect.c
+133
-5
@@ -12,9 +12,11 @@
12
#include "sha1-array.h"
13
#include "transport.h"
14
#include "strbuf.h"
15
+#include "version.h"
16
#include "protocol.h"
17
17
-static char *server_capabilities;
18
+static char *server_capabilities_v1;
19
+static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT;
20
static const char *parse_feature_value(const char *, const char *, int *);
21
22
static int check_ref(const char *name, unsigned int flags)
@@ -62,6 +64,33 @@ static void die_initial_contact(int unexpected)
64
"and the repository exists."));
65
}
66
67
+/* Checks if the server supports the capability 'c' */
68
+int server_supports_v2(const char *c, int die_on_error)
69
+{
70
+ int i;
71
+
72
+ for (i = 0; i < server_capabilities_v2.argc; i++) {
73
+ const char *out;
74
+ if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
75
+ (!*out || *out == '='))
76
+ return 1;
77
+ }
78
+
79
+ if (die_on_error)
80
+ die("server doesn't support '%s'", c);
81
+
82
+ return 0;
83
+}
84
+
85
+static void process_capabilities_v2(struct packet_reader *reader)
86
+{
87
+ while (packet_reader_read(reader) == PACKET_READ_NORMAL)
88
+ argv_array_push(&server_capabilities_v2, reader->line);
89
+
90
+ if (reader->status != PACKET_READ_FLUSH)
91
+ die("expected flush after capabilities");
92
+}
93
+
94
enum protocol_version discover_version(struct packet_reader *reader)
95
{
96
enum protocol_version version = protocol_unknown_version;
@@ -84,7 +113,7 @@ enum protocol_version discover_version(struct packet_reader *reader)
113
114
switch (version) {
115
case protocol_v2:
87
- die("support for protocol v2 not implemented yet");
116
+ process_capabilities_v2(reader);
117
break;
118
case protocol_v1:
119
/* Read the peeked version line */
@@ -128,7 +157,7 @@ reject:
157
static void annotate_refs_with_symref_info(struct ref *ref)
158
{
159
struct string_list symref = STRING_LIST_INIT_DUP;
131
- const char *feature_list = server_capabilities;
160
+ const char *feature_list = server_capabilities_v1;
161
162
while (feature_list) {
163
int len;
@@ -157,7 +186,7 @@ static void process_capabilities(const char *line, int *len)
186
int nul_location = strlen(line);
187
if (nul_location == *len)
188
return;
160
- server_capabilities = xstrdup(line + nul_location + 1);
189
+ server_capabilities_v1 = xstrdup(line + nul_location + 1);
190
*len = nul_location;
191
}
192
@@ -292,6 +321,105 @@ struct ref **get_remote_heads(struct packet_reader *reader,
321
return list;
322
}
323
324
+/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
325
+static int process_ref_v2(const char *line, struct ref ***list)
326
+{
327
+ int ret = 1;
328
+ int i = 0;
329
+ struct object_id old_oid;
330
+ struct ref *ref;
331
+ struct string_list line_sections = STRING_LIST_INIT_DUP;
332
+ const char *end;
333
+
334
+ /*
335
+ * Ref lines have a number of fields which are space deliminated. The
336
+ * first field is the OID of the ref. The second field is the ref
337
+ * name. Subsequent fields (symref-target and peeled) are optional and
338
+ * don't have a particular order.
339
+ */
340
+ if (string_list_split(&line_sections, line, ' ', -1) < 2) {
341
+ ret = 0;
342
+ goto out;
343
+ }
344
+
345
+ if (parse_oid_hex(line_sections.items[i++].string, &old_oid, &end) ||
346
+ *end) {
347
+ ret = 0;
348
+ goto out;
349
+ }
350
+
351
+ ref = alloc_ref(line_sections.items[i++].string);
352
+
353
+ oidcpy(&ref->old_oid, &old_oid);
354
+ **list = ref;
355
+ *list = &ref->next;
356
+
357
+ for (; i < line_sections.nr; i++) {
358
+ const char *arg = line_sections.items[i].string;
359
+ if (skip_prefix(arg, "symref-target:", &arg))
360
+ ref->symref = xstrdup(arg);
361
+
362
+ if (skip_prefix(arg, "peeled:", &arg)) {
363
+ struct object_id peeled_oid;
364
+ char *peeled_name;
365
+ struct ref *peeled;
366
+ if (parse_oid_hex(arg, &peeled_oid, &end) || *end) {
367
+ ret = 0;
368
+ goto out;
369
+ }
370
+
371
+ peeled_name = xstrfmt("%s^{}", ref->name);
372
+ peeled = alloc_ref(peeled_name);
373
+
374
+ oidcpy(&peeled->old_oid, &peeled_oid);
375
+ **list = peeled;
376
+ *list = &peeled->next;
377
+
378
+ free(peeled_name);
379
+ }
380
+ }
381
+
382
+out:
383
+ string_list_clear(&line_sections, 0);
384
+ return ret;
385
+}
386
+
387
+struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
388
+ struct ref **list, int for_push,
389
+ const struct argv_array *ref_prefixes)
390
+{
391
+ int i;
392
+ *list = NULL;
393
+
394
+ if (server_supports_v2("ls-refs", 1))
395
+ packet_write_fmt(fd_out, "command=ls-refs\n");
396
+
397
+ if (server_supports_v2("agent", 0))
398
+ packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
399
+
400
+ packet_delim(fd_out);
401
+ /* When pushing we don't want to request the peeled tags */
402
+ if (!for_push)
403
+ packet_write_fmt(fd_out, "peel\n");
404
+ packet_write_fmt(fd_out, "symrefs\n");
405
+ for (i = 0; ref_prefixes && i < ref_prefixes->argc; i++) {
406
+ packet_write_fmt(fd_out, "ref-prefix %s\n",
407
+ ref_prefixes->argv[i]);
408
+ }
409
+ packet_flush(fd_out);
410
+
411
+ /* Process response from server */
412
+ while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
413
+ if (!process_ref_v2(reader->line, &list))
414
+ die("invalid ls-refs response: %s", reader->line);
415
+ }
416
+
417
+ if (reader->status != PACKET_READ_FLUSH)
418
+ die("expected flush after ref listing");
419
+
420
+ return list;
421
+}
422
+
423
static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
424
{
425
int len;
@@ -336,7 +464,7 @@ int parse_feature_request(const char *feature_list, const char *feature)
464
465
const char *server_feature_value(const char *feature, int *len)
466
{
339
- return parse_feature_value(server_capabilities, feature, len);
467
+ return parse_feature_value(server_capabilities_v1, feature, len);
468
}
469
470
int server_supports(const char *feature)
connect.h
+2
@@ -16,4 +16,6 @@ extern int url_is_local_not_ssh(const char *url);
16
struct packet_reader;
17
extern enum protocol_version discover_version(struct packet_reader *reader);
18
19
+extern int server_supports_v2(const char *c, int die_on_error);
20
+
21
#endif
remote.h
+6
@@ -151,11 +151,17 @@ void free_refs(struct ref *ref);
151
152
struct oid_array;
153
struct packet_reader;
154
+struct argv_array;
155
extern struct ref **get_remote_heads(struct packet_reader *reader,
156
struct ref **list, unsigned int flags,
157
struct oid_array *extra_have,
158
struct oid_array *shallow_points);
159
160
+/* Used for protocol v2 in order to retrieve refs from a remote */
161
+extern struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
162
+ struct ref **list, int for_push,
163
+ const struct argv_array *ref_prefixes);
164
+
165
int resolve_remote_symref(struct ref *ref, struct ref *list);
166
int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
167
t/t5702-protocol-v2.sh
new
+57
@@ -0,0 +1,57 @@
1
+#!/bin/sh
2
+
3
+test_description='test git wire-protocol version 2'
4
+
5
+TEST_NO_CREATE_REPO=1
6
+
7
+. ./test-lib.sh
8
+
9
+# Test protocol v2 with 'git://' transport
10
+#
11
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
12
+start_git_daemon --export-all --enable=receive-pack
13
+daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
14
+
15
+test_expect_success 'create repo to be served by git-daemon' '
16
+ git init "$daemon_parent" &&
17
+ test_commit -C "$daemon_parent" one
18
+'
19
+
20
+test_expect_success 'list refs with git:// using protocol v2' '
21
+ test_when_finished "rm -f log" &&
22
+
23
+ GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
24
+ ls-remote --symref "$GIT_DAEMON_URL/parent" >actual &&
25
+
26
+ # Client requested to use protocol v2
27
+ grep "git> .*\\\0\\\0version=2\\\0$" log &&
28
+ # Server responded using protocol v2
29
+ grep "git< version 2" log &&
30
+
31
+ git ls-remote --symref "$GIT_DAEMON_URL/parent" >expect &&
32
+ test_cmp actual expect
33
+'
34
+
35
+stop_git_daemon
36
+
37
+# Test protocol v2 with 'file://' transport
38
+#
39
+test_expect_success 'create repo to be served by file:// transport' '
40
+ git init file_parent &&
41
+ test_commit -C file_parent one
42
+'
43
+
44
+test_expect_success 'list refs with file:// using protocol v2' '
45
+ test_when_finished "rm -f log" &&
46
+
47
+ GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
48
+ ls-remote --symref "file://$(pwd)/file_parent" >actual &&
49
+
50
+ # Server responded using protocol v2
51
+ grep "git< version 2" log &&
52
+
53
+ git ls-remote --symref "file://$(pwd)/file_parent" >expect &&
54
+ test_cmp actual expect
55
+'
56
+
57
+test_done
transport.c
+1
-1
@@ -204,7 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
204
data->version = discover_version(&reader);
205
switch (data->version) {
206
case protocol_v2:
207
- die("support for protocol v2 not implemented yet");
207
+ get_remote_refs(data->fd[1], &reader, &refs, for_push, NULL);
208
break;
209
case protocol_v1:
210
case protocol_v0: