remote-curl.c 21.2 KB
Newer Older
1 2 3 4 5
#include "cache.h"
#include "remote.h"
#include "strbuf.h"
#include "walker.h"
#include "http.h"
6
#include "exec_cmd.h"
7
#include "run-command.h"
8
#include "pkt-line.h"
9
#include "sideband.h"
10

11
static struct remote *remote;
12
static const char *url; /* always ends with a trailing slash */
13

14 15 16 17
struct options {
	int verbosity;
	unsigned long depth;
	unsigned progress : 1,
18
		followtags : 1,
19 20
		dry_run : 1,
		thin : 1;
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
};
static struct options options;

static int set_option(const char *name, const char *value)
{
	if (!strcmp(name, "verbosity")) {
		char *end;
		int v = strtol(value, &end, 10);
		if (value == end || *end)
			return -1;
		options.verbosity = v;
		return 0;
	}
	else if (!strcmp(name, "progress")) {
		if (!strcmp(value, "true"))
			options.progress = 1;
		else if (!strcmp(value, "false"))
			options.progress = 0;
		else
			return -1;
41
		return 0;
42 43 44 45 46 47 48
	}
	else if (!strcmp(name, "depth")) {
		char *end;
		unsigned long v = strtoul(value, &end, 10);
		if (value == end || *end)
			return -1;
		options.depth = v;
49
		return 0;
50 51 52 53 54 55 56 57
	}
	else if (!strcmp(name, "followtags")) {
		if (!strcmp(value, "true"))
			options.followtags = 1;
		else if (!strcmp(value, "false"))
			options.followtags = 0;
		else
			return -1;
58
		return 0;
59
	}
60 61 62 63 64 65 66 67 68
	else if (!strcmp(name, "dry-run")) {
		if (!strcmp(value, "true"))
			options.dry_run = 1;
		else if (!strcmp(value, "false"))
			options.dry_run = 0;
		else
			return -1;
		return 0;
	}
69 70 71 72 73
	else {
		return 1 /* unsupported */;
	}
}

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
struct discovery {
	const char *service;
	char *buf_alloc;
	char *buf;
	size_t len;
	unsigned proto_git : 1;
};
static struct discovery *last_discovery;

static void free_discovery(struct discovery *d)
{
	if (d) {
		if (d == last_discovery)
			last_discovery = NULL;
		free(d->buf_alloc);
		free(d);
	}
}

static struct discovery* discover_refs(const char *service)
94
{
95 96
	struct strbuf exp = STRBUF_INIT;
	struct strbuf type = STRBUF_INIT;
97
	struct strbuf buffer = STRBUF_INIT;
98
	struct discovery *last = last_discovery;
99
	char *refs_url;
J
Jeff King 已提交
100
	int http_ret, maybe_smart = 0;
101

102 103 104
	if (last && !strcmp(service, last->service))
		return last;
	free_discovery(last);
105

106
	strbuf_addf(&buffer, "%sinfo/refs", url);
107 108
	if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
	     git_env_bool("GIT_SMART_HTTP", 1)) {
J
Jeff King 已提交
109
		maybe_smart = 1;
110 111 112 113 114 115 116
		if (!strchr(url, '?'))
			strbuf_addch(&buffer, '?');
		else
			strbuf_addch(&buffer, '&');
		strbuf_addf(&buffer, "service=%s", service);
	}
	refs_url = strbuf_detach(&buffer, NULL);
117

118
	http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
119 120 121 122 123 124
	switch (http_ret) {
	case HTTP_OK:
		break;
	case HTTP_MISSING_TARGET:
		die("%s not found: did you run git update-server-info on the"
		    " server?", refs_url);
125 126
	case HTTP_NOAUTH:
		die("Authentication failed");
127 128 129 130 131
	default:
		http_error(refs_url, http_ret);
		die("HTTP request failed");
	}

132 133 134 135 136
	last= xcalloc(1, sizeof(*last_discovery));
	last->service = service;
	last->buf_alloc = strbuf_detach(&buffer, &last->len);
	last->buf = last->buf_alloc;

