Skip to content

Commit 249b200

Browse files
spearcegitster
authored andcommitted
Smart fetch over HTTP: client side
The git-remote-curl backend detects if the remote server supports the git-upload-pack service, and if so, runs git-fetch-pack locally in a pipe to generate the want/have commands. The advertisements from the server that were obtained during the discovery are passed into git-fetch-pack before the POST request starts, permitting server capability discovery and enablement. Common objects that are discovered are appended onto the request as have lines and are sent again on the next request. This allows the remote side to reinitialize its in-memory list of common objects during the next request. Because all requests are relatively short, below git-remote-curl's 1 MiB buffer limit, requests will use the standard Content-Length header and be valid HTTP/1.0 POST requests. This makes the fetch client more tolerant of proxy servers which don't support HTTP/1.1 or the chunked transfer encoding. Signed-off-by: Shawn O. Pearce <[email protected]> CC: Daniel Barkalow <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent de1a2fd commit 249b200

File tree

3 files changed

+160
-22
lines changed

3 files changed

+160
-22
lines changed

builtin-fetch-pack.c

Lines changed: 93 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,24 @@ enum ack_type {
165165
ACK_ready
166166
};
167167

168+
static void consume_shallow_list(int fd)
169+
{
170+
if (args.stateless_rpc && args.depth > 0) {
171+
/* If we sent a depth we will get back "duplicate"
172+
* shallow and unshallow commands every time there
173+
* is a block of have lines exchanged.
174+
*/
175+
char line[1000];
176+
while (packet_read_line(fd, line, sizeof(line))) {
177+
if (!prefixcmp(line, "shallow "))
178+
continue;
179+
if (!prefixcmp(line, "unshallow "))
180+
continue;
181+
die("git fetch-pack: expected shallow list");
182+
}
183+
}
184+
}
185+
168186
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
169187
{
170188
static char line[1000];
@@ -190,6 +208,15 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
190208
die("git fetch_pack: expected ACK/NAK, got '%s'", line);
191209
}
192210

