@matheusbsbs / matheus-bagliano / commits / fa8c097cc9
Fork 0

Support remote helpers implementing smart transports

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ilari Liusvaara committed Dec 9, 2009 at 17:26 UTC fa8c097cc9b416283ae4c1c1507909ff207b7f60
2 files changed +142 -7
Documentation/git-remote-helpers.txt
+24 -1
@@ -93,6 +93,20 @@ Supported if the helper has the "push" capability.
93 +
94 Supported if the helper has the "import" capability.
95
96 +'connect' <service>::
97 + Connects to given service. Standard input and standard output
98 + of helper are connected to specified service (git prefix is
99 + included in service name so e.g. fetching uses 'git-upload-pack'
100 + as service) on remote side. Valid replies to this command are
101 + empty line (connection established), 'fallback' (no smart
102 + transport support, fall back to dumb transports) and just
103 + exiting with error message printed (can't connect, don't
104 + bother trying to fall back). After line feed terminating the
105 + positive (empty) response, the output of service starts. After
106 + the connection ends, the remote helper exits.
107 ++
108 +Supported if the helper has the "connect" capability.
109 +
110 If a fatal error occurs, the program writes the error message to
111 stderr and exits. The caller should expect that a suitable error
112 message has been printed if the child closes the connection without
@@ -126,6 +140,9 @@ CAPABILITIES
140 all, it must cover all refs reported by the list command; if
141 it is not used, it is effectively "*:*"
142
143 +'connect'::
144 + This helper supports the 'connect' command.
145 +
146 REF LIST ATTRIBUTES
147 -------------------
148
@@ -168,9 +185,15 @@ OPTIONS
185 but don't actually change any repository data. For most
186 helpers this only applies to the 'push', if supported.
187
188 +'option servpath <c-style-quoted-path>'::
189 + Set service path (--upload-pack, --receive-pack etc.) for
190 + next connect. Remote helper MAY support this option. Remote
191 + helper MUST NOT rely on this option being set before
192 + connect request occurs.
193 +
194 Documentation
195 -------------
173 -Documentation by Daniel Barkalow.
196 +Documentation by Daniel Barkalow and Ilari Liusvaara
197
198 GIT
199 ---
transport-helper.c
+118 -6
@@ -18,7 +18,9 @@ struct helper_data
18 unsigned fetch : 1,
19 import : 1,
20 option : 1,
21 - push : 1;
21 + push : 1,
22 + connect : 1,
23 + no_disconnect_req : 1;
24 /* These go from remote name (as in "list") to private name */
25 struct refspec *refspecs;
26 int refspec_nr;
@@ -37,12 +39,12 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
39 die_errno("Full write to remote helper failed");
40 }
41
40 -static int recvline(struct helper_data *helper, struct strbuf *buffer)
42 +static int recvline_fh(FILE *helper, struct strbuf *buffer)
43 {
44 strbuf_reset(buffer);
45 if (debug)
46 fprintf(stderr, "Debug: Remote helper: Waiting...\n");
45 - if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
47 + if (strbuf_getline(buffer, helper, '\n') == EOF) {
48 if (debug)
49 fprintf(stderr, "Debug: Remote helper quit.\n");
50 exit(128);
@@ -53,6 +55,11 @@ static int recvline(struct helper_data *helper, struct strbuf *buffer)
55 return 0;
56 }
57
58 +static int recvline(struct helper_data *helper, struct strbuf *buffer)
59 +{
60 + return recvline_fh(helper->out, buffer);
61 +}
62 +
63 static void xchgline(struct helper_data *helper, struct strbuf *buffer)
64 {
65 sendline(helper, buffer);
@@ -77,6 +84,15 @@ const char *remove_ext_force(const char *url)
84 return url;
85 }
86
87 +static void do_take_over(struct transport *transport)
88 +{
89 + struct helper_data *data;
90 + data = (struct helper_data *)transport->data;
91 + transport_take_over(transport, data->helper);
92 + fclose(data->out);
93 + free(data);
94 +}
95 +
96 static struct child_process *get_helper(struct transport *transport)
97 {
98 struct helper_data *data = transport->data;
@@ -103,12 +119,12 @@ static struct child_process *get_helper(struct transport *transport)
119 if (start_command(helper))
120 die("Unable to run helper: git %s", helper->argv[0]);
121 data->helper = helper;
122 + data->no_disconnect_req = 0;
123
124 /*
125 * Open the output as FILE* so strbuf_getline() can be used.
126 * Do this with duped fd because fclose() will close the fd,
127 * and stuff like taking over will require the fd to remain.
111 - *
128 */
129 duped = dup(helper->out);
130 if (duped < 0)
@@ -146,6 +162,8 @@ static struct child_process *get_helper(struct transport *transport)
162 refspec_nr + 1,
163 refspec_alloc);
164 refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
165 + } else if (!strcmp(capname, "connect")) {
166 + data->connect = 1;
167 } else if (mandatory) {
168 die("Unknown madatory capability %s. This remote "
169 "helper probably needs newer version of Git.\n",
@@ -175,8 +193,10 @@ static int disconnect_helper(struct transport *transport)
193 if (data->helper) {
194 if (debug)
195 fprintf(stderr, "Debug: Disconnecting.\n");
178 - strbuf_addf(&buf, "\n");
179 - sendline(data, &buf);
196 + if (!data->no_disconnect_req) {
197 + strbuf_addf(&buf, "\n");
198 + sendline(data, &buf);
199 + }
200 close(data->helper->in);
201 close(data->helper->out);
202 fclose(data->out);
@@ -370,12 +390,94 @@ static int fetch_with_import(struct transport *transport,
390 return 0;
391 }
392
393 +static int process_connect_service(struct transport *transport,
394 + const char *name, const char *exec)
395 +{
396 + struct helper_data *data = transport->data;
397 + struct strbuf cmdbuf = STRBUF_INIT;
398 + struct child_process *helper;
399 + int r, duped, ret = 0;
400 + FILE *input;
401 +
402 + helper = get_helper(transport);
403 +
404 + /*
405 + * Yes, dup the pipe another time, as we need unbuffered version
406 + * of input pipe as FILE*. fclose() closes the underlying fd and
407 + * stream buffering only can be changed before first I/O operation
408 + * on it.
409 + */
410 + duped = dup(helper->out);
411 + if (duped < 0)
412 + die_errno("Can't dup helper output fd");
413 + input = xfdopen(duped, "r");
414 + setvbuf(input, NULL, _IONBF, 0);
415 +
416 + /*
417 + * Handle --upload-pack and friends. This is fire and forget...
418 + * just warn if it fails.
419 + */
420 + if (strcmp(name, exec)) {
421 + r = set_helper_option(transport, "servpath", exec);
422 + if (r > 0)
423 + warning("Setting remote service path not supported by protocol.");
424 + else if (r < 0)
425 + warning("Invalid remote service path.");
426 + }
427 +
428 + if (data->connect)
429 + strbuf_addf(&cmdbuf, "connect %s\n", name);
430 + else
431 + goto exit;
432 +
433 + sendline(data, &cmdbuf);
434 + recvline_fh(input, &cmdbuf);
435 + if (!strcmp(cmdbuf.buf, "")) {
436 + data->no_disconnect_req = 1;
437 + if (debug)
438 + fprintf(stderr, "Debug: Smart transport connection "
439 + "ready.\n");
440 + ret = 1;
441 + } else if (!strcmp(cmdbuf.buf, "fallback")) {
442 + if (debug)
443 + fprintf(stderr, "Debug: Falling back to dumb "
444 + "transport.\n");
445 + } else
446 + die("Unknown response to connect: %s",
447 + cmdbuf.buf);
448 +
449 +exit:
450 + fclose(input);
451 + return ret;
452 +}
453 +
454 +static int process_connect(struct transport *transport,
455 + int for_push)
456 +{
457 + struct helper_data *data = transport->data;
458 + const char *name;
459 + const char *exec;
460 +
461 + name = for_push ? "git-receive-pack" : "git-upload-pack";
462 + if (for_push)
463 + exec = data->transport_options.receivepack;
464 + else
465 + exec = data->transport_options.uploadpack;
466 +
467 + return process_connect_service(transport, name, exec);
468 +}
469 +
470 static int fetch(struct transport *transport,
471 int nr_heads, struct ref **to_fetch)
472 {
473 struct helper_data *data = transport->data;
474 int i, count;
475
476 + if (process_connect(transport, 0)) {
477 + do_take_over(transport);
478 + return transport->fetch(transport, nr_heads, to_fetch);
479 + }
480 +
481 count = 0;
482 for (i = 0; i < nr_heads; i++)
483 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -403,6 +505,11 @@ static int push_refs(struct transport *transport,
505 struct child_process *helper;
506 struct ref *ref;
507
508 + if (process_connect(transport, 1)) {
509 + do_take_over(transport);
510 + return transport->push_refs(transport, remote_refs, flags);
511 + }
512 +
513 if (!remote_refs)
514 return 0;
515
@@ -543,6 +650,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
650
651 helper = get_helper(transport);
652
653 + if (process_connect(transport, for_push)) {
654 + do_take_over(transport);
655 + return transport->get_refs_list(transport, for_push);
656 + }
657 +
658 if (data->push && for_push)
659 write_str_in_full(helper->in, "list for-push\n");
660 else