137 138 139 140
	strbuf_addf(&exp, "application/x-%s-advertisement", service);
	if (maybe_smart &&
	    (5 <= last->len && last->buf[4] == '#') &&
	    !strbuf_cmp(&exp, &type)) {
141 142
		char *line;

143 144
		/*
		 * smart HTTP response; validate that the service
145 146
		 * pkt-line matches our request.
		 */
147
		line = packet_read_line_buf(&last->buf, &last->len, NULL);
148

149
		strbuf_reset(&exp);
150
		strbuf_addf(&exp, "# service=%s", service);
151 152
		if (strcmp(line, exp.buf))
			die("invalid server response; got '%s'", line);
153 154 155 156 157 158
		strbuf_release(&exp);

		/* The header can include additional metadata lines, up
		 * until a packet flush marker.  Ignore these now, but
		 * in the future we might start to scan them.
		 */
159 160
		while (packet_read_line_buf(&last->buf, &last->len, NULL))
			;
161 162 163 164 165

		last->proto_git = 1;
	}

	free(refs_url);
166 167
	strbuf_release(&exp);
	strbuf_release(&type);
168 169 170 171 172
	strbuf_release(&buffer);
	last_discovery = last;
	return last;
}

J
Jeff King 已提交
173
static struct ref *parse_git_refs(struct discovery *heads, int for_push)
174 175
{
	struct ref *list = NULL;
176 177
	get_remote_heads(-1, heads->buf, heads->len, &list,
			 for_push ? REF_NORMAL : 0, NULL);
178 179 180 181 182 183 184 185 186 187 188 189 190 191
	return list;
}

static struct ref *parse_info_refs(struct discovery *heads)
{
	char *data, *start, *mid;
	char *ref_name;
	int i = 0;

	struct ref *refs = NULL;
	struct ref *ref = NULL;
	struct ref *last_ref = NULL;

	data = heads->buf;
192 193
	start = NULL;
	mid = data;
194
	while (i < heads->len) {
195 196 197 198 199 200
		if (!start) {
			start = &data[i];
		}
		if (data[i] == '\t')
			mid = &data[i];
		if (data[i] == '\n') {
201 202
			if (mid - start != 40)
				die("%sinfo/refs not valid: is this a git repository?", url);
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
			data[i] = 0;
			ref_name = mid + 1;
			ref = xmalloc(sizeof(struct ref) +
				      strlen(ref_name) + 1);
			memset(ref, 0, sizeof(struct ref));
			strcpy(ref->name, ref_name);
			get_sha1_hex(start, ref->old_sha1);
			if (!refs)
				refs = ref;
			if (last_ref)
				last_ref->next = ref;
			last_ref = ref;
			start = NULL;
		}
		i++;
	}

	ref = alloc_ref("HEAD");
221
	if (!http_fetch_ref(url, ref) &&
222 223 224 225 226 227 228 229 230 231
	    !resolve_remote_symref(ref, refs)) {
		ref->next = refs;
		refs = ref;
	} else {
		free(ref);
	}

	return refs;
}

232 233 234 235 236 237 238 239 240 241
static struct ref *get_refs(int for_push)
{
	struct discovery *heads;

	if (for_push)
		heads = discover_refs("git-receive-pack");
	else
		heads = discover_refs("git-upload-pack");