211+
static void send_request(int fd, struct strbuf *buf)
212+
{
213+
if (args.stateless_rpc) {
214+
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
215+
packet_flush(fd);
216+
} else
217+
safe_write(fd, buf->buf, buf->len);
218+
}
219+
193220
static int find_common(int fd[2], unsigned char *result_sha1,
194221
struct ref *refs)
195222
{
@@ -199,7 +226,10 @@ static int find_common(int fd[2], unsigned char *result_sha1,
199226
unsigned in_vain = 0;
200227
int got_continue = 0;
201228
struct strbuf req_buf = STRBUF_INIT;
229+
size_t state_len = 0;
202230

231+
if (args.stateless_rpc && multi_ack == 1)
232+
die("--stateless-rpc requires multi_ack_detailed");
203233
if (marked)
204234
for_each_ref(clear_marks, NULL);
205235
marked = 1;
@@ -256,13 +286,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
256286
if (args.depth > 0)
257287
packet_buf_write(&req_buf, "deepen %d", args.depth);
258288
packet_buf_flush(&req_buf);
259-
260-
safe_write(fd[1], req_buf.buf, req_buf.len);
289+
state_len = req_buf.len;
261290

262291
if (args.depth > 0) {
263292
char line[1024];
264293
unsigned char sha1[20];
265294

295+
send_request(fd[1], &req_buf);
266296
while (packet_read_line(fd[0], line, sizeof(line))) {
267297
if (!prefixcmp(line, "shallow ")) {
268298
if (get_sha1_hex(line + 8, sha1))
@@ -284,28 +314,40 @@ static int find_common(int fd[2], unsigned char *result_sha1,
284314
}
285315
die("expected shallow/unshallow, got %s", line);
286316
}
317+
} else if (!args.stateless_rpc)
318+
send_request(fd[1], &req_buf);
319+
320+
if (!args.stateless_rpc) {
321+
/* If we aren't using the stateless-rpc interface
322+
* we don't need to retain the headers.
323+
*/
324+
strbuf_setlen(&req_buf, 0);
325+
state_len = 0;
287326
}
288327

289328
flushes = 0;
290329
retval = -1;
291330
while ((sha1 = get_rev())) {
292-
packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
331+
packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
293332
if (args.verbose)
294333
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
295334
in_vain++;
296335
if (!(31 & ++count)) {
297336
int ack;
298337

299-
packet_flush(fd[1]);
338+
packet_buf_flush(&req_buf);
339+
send_request(fd[1], &req_buf);
340+
strbuf_setlen(&req_buf, state_len);
300341
flushes++;
301342

302343
/*
303344
* We keep one window "ahead" of the other side, and
304345
* will wait for an ACK only on the next one
305346
*/
306-
if (count == 32)
347+
if (!args.stateless_rpc && count == 32)
307348
continue;
308349

350+
consume_shallow_list(fd[0]);
309351
do {
310352
ack = get_ack(fd[0], result_sha1);
311353
if (args.verbose && ack)
@@ -322,6 +364,17 @@ static int find_common(int fd[2], unsigned char *result_sha1,
322364
case ACK_continue: {
323365
struct commit *commit =
324366
lookup_commit(result_sha1);
367+
if (args.stateless_rpc
368+
&& ack == ACK_common
369+
&& !(commit->object.flags & COMMON)) {
370+
/* We need to replay the have for this object
371+
* on the next RPC request so the peer knows
372+
* it is in common with us.
373+
*/
374+
const char *hex = sha1_to_hex(result_sha1);
375+
packet_buf_write(&req_buf, "have %s\n", hex);
376+
state_len = req_buf.len;
377+
}
325378
mark_common(commit, 0, 1);
326379
retval = 0;
327380
in_vain = 0;
@@ -339,7 +392,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
339392
}
340393
}
341394
done:
342-
packet_write(fd[1], "done\n");
395+
packet_buf_write(&req_buf, "done\n");
396+
send_request(fd[1], &req_buf);
343397
if (args.verbose)
344398
fprintf(stderr, "done\n");
345399
if (retval != 0) {
@@ -348,6 +402,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
348402
}
349403
strbuf_release(&req_buf);
350404

405+
consume_shallow_list(fd[0]);
351406
while (flushes || multi_ack) {
352407
int ack = get_ack(fd[0], result_sha1);
353408
if (ack) {
@@ -672,6 +727,8 @@ static struct ref *do_fetch_pack(int fd[2],
672727
*/
673728
warning("no common commits");
674729

730+
if (args.stateless_rpc)
731+
packet_flush(fd[1]);
675732
if (get_pack(fd, pack_lockfile))
676733
die("git fetch-pack: fetch failed.");
677734

@@ -742,6 +799,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
742799
struct ref *ref = NULL;
743800
char *dest = NULL, **heads;
744801
int fd[2];
802+
char *pack_lockfile = NULL;
803+
char **pack_lockfile_ptr = NULL;
745804
struct child_process *conn;
746805

747806
nr_heads = 0;
@@ -791,6 +850,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
791850
args.no_progress = 1;
792851
continue;
793852
}
853+
if (!strcmp("--stateless-rpc", arg)) {
854+
args.stateless_rpc = 1;
855+
continue;
856+
}
857+
if (!strcmp("--lock-pack", arg)) {
858+
args.lock_pack = 1;
859+
pack_lockfile_ptr = &pack_lockfile;
860+
continue;
861+
}
794862
usage(fetch_pack_usage);
795863
}
796864
dest = (char *)arg;
@@ -801,19 +869,27 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
801869
if (!dest)
802870
usage(fetch_pack_usage);
803871

804-
conn = git_connect(fd, (char *)dest, args.uploadpack,
805-
args.verbose ? CONNECT_VERBOSE : 0);
806-
if (conn) {
807-
get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
808-
809-
ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
810-
close(fd[0]);
811-
close(fd[1]);
812-
if (finish_connect(conn))
813-
ref = NULL;
872+
if (args.stateless_rpc) {
873+
conn = NULL;
874+
fd[0] = 0;
875+
fd[1] = 1;
814876
} else {
815-
ref = NULL;
877+
conn = git_connect(fd, (char *)dest, args.uploadpack,
878+
args.verbose ? CONNECT_VERBOSE : 0);
879+
}
880+
881+
get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
882+
883+
ref = fetch_pack(&args, fd, conn, ref, dest,
884+
nr_heads, heads, pack_lockfile_ptr);
885+
if (pack_lockfile) {
886+
printf("lock %s\n", pack_lockfile);
887+
fflush(stdout);
816888
}
889+
close(fd[0]);
890+
close(fd[1]);
891+
if (finish_connect(conn))
892+
ref = NULL;
817893
ret = !ref;
818894

819895
if (!ret && nr_heads) {

fetch-pack.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ struct fetch_pack_args
1313
fetch_all:1,
1414
verbose:1,
1515
no_progress:1,
16-
include_tag:1;
16+
include_tag:1,
17+
stateless_rpc:1;
1718
};
1819

1920
struct ref *fetch_pack(struct fetch_pack_args *args,

remote-curl.c

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ static int set_option(const char *name, const char *value)
4545
options.progress = 0;
4646
else
4747
return -1;
48-
return 1 /* TODO implement later */;
48+
return 0;
4949
}
5050
else if (!strcmp(name, "depth")) {
5151
char *end;
5252
unsigned long v = strtoul(value, &end, 10);
5353
if (value == end || *end)
5454
return -1;
5555
options.depth = v;
56-
return 1 /* TODO implement later */;
56+
return 0;
5757
}
5858
else if (!strcmp(name, "followtags")) {
5959
if (!strcmp(value, "true"))
@@ -62,7 +62,7 @@ static int set_option(const char *name, const char *value)
6262
options.followtags = 0;
6363
else
6464
return -1;
65-
return 1 /* TODO implement later */;
65+
return 0;
6666
}
6767
else if (!strcmp(name, "dry-run")) {
6868
if (!strcmp(value, "true"))
@@ -463,6 +463,8 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
463463
char **targets = xmalloc(nr_heads * sizeof(char*));
464464
int ret, i;
465465

466+
if (options.depth)
467+
die("dumb http transport does not support --depth");
466468
for (i = 0; i < nr_heads; i++)
467469
targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
468470

@@ -481,6 +483,65 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
481483
return ret ? error("Fetch failed.") : 0;
482484
}
483485

486+
static int fetch_git(struct discovery *heads,
487+
int nr_heads, struct ref **to_fetch)
488+
{
489+
struct rpc_state rpc;
490+
char *depth_arg = NULL;
491+
const char **argv;
492+
int argc = 0, i, err;
493+
494+
argv = xmalloc((15 + nr_heads) * sizeof(char*));
495+
argv[argc++] = "fetch-pack";
496+
argv[argc++] = "--stateless-rpc";
497+
argv[argc++] = "--lock-pack";
498+
if (options.followtags)
499+
argv[argc++] = "--include-tag";
500+
if (options.thin)
501+
argv[argc++] = "--thin";
502+
if (options.verbosity >= 3) {
503+
argv[argc++] = "-v";
504+
argv[argc++] = "-v";
505+
}
506+
if (!options.progress)
507+
argv[argc++] = "--no-progress";
508+
if (options.depth) {
509+
struct strbuf buf = STRBUF_INIT;
510+
strbuf_addf(&buf, "--depth=%lu", options.depth);
511+
depth_arg = strbuf_detach(&buf, NULL);
512+
argv[argc++] = depth_arg;
513+
}
514+
argv[argc++] = url;
515+
for (i = 0; i < nr_heads; i++) {
516+
struct ref *ref = to_fetch[i];
517+
if (!ref->name || !*ref->name)
518+
die("cannot fetch by sha1 over smart http");
519+
argv[argc++] = ref->name;
520+
}
521+
argv[argc++] = NULL;
522+
523+
memset(&rpc, 0, sizeof(rpc));
524+
rpc.service_name = "git-upload-pack",
525+
rpc.argv = argv;
526+
527+
err = rpc_service(&rpc, heads);
528+
if (rpc.result.len)
529+
safe_write(1, rpc.result.buf, rpc.result.len);
530+
strbuf_release(&rpc.result);
531+
free(argv);
532+
free(depth_arg);
533+
return err;
534+
}
535+
536+
static int fetch(int nr_heads, struct ref **to_fetch)
537+
{
538+
struct discovery *d = discover_refs("git-upload-pack");
539+
if (d->proto_git)
540+
return fetch_git(d, nr_heads, to_fetch);
541+
else
542+
return fetch_dumb(nr_heads, to_fetch);
543+
}
544+
484545
static void parse_fetch(struct strbuf *buf)
485546
{
486547
struct ref **to_fetch = NULL;
@@ -523,7 +584,7 @@ static void parse_fetch(struct strbuf *buf)
523584
break;
524585
} while (1);
525586

526-
if (fetch_dumb(nr_heads, to_fetch))
587+
if (fetch(nr_heads, to_fetch))
527588
exit(128); /* error already reported */
528589
free_refs(list_head);
529590
free(to_fetch);

0 commit comments

Comments
 (0)