Skip to content

Commit fa8c097

Browse files
Ilari Liusvaaragitster
authored andcommitted
Support remote helpers implementing smart transports
Signed-off-by: Ilari Liusvaara <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 61b075b commit fa8c097

File tree

2 files changed

+142
-7
lines changed

2 files changed

+142
-7
lines changed

Documentation/git-remote-helpers.txt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ Supported if the helper has the "push" capability.
9393
+
9494
Supported if the helper has the "import" capability.
9595

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+
96110
If a fatal error occurs, the program writes the error message to
97111
stderr and exits. The caller should expect that a suitable error
98112
message has been printed if the child closes the connection without
@@ -126,6 +140,9 @@ CAPABILITIES
126140
all, it must cover all refs reported by the list command; if
127141
it is not used, it is effectively "*:*"
128142

143+
'connect'::
144+
This helper supports the 'connect' command.
145+
129146
REF LIST ATTRIBUTES
130147
-------------------
131148

@@ -168,9 +185,15 @@ OPTIONS
168185
but don't actually change any repository data. For most
169186
helpers this only applies to the 'push', if supported.
170187

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+
171194
Documentation
172195
-------------
173-
Documentation by Daniel Barkalow.
196+
Documentation by Daniel Barkalow and Ilari Liusvaara
174197

175198
GIT
176199
---

transport-helper.c

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ struct helper_data
1818
unsigned fetch : 1,
1919
import : 1,
2020
option : 1,
21-
push : 1;
21+
push : 1,
22+
connect : 1,
23+
no_disconnect_req : 1;
2224
/* These go from remote name (as in "list") to private name */
2325
struct refspec *refspecs;
2426
int refspec_nr;
@@ -37,12 +39,12 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
3739
die_errno("Full write to remote helper failed");
3840
}
3941

40-
static int recvline(struct helper_data *helper, struct strbuf *buffer)
42+
static int recvline_fh(FILE *helper, struct strbuf *buffer)
4143
{
4244
strbuf_reset(buffer);
4345
if (debug)
4446
fprintf(stderr, "Debug: Remote helper: Waiting...\n");
45-
if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
47+
if (strbuf_getline(buffer, helper, '\n') == EOF) {
4648
if (debug)
4749
fprintf(stderr, "Debug: Remote helper quit.\n");
4850
exit(128);
@@ -53,6 +55,11 @@ static int recvline(struct helper_data *helper, struct strbuf *buffer)
5355
return 0;
5456
}
5557

58+
static int recvline(struct helper_data *helper, struct strbuf *buffer)
59+
{
60+
return recvline_fh(helper->out, buffer);
61+
}
62+
5663
static void xchgline(struct helper_data *helper, struct strbuf *buffer)
5764
{
5865
sendline(helper, buffer);
@@ -77,6 +84,15 @@ const char *remove_ext_force(const char *url)
7784
return url;
7885
}
7986

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+
8096
static struct child_process *get_helper(struct transport *transport)
8197
{
8298
struct helper_data *data = transport->data;
@@ -103,12 +119,12 @@ static struct child_process *get_helper(struct transport *transport)
103119
if (start_command(helper))
104120
die("Unable to run helper: git %s", helper->argv[0]);
105121
data->helper = helper;
122+
data->no_disconnect_req = 0;
106123

107124
/*
108125
* Open the output as FILE* so strbuf_getline() can be used.
109126
* Do this with duped fd because fclose() will close the fd,
110127
* and stuff like taking over will require the fd to remain.
111-
*
112128
*/
113129
duped = dup(helper->out);
114130
if (duped < 0)
@@ -146,6 +162,8 @@ static struct child_process *get_helper(struct transport *transport)
146162
refspec_nr + 1,
147163
refspec_alloc);
148164
refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
165+
} else if (!strcmp(capname, "connect")) {
166+
data->connect = 1;
149167
} else if (mandatory) {
150168
die("Unknown madatory capability %s. This remote "
151169
"helper probably needs newer version of Git.\n",
@@ -175,8 +193,10 @@ static int disconnect_helper(struct transport *transport)
175193
if (data->helper) {
176194
if (debug)
177195
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+
}
180200
close(data->helper->in);
181201
close(data->helper->out);
182202
fclose(data->out);
@@ -370,12 +390,94 @@ static int fetch_with_import(struct transport *transport,
370390
return 0;
371391
}
372392

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+
373470
static int fetch(struct transport *transport,
374471
int nr_heads, struct ref **to_fetch)
375472
{
376473
struct helper_data *data = transport->data;
377474
int i, count;
378475

476+
if (process_connect(transport, 0)) {
477+
do_take_over(transport);
478+
return transport->fetch(transport, nr_heads, to_fetch);
479+
}
480+
379481
count = 0;
380482
for (i = 0; i < nr_heads; i++)
381483
if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -403,6 +505,11 @@ static int push_refs(struct transport *transport,
403505
struct child_process *helper;
404506
struct ref *ref;
405507

508+
if (process_connect(transport, 1)) {
509+
do_take_over(transport);
510+
return transport->push_refs(transport, remote_refs, flags);
511+
}
512+
406513
if (!remote_refs)
407514
return 0;
408515

@@ -543,6 +650,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
543650

544651
helper = get_helper(transport);
545652

653+
if (process_connect(transport, for_push)) {
654+
do_take_over(transport);
655+
return transport->get_refs_list(transport, for_push);
656+
}
657+
546658
if (data->push && for_push)
547659
write_str_in_full(helper->in, "list for-push\n");
548660
else

0 commit comments

Comments
 (0)