	if (heads->proto_git)
J
Jeff King 已提交
242
		return parse_git_refs(heads, for_push);
243 244 245
	return parse_info_refs(heads);
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259
static void output_refs(struct ref *refs)
{
	struct ref *posn;
	for (posn = refs; posn; posn = posn->next) {
		if (posn->symref)
			printf("@%s %s\n", posn->symref, posn->name);
		else
			printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
	}
	printf("\n");
	fflush(stdout);
	free_refs(refs);
}

260 261 262
struct rpc_state {
	const char *service_name;
	const char **argv;
263
	struct strbuf *stdin_preamble;
264 265 266 267 268 269 270 271 272 273
	char *service_url;
	char *hdr_content_type;
	char *hdr_accept;
	char *buf;
	size_t alloc;
	size_t len;
	size_t pos;
	int in;
	int out;
	struct strbuf result;
S
Shawn O. Pearce 已提交
274
	unsigned gzip_request : 1;
275
	unsigned initial_buffer : 1;
276 277 278 279 280 281 282 283 284 285
};

static size_t rpc_out(void *ptr, size_t eltsize,
		size_t nmemb, void *buffer_)
{
	size_t max = eltsize * nmemb;
	struct rpc_state *rpc = buffer_;
	size_t avail = rpc->len - rpc->pos;

	if (!avail) {
286
		rpc->initial_buffer = 0;
287
		avail = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
288 289 290 291 292 293
		if (!avail)
			return 0;
		rpc->pos = 0;
		rpc->len = avail;
	}

T
Tay Ray Chuan 已提交
294
	if (max < avail)
295 296 297 298 299 300
		avail = max;
	memcpy(ptr, rpc->buf + rpc->pos, avail);
	rpc->pos += avail;
	return avail;
}

301
#ifndef NO_CURL_IOCTL
302
static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
{
	struct rpc_state *rpc = clientp;

	switch (cmd) {
	case CURLIOCMD_NOP:
		return CURLIOE_OK;

	case CURLIOCMD_RESTARTREAD:
		if (rpc->initial_buffer) {
			rpc->pos = 0;
			return CURLIOE_OK;
		}
		fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
		return CURLIOE_FAILRESTART;

	default:
		return CURLIOE_UNKNOWNCMD;
	}
}
#endif

324
static size_t rpc_in(char *ptr, size_t eltsize,
325 326 327 328 329 330 331 332
		size_t nmemb, void *buffer_)
{
	size_t size = eltsize * nmemb;
	struct rpc_state *rpc = buffer_;
	write_or_die(rpc->in, ptr, size);
	return size;
}

333 334
static int run_slot(struct active_request_slot *slot)
{
335
	int err;
336 337 338 339 340 341
	struct slot_results results;

	slot->results = &results;
	slot->curl_result = curl_easy_perform(slot->curl);
	finish_active_slot(slot);

342
	err = handle_curl_result(&results);
343 344 345
	if (err != HTTP_OK && err != HTTP_REAUTH) {
		error("RPC failed; result=%d, HTTP code = %ld",
		      results.curl_result, results.http_code);
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	}

	return err;
}

static int probe_rpc(struct rpc_state *rpc)
{
	struct active_request_slot *slot;
	struct curl_slist *headers = NULL;
	struct strbuf buf = STRBUF_INIT;
	int err;

	slot = get_active_slot();

	headers = curl_slist_append(headers, rpc->hdr_content_type);
	headers = curl_slist_append(headers, rpc->hdr_accept);

	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
	curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
	curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
366
	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, NULL);
367 368 369 370 371 372 373 374 375 376 377 378 379
	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);

	err = run_slot(slot);

	curl_slist_free_all(headers);
	strbuf_release(&buf);
	return err;
}

380 381 382 383
static int post_rpc(struct rpc_state *rpc)
{
	struct active_request_slot *slot;
	struct curl_slist *headers = NULL;
S
Shawn O. Pearce 已提交
384 385
	int use_gzip = rpc->gzip_request;
	char *gzip_body = NULL;
386
	size_t gzip_size = 0;
387
	int err, large_request = 0;
388 389 390 391 392 393 394 395 396 397 398 399

	/* Try to load the entire request, if we can fit it into the
	 * allocated buffer space we can use HTTP/1.0 and avoid the
	 * chunked encoding mess.
	 */
	while (1) {
		size_t left = rpc->alloc - rpc->len;
		char *buf = rpc->buf + rpc->len;
		int n;

		if (left < LARGE_PACKET_MAX) {
			large_request = 1;
S
Shawn O. Pearce 已提交
400
			use_gzip = 0;
401 402 403
			break;
		}

404
		n = packet_read(rpc->out, NULL, NULL, buf, left, 0);
405 406 407 408 409
		if (!n)
			break;
		rpc->len += n;
	}

410
	if (large_request) {
411 412 413 414 415
		do {
			err = probe_rpc(rpc);
		} while (err == HTTP_REAUTH);
		if (err != HTTP_OK)
			return -1;
416 417
	}

418 419 420 421 422
	headers = curl_slist_append(headers, rpc->hdr_content_type);
	headers = curl_slist_append(headers, rpc->hdr_accept);
	headers = curl_slist_append(headers, "Expect:");

retry:
423 424 425
	slot = get_active_slot();

	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
426
	curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
427
	curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
428
	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
429 430 431 432 433 434

	if (large_request) {
		/* The request body is large and the size cannot be predicted.
		 * We must use chunked encoding to send it.
		 */
		headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
435
		rpc->initial_buffer = 1;
436 437
		curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
		curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
438 439 440 441
#ifndef NO_CURL_IOCTL
		curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
		curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
#endif
442 443 444 445 446
		if (options.verbosity > 1) {
			fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
			fflush(stderr);
		}

447 448 449 450 451 452 453 454 455
	} else if (gzip_body) {
		/*
		 * If we are looping to retry authentication, then the previous
		 * run will have set up the headers and gzip buffer already,
		 * and we just need to send it.
		 */
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);

S
Shawn O. Pearce 已提交
456 457 458 459 460
	} else if (use_gzip && 1024 < rpc->len) {
		/* The client backend isn't giving us compressed data so
		 * we can try to deflate it ourselves, this may save on.
		 * the transfer time.
		 */
461
		git_zstream stream;
S
Shawn O. Pearce 已提交
462 463 464
		int ret;

		memset(&stream, 0, sizeof(stream));
465
		git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
466 467
		gzip_size = git_deflate_bound(&stream, rpc->len);
		gzip_body = xmalloc(gzip_size);
S
Shawn O. Pearce 已提交
468 469 470 471

		stream.next_in = (unsigned char *)rpc->buf;
		stream.avail_in = rpc->len;
		stream.next_out = (unsigned char *)gzip_body;
472
		stream.avail_out = gzip_size;
S
Shawn O. Pearce 已提交
473

474
		ret = git_deflate(&stream, Z_FINISH);
S
Shawn O. Pearce 已提交
475 476 477
		if (ret != Z_STREAM_END)
			die("cannot deflate request; zlib deflate error %d", ret);

478
		ret = git_deflate_end_gently(&stream);
S
Shawn O. Pearce 已提交
479 480 481
		if (ret != Z_OK)
			die("cannot deflate request; zlib end error %d", ret);

482
		gzip_size = stream.total_out;
S
Shawn O. Pearce 已提交
483 484 485

		headers = curl_slist_append(headers, "Content-Encoding: gzip");
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
486
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
S
Shawn O. Pearce 已提交
487 488 489 490

		if (options.verbosity > 1) {
			fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
				rpc->service_name,
491
				(unsigned long)rpc->len, (unsigned long)gzip_size);
S
Shawn O. Pearce 已提交
492 493
			fflush(stderr);
		}
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
	} else {
		/* We know the complete request size in advance, use the
		 * more normal Content-Length approach.
		 */
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf);
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len);
		if (options.verbosity > 1) {
			fprintf(stderr, "POST %s (%lu bytes)\n",
				rpc->service_name, (unsigned long)rpc->len);
			fflush(stderr);
		}
	}

	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
	curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);

511
	err = run_slot(slot);
512
	if (err == HTTP_REAUTH && !large_request)
513
		goto retry;
514 515
	if (err != HTTP_OK)
		err = -1;
516 517

	curl_slist_free_all(headers);
S
Shawn O. Pearce 已提交
518
	free(gzip_body);
519 520 521 522 523 524 525
	return err;
}

static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
{
	const char *svc = rpc->service_name;
	struct strbuf buf = STRBUF_INIT;
526
	struct strbuf *preamble = rpc->stdin_preamble;
527 528 529 530 531 532 533 534 535 536
	struct child_process client;
	int err = 0;

	memset(&client, 0, sizeof(client));
	client.in = -1;
	client.out = -1;
	client.git_cmd = 1;
	client.argv = rpc->argv;
	if (start_command(&client))
		exit(1);
537 538
	if (preamble)
		write_or_die(client.in, preamble->buf, preamble->len);
539 540 541 542 543 544 545 546 547
	if (heads)
		write_or_die(client.in, heads->buf, heads->len);

	rpc->alloc = http_post_buffer;
	rpc->buf = xmalloc(rpc->alloc);
	rpc->in = client.in;
	rpc->out = client.out;
	strbuf_init(&rpc->result, 0);

548
	strbuf_addf(&buf, "%s%s", url, svc);
549 550 551 552 553
	rpc->service_url = strbuf_detach(&buf, NULL);

	strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
	rpc->hdr_content_type = strbuf_detach(&buf, NULL);

554
	strbuf_addf(&buf, "Accept: application/x-%s-result", svc);
555 556 557
	rpc->hdr_accept = strbuf_detach(&buf, NULL);

	while (!err) {
558
		int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
559 560 561 562 563 564 565 566 567
		if (!n)
			break;
		rpc->pos = 0;
		rpc->len = n;
		err |= post_rpc(rpc);
	}

	close(client.in);
	client.in = -1;
568 569 570 571 572 573 574 575
	if (!err) {
		strbuf_read(&rpc->result, client.out, 0);
	} else {
		char buf[4096];
		for (;;)
			if (xread(client.out, buf, sizeof(buf)) <= 0)
				break;
	}
576 577

	close(client.out);
578 579 580 581 582 583 584 585 586 587 588
	client.out = -1;

	err |= finish_command(&client);
	free(rpc->service_url);
	free(rpc->hdr_content_type);
	free(rpc->hdr_accept);
	free(rpc->buf);
	strbuf_release(&buf);
	return err;
}

589 590
static int fetch_dumb(int nr_heads, struct ref **to_fetch)
{
591
	struct walker *walker;
592 593 594
	char **targets = xmalloc(nr_heads * sizeof(char*));
	int ret, i;

595 596
	if (options.depth)
		die("dumb http transport does not support --depth");
597 598 599
	for (i = 0; i < nr_heads; i++)
		targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));

600
	walker = get_http_walker(url);
601 602 603
	walker->get_all = 1;
	walker->get_tree = 1;
	walker->get_history = 1;
604
	walker->get_verbosely = options.verbosity >= 3;
605 606
	walker->get_recover = 0;
	ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
607
	walker_free(walker);
608 609 610 611 612 613 614 615

	for (i = 0; i < nr_heads; i++)
		free(targets[i]);
	free(targets);

	return ret ? error("Fetch failed.") : 0;
}

616 617 618 619
static int fetch_git(struct discovery *heads,
	int nr_heads, struct ref **to_fetch)
{
	struct rpc_state rpc;
620
	struct strbuf preamble = STRBUF_INIT;
621 622
	char *depth_arg = NULL;
	int argc = 0, i, err;
623
	const char *argv[15];
624 625 626

	argv[argc++] = "fetch-pack";
	argv[argc++] = "--stateless-rpc";
627
	argv[argc++] = "--stdin";
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
	argv[argc++] = "--lock-pack";
	if (options.followtags)
		argv[argc++] = "--include-tag";
	if (options.thin)
		argv[argc++] = "--thin";
	if (options.verbosity >= 3) {
		argv[argc++] = "-v";
		argv[argc++] = "-v";
	}
	if (!options.progress)
		argv[argc++] = "--no-progress";
	if (options.depth) {
		struct strbuf buf = STRBUF_INIT;
		strbuf_addf(&buf, "--depth=%lu", options.depth);
		depth_arg = strbuf_detach(&buf, NULL);
		argv[argc++] = depth_arg;
	}
	argv[argc++] = url;
646 647
	argv[argc++] = NULL;

648 649 650 651
	for (i = 0; i < nr_heads; i++) {
		struct ref *ref = to_fetch[i];
		if (!ref->name || !*ref->name)
			die("cannot fetch by sha1 over smart http");
652
		packet_buf_write(&preamble, "%s\n", ref->name);
653
	}
654
	packet_buf_flush(&preamble);
655 656 657 658

	memset(&rpc, 0, sizeof(rpc));
	rpc.service_name = "git-upload-pack",
	rpc.argv = argv;
659
	rpc.stdin_preamble = &preamble;
S
Shawn O. Pearce 已提交
660
	rpc.gzip_request = 1;
661 662 663

	err = rpc_service(&rpc, heads);
	if (rpc.result.len)
J
Jeff King 已提交
664
		write_or_die(1, rpc.result.buf, rpc.result.len);
665
	strbuf_release(&rpc.result);
666
	strbuf_release(&preamble);
667 668 669 670 671 672 673 674 675 676 677 678 679
	free(depth_arg);
	return err;
}

static int fetch(int nr_heads, struct ref **to_fetch)
{
	struct discovery *d = discover_refs("git-upload-pack");
	if (d->proto_git)
		return fetch_git(d, nr_heads, to_fetch);
	else
		return fetch_dumb(nr_heads, to_fetch);
}

680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
static void parse_fetch(struct strbuf *buf)
{
	struct ref **to_fetch = NULL;
	struct ref *list_head = NULL;
	struct ref **list = &list_head;
	int alloc_heads = 0, nr_heads = 0;

	do {
		if (!prefixcmp(buf->buf, "fetch ")) {
			char *p = buf->buf + strlen("fetch ");
			char *name;
			struct ref *ref;
			unsigned char old_sha1[20];

			if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
				die("protocol error: expected sha/ref, got %s'", p);
			if (p[40] == ' ')
				name = p + 41;
			else if (!p[40])
				name = "";
			else
				die("protocol error: expected sha/ref, got %s'", p);

			ref = alloc_ref(name);
			hashcpy(ref->old_sha1, old_sha1);

			*list = ref;
			list = &ref->next;

			ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
			to_fetch[nr_heads++] = ref;
		}
		else
			die("http transport does not support %s", buf->buf);

		strbuf_reset(buf);
		if (strbuf_getline(buf, stdin, '\n') == EOF)
			return;
		if (!*buf->buf)
			break;
	} while (1);

722
	if (fetch(nr_heads, to_fetch))
723 724 725 726 727 728 729 730 731
		exit(128); /* error already reported */
	free_refs(list_head);
	free(to_fetch);

	printf("\n");
	fflush(stdout);
	strbuf_reset(buf);
}

732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
static int push_dav(int nr_spec, char **specs)
{
	const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
	int argc = 0, i;

	argv[argc++] = "http-push";
	argv[argc++] = "--helper-status";
	if (options.dry_run)
		argv[argc++] = "--dry-run";
	if (options.verbosity > 1)
		argv[argc++] = "--verbose";
	argv[argc++] = url;
	for (i = 0; i < nr_spec; i++)
		argv[argc++] = specs[i];
	argv[argc++] = NULL;

	if (run_command_v_opt(argv, RUN_GIT_CMD))
		die("git-%s failed", argv[0]);
	free(argv);
	return 0;
}

754 755 756 757 758 759 760 761 762 763 764 765 766 767
static int push_git(struct discovery *heads, int nr_spec, char **specs)
{
	struct rpc_state rpc;
	const char **argv;
	int argc = 0, i, err;

	argv = xmalloc((10 + nr_spec) * sizeof(char*));
	argv[argc++] = "send-pack";
	argv[argc++] = "--stateless-rpc";
	argv[argc++] = "--helper-status";
	if (options.thin)
		argv[argc++] = "--thin";
	if (options.dry_run)
		argv[argc++] = "--dry-run";
768 769 770
	if (options.verbosity == 0)
		argv[argc++] = "--quiet";
	else if (options.verbosity > 1)
771
		argv[argc++] = "--verbose";
772
	argv[argc++] = options.progress ? "--progress" : "--no-progress";
773 774 775 776 777 778 779 780 781 782 783
	argv[argc++] = url;
	for (i = 0; i < nr_spec; i++)
		argv[argc++] = specs[i];
	argv[argc++] = NULL;

	memset(&rpc, 0, sizeof(rpc));
	rpc.service_name = "git-receive-pack",
	rpc.argv = argv;

	err = rpc_service(&rpc, heads);
	if (rpc.result.len)
J
Jeff King 已提交
784
		write_or_die(1, rpc.result.buf, rpc.result.len);
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
	strbuf_release(&rpc.result);
	free(argv);
	return err;
}

static int push(int nr_spec, char **specs)
{
	struct discovery *heads = discover_refs("git-receive-pack");
	int ret;

	if (heads->proto_git)
		ret = push_git(heads, nr_spec, specs);
	else
		ret = push_dav(nr_spec, specs);
	free_discovery(heads);
	return ret;
}

803 804 805
static void parse_push(struct strbuf *buf)
{
	char **specs = NULL;
806
	int alloc_spec = 0, nr_spec = 0, i, ret;
807 808 809 810 811 812 813 814 815 816 817

	do {
		if (!prefixcmp(buf->buf, "push ")) {
			ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
			specs[nr_spec++] = xstrdup(buf->buf + 5);
		}
		else
			die("http transport does not support %s", buf->buf);

		strbuf_reset(buf);
		if (strbuf_getline(buf, stdin, '\n') == EOF)
J
Jim Meyering 已提交
818
			goto free_specs;
819 820 821 822
		if (!*buf->buf)
			break;
	} while (1);

823
	ret = push(nr_spec, specs);
824 825
	printf("\n");
	fflush(stdout);
J
Jim Meyering 已提交
826

827 828 829
	if (ret)
		exit(128); /* error already reported */

J
Jim Meyering 已提交
830 831 832 833
 free_specs:
	for (i = 0; i < nr_spec; i++)
		free(specs[i]);
	free(specs);
834 835
}

836 837 838
int main(int argc, const char **argv)
{
	struct strbuf buf = STRBUF_INIT;
839
	int nongit;
840

841
	git_extract_argv0_path(argv[0]);
842
	setup_git_directory_gently(&nongit);
843 844 845 846 847
	if (argc < 2) {
		fprintf(stderr, "Remote needed\n");
		return 1;
	}

848 849
	options.verbosity = 1;
	options.progress = !!isatty(2);
850
	options.thin = 1;
851

852 853 854
	remote = remote_get(argv[1]);

	if (argc > 2) {
855
		end_url_with_slash(&buf, argv[2]);
856
	} else {
857
		end_url_with_slash(&buf, remote->url[0]);
858 859
	}

860 861
	url = strbuf_detach(&buf, NULL);

J
Jeff King 已提交
862
	http_init(remote, url, 0);
863

864
	do {
865 866 867 868 869 870 871 872
		if (strbuf_getline(&buf, stdin, '\n') == EOF) {
			if (ferror(stdin))
				fprintf(stderr, "Error reading command stream\n");
			else
				fprintf(stderr, "Unexpected end of command stream\n");
			return 1;
		}
		if (buf.len == 0)
873 874
			break;
		if (!prefixcmp(buf.buf, "fetch ")) {
875 876
			if (nongit)
				die("Fetch attempted without a local repo");
877 878
			parse_fetch(&buf);

879
		} else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
880 881
			int for_push = !!strstr(buf.buf + 4, "for-push");
			output_refs(get_refs(for_push));
882 883 884 885

		} else if (!prefixcmp(buf.buf, "push ")) {
			parse_push(&buf);

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
		} else if (!prefixcmp(buf.buf, "option ")) {
			char *name = buf.buf + strlen("option ");
			char *value = strchr(name, ' ');
			int result;

			if (value)
				*value++ = '\0';
			else
				value = "true";

			result = set_option(name, value);
			if (!result)
				printf("ok\n");
			else if (result < 0)
				printf("error invalid value\n");
			else
				printf("unsupported\n");
903
			fflush(stdout);
904

905 906
		} else if (!strcmp(buf.buf, "capabilities")) {
			printf("fetch\n");
907
			printf("option\n");
908
			printf("push\n");
909 910 911
			printf("\n");
			fflush(stdout);
		} else {
912
			fprintf(stderr, "Unknown command '%s'\n", buf.buf);
913 914 915 916
			return 1;
		}
		strbuf_reset(&buf);
	} while (1);
917 918 919

	http_cleanup();

920 921
	return 0;
}