remote-curl.c 21.6 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
{
	struct strbuf buffer = STRBUF_INIT;
96
	struct discovery *last = last_discovery;
97
	char *refs_url;
98
	int http_ret, is_http = 0, proto_git_candidate = 1;
99

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

104
	strbuf_addf(&buffer, "%sinfo/refs", url);
105 106 107 108 109 110 111 112 113
	if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
		is_http = 1;
		if (!strchr(url, '?'))
			strbuf_addch(&buffer, '?');
		else
			strbuf_addch(&buffer, '&');
		strbuf_addf(&buffer, "service=%s", service);
	}
	refs_url = strbuf_detach(&buffer, NULL);
114 115

	http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
116 117

	/* try again with "plain" url (no ? or & appended) */
118
	if (http_ret != HTTP_OK && http_ret != HTTP_NOAUTH) {
119 120 121 122
		free(refs_url);
		strbuf_reset(&buffer);

		proto_git_candidate = 0;
123
		strbuf_addf(&buffer, "%sinfo/refs", url);
124 125 126 127 128
		refs_url = strbuf_detach(&buffer, NULL);

		http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
	}

129 130 131 132 133 134
	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);
135 136
	case HTTP_NOAUTH:
		die("Authentication failed");
137 138 139 140 141
	default:
		http_error(refs_url, http_ret);
		die("HTTP request failed");
	}

142 143 144 145 146
	last= xcalloc(1, sizeof(*last_discovery));
	last->service = service;
	last->buf_alloc = strbuf_detach(&buffer, &last->len);
	last->buf = last->buf_alloc;

147 148
	if (is_http && proto_git_candidate
		&& 5 <= last->len && last->buf[4] == '#') {
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
		/* smart HTTP response; validate that the service
		 * pkt-line matches our request.
		 */
		struct strbuf exp = STRBUF_INIT;

		if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
			die("%s has invalid packet header", refs_url);
		if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
			strbuf_setlen(&buffer, buffer.len - 1);

		strbuf_addf(&exp, "# service=%s", service);
		if (strbuf_cmp(&exp, &buffer))
			die("invalid server response; got '%s'", buffer.buf);
		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.
		 */
		strbuf_reset(&buffer);
		while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
			strbuf_reset(&buffer);

		last->proto_git = 1;
	}

	free(refs_url);
	strbuf_release(&buffer);
	last_discovery = last;
	return last;
}

181
static int write_discovery(int in, int out, void *data)
182 183 184
{
	struct discovery *heads = data;
	int err = 0;
185
	if (write_in_full(out, heads->buf, heads->len) != heads->len)
186
		err = 1;
187
	close(out);
188 189 190
	return err;
}

J
Jeff King 已提交
191
static struct ref *parse_git_refs(struct discovery *heads, int for_push)
192 193 194 195 196 197 198
{
	struct ref *list = NULL;
	struct async async;

	memset(&async, 0, sizeof(async));
	async.proc = write_discovery;
	async.data = heads;
199
	async.out = -1;
200 201 202

	if (start_async(&async))
		die("cannot start thread to parse advertised refs");
203
	get_remote_heads(async.out, &list,
J
Jeff King 已提交
204
			for_push ? REF_NORMAL : 0, NULL);
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
	close(async.out);
	if (finish_async(&async))
		die("ref parsing thread failed");
	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;
222 223
	start = NULL;
	mid = data;
224
	while (i < heads->len) {
225 226 227 228 229 230
		if (!start) {
			start = &data[i];
		}
		if (data[i] == '\t')
			mid = &data[i];
		if (data[i] == '\n') {
231 232
			if (mid - start != 40)
				die("%sinfo/refs not valid: is this a git repository?", url);
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
			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");
251
	if (!http_fetch_ref(url, ref) &&
252 253 254 255 256 257 258 259 260 261
	    !resolve_remote_symref(ref, refs)) {
		ref->next = refs;
		refs = ref;
	} else {
		free(ref);
	}

	return refs;
}

262 263 264 265 266 267 268 269 270 271
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 已提交
272
		return parse_git_refs(heads, for_push);
273 274 275
	return parse_info_refs(heads);
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289
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);
}

290 291 292
struct rpc_state {
	const char *service_name;
	const char **argv;
293
	struct strbuf *stdin_preamble;
294 295 296 297 298 299 300 301 302 303
	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 已提交
304
	unsigned gzip_request : 1;
305
	unsigned initial_buffer : 1;
306 307 308 309 310 311 312 313 314 315
};

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) {
316
		rpc->initial_buffer = 0;
317 318 319 320 321 322 323
		avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
		if (!avail)
			return 0;
		rpc->pos = 0;
		rpc->len = avail;
	}

T
Tay Ray Chuan 已提交
324
	if (max < avail)
325 326 327 328 329 330
		avail = max;
	memcpy(ptr, rpc->buf + rpc->pos, avail);
	rpc->pos += avail;
	return avail;
}

331
#ifndef NO_CURL_IOCTL
332
static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
{
	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

354
static size_t rpc_in(char *ptr, size_t eltsize,
355 356 357 358 359 360 361 362
		size_t nmemb, void *buffer_)
{
	size_t size = eltsize * nmemb;
	struct rpc_state *rpc = buffer_;
	write_or_die(rpc->in, ptr, size);
	return size;
}

363 364
static int run_slot(struct active_request_slot *slot)
{
365
	int err;
366 367 368 369 370 371
	struct slot_results results;

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

372
	err = handle_curl_result(&results);
373 374 375
	if (err != HTTP_OK && err != HTTP_REAUTH) {
		error("RPC failed; result=%d, HTTP code = %ld",
		      results.curl_result, results.http_code);
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
	}

	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);
	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
	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;
}

410 411 412 413
static int post_rpc(struct rpc_state *rpc)
{
	struct active_request_slot *slot;
	struct curl_slist *headers = NULL;
S
Shawn O. Pearce 已提交
414 415
	int use_gzip = rpc->gzip_request;
	char *gzip_body = NULL;
416
	int err, large_request = 0;
417 418 419 420 421 422 423 424 425 426 427 428

	/* 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 已提交
429
			use_gzip = 0;
430 431 432 433 434 435 436 437 438
			break;
		}

		n = packet_read_line(rpc->out, buf, left);
		if (!n)
			break;
		rpc->len += n;
	}

439
	if (large_request) {
440 441 442 443 444
		do {
			err = probe_rpc(rpc);
		} while (err == HTTP_REAUTH);
		if (err != HTTP_OK)
			return -1;
445 446
	}

447 448 449 450 451
	headers = curl_slist_append(headers, rpc->hdr_content_type);
	headers = curl_slist_append(headers, rpc->hdr_accept);
	headers = curl_slist_append(headers, "Expect:");

retry:
452 453 454
	slot = get_active_slot();

	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
455
	curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
456
	curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
S
Shawn O. Pearce 已提交
457
	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
458 459 460 461 462 463

	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");
464
		rpc->initial_buffer = 1;
465 466
		curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
		curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
467 468 469 470
#ifndef NO_CURL_IOCTL
		curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
		curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
#endif
471 472 473 474 475
		if (options.verbosity > 1) {
			fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
			fflush(stderr);
		}

S
Shawn O. Pearce 已提交
476 477 478 479 480 481
	} 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.
		 */
		size_t size;
482
		git_zstream stream;
S
Shawn O. Pearce 已提交
483 484 485
		int ret;

		memset(&stream, 0, sizeof(stream));
486
		git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
J
Junio C Hamano 已提交
487
		size = git_deflate_bound(&stream, rpc->len);
S
Shawn O. Pearce 已提交
488 489 490 491 492 493 494
		gzip_body = xmalloc(size);

		stream.next_in = (unsigned char *)rpc->buf;
		stream.avail_in = rpc->len;
		stream.next_out = (unsigned char *)gzip_body;
		stream.avail_out = size;

495
		ret = git_deflate(&stream, Z_FINISH);
S
Shawn O. Pearce 已提交
496 497 498
		if (ret != Z_STREAM_END)
			die("cannot deflate request; zlib deflate error %d", ret);

499
		ret = git_deflate_end_gently(&stream);
S
Shawn O. Pearce 已提交
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
		if (ret != Z_OK)
			die("cannot deflate request; zlib end error %d", ret);

		size = stream.total_out;

		headers = curl_slist_append(headers, "Content-Encoding: gzip");
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, size);

		if (options.verbosity > 1) {
			fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
				rpc->service_name,
				(unsigned long)rpc->len, (unsigned long)size);
			fflush(stderr);
		}
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
	} 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);

532 533 534
	err = run_slot(slot);
	if (err == HTTP_REAUTH && !large_request && !use_gzip)
		goto retry;
535 536
	if (err != HTTP_OK)
		err = -1;
537 538

	curl_slist_free_all(headers);
S
Shawn O. Pearce 已提交
539
	free(gzip_body);
540 541 542 543 544 545 546
	return err;
}

static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
{
	const char *svc = rpc->service_name;
	struct strbuf buf = STRBUF_INIT;
547
	struct strbuf *preamble = rpc->stdin_preamble;
548 549 550 551 552 553 554 555 556 557
	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);
558 559
	if (preamble)
		write_or_die(client.in, preamble->buf, preamble->len);
560 561 562 563 564 565 566 567 568
	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);

569
	strbuf_addf(&buf, "%s%s", url, svc);
570 571 572 573 574
	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);

575
	strbuf_addf(&buf, "Accept: application/x-%s-result", svc);
576 577 578 579 580 581 582 583 584 585 586 587 588
	rpc->hdr_accept = strbuf_detach(&buf, NULL);

	while (!err) {
		int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
		if (!n)
			break;
		rpc->pos = 0;
		rpc->len = n;
		err |= post_rpc(rpc);
	}

	close(client.in);
	client.in = -1;
589 590 591 592 593 594 595 596
	if (!err) {
		strbuf_read(&rpc->result, client.out, 0);
	} else {
		char buf[4096];
		for (;;)
			if (xread(client.out, buf, sizeof(buf)) <= 0)
				break;
	}
597 598

	close(client.out);
599 600 601 602 603 604 605 606 607 608 609
	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;
}

610 611
static int fetch_dumb(int nr_heads, struct ref **to_fetch)
{
612
	struct walker *walker;
613 614 615
	char **targets = xmalloc(nr_heads * sizeof(char*));
	int ret, i;

616 617
	if (options.depth)
		die("dumb http transport does not support --depth");
618 619 620
	for (i = 0; i < nr_heads; i++)
		targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));

621
	walker = get_http_walker(url);
622 623 624
	walker->get_all = 1;
	walker->get_tree = 1;
	walker->get_history = 1;
625
	walker->get_verbosely = options.verbosity >= 3;
626 627
	walker->get_recover = 0;
	ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
628
	walker_free(walker);
629 630 631 632 633 634 635 636

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

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

637 638 639 640
static int fetch_git(struct discovery *heads,
	int nr_heads, struct ref **to_fetch)
{
	struct rpc_state rpc;
641
	struct strbuf preamble = STRBUF_INIT;
642 643
	char *depth_arg = NULL;
	int argc = 0, i, err;
644
	const char *argv[15];
645 646 647

	argv[argc++] = "fetch-pack";
	argv[argc++] = "--stateless-rpc";
648
	argv[argc++] = "--stdin";
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
	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;
667 668
	argv[argc++] = NULL;

669 670 671 672
	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");
673
		packet_buf_write(&preamble, "%s\n", ref->name);
674
	}
675
	packet_buf_flush(&preamble);
676 677 678 679

	memset(&rpc, 0, sizeof(rpc));
	rpc.service_name = "git-upload-pack",
	rpc.argv = argv;
680
	rpc.stdin_preamble = &preamble;
S
Shawn O. Pearce 已提交
681
	rpc.gzip_request = 1;
682 683 684 685 686

	err = rpc_service(&rpc, heads);
	if (rpc.result.len)
		safe_write(1, rpc.result.buf, rpc.result.len);
	strbuf_release(&rpc.result);
687
	strbuf_release(&preamble);
688 689 690 691 692 693 694 695 696 697 698 699 700
	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);
}

701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
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);

743
	if (fetch(nr_heads, to_fetch))
744 745 746 747 748 749 750 751 752
		exit(128); /* error already reported */
	free_refs(list_head);
	free(to_fetch);

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

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
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;
}

775 776 777 778 779 780 781 782 783 784 785 786 787 788
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";
789 790 791
	if (options.verbosity == 0)
		argv[argc++] = "--quiet";
	else if (options.verbosity > 1)
792
		argv[argc++] = "--verbose";
793
	argv[argc++] = options.progress ? "--progress" : "--no-progress";
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
	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)
		safe_write(1, rpc.result.buf, rpc.result.len);
	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;
}

824 825 826
static void parse_push(struct strbuf *buf)
{
	char **specs = NULL;
827
	int alloc_spec = 0, nr_spec = 0, i, ret;
828 829 830 831 832 833 834 835 836 837 838

	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 已提交
839
			goto free_specs;
840 841 842 843
		if (!*buf->buf)
			break;
	} while (1);

844
	ret = push(nr_spec, specs);
845 846
	printf("\n");
	fflush(stdout);
J
Jim Meyering 已提交
847

848 849 850
	if (ret)
		exit(128); /* error already reported */

J
Jim Meyering 已提交
851 852 853 854
 free_specs:
	for (i = 0; i < nr_spec; i++)
		free(specs[i]);
	free(specs);
855 856
}

857 858 859
int main(int argc, const char **argv)
{
	struct strbuf buf = STRBUF_INIT;
860
	int nongit;
861

862
	git_extract_argv0_path(argv[0]);
863
	setup_git_directory_gently(&nongit);
864 865 866 867 868
	if (argc < 2) {
		fprintf(stderr, "Remote needed\n");
		return 1;
	}

869 870
	options.verbosity = 1;
	options.progress = !!isatty(2);
871
	options.thin = 1;
872

873 874 875
	remote = remote_get(argv[1]);

	if (argc > 2) {
876
		end_url_with_slash(&buf, argv[2]);
877
	} else {
878
		end_url_with_slash(&buf, remote->url[0]);
879 880
	}

881 882
	url = strbuf_detach(&buf, NULL);

J
Jeff King 已提交
883
	http_init(remote, url, 0);
884

885
	do {
886 887 888 889 890 891 892 893
		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)
894 895
			break;
		if (!prefixcmp(buf.buf, "fetch ")) {
896 897
			if (nongit)
				die("Fetch attempted without a local repo");
898 899
			parse_fetch(&buf);

900
		} else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
901 902
			int for_push = !!strstr(buf.buf + 4, "for-push");
			output_refs(get_refs(for_push));
903 904 905 906

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

907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
		} 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");
924
			fflush(stdout);
925

926 927
		} else if (!strcmp(buf.buf, "capabilities")) {
			printf("fetch\n");
928
			printf("option\n");
929
			printf("push\n");
930 931 932
			printf("\n");
			fflush(stdout);
		} else {
933
			fprintf(stderr, "Unknown command '%s'\n", buf.buf);
934 935 936 937
			return 1;
		}
		strbuf_reset(&buf);
	} while (1);
938 939 940

	http_cleanup();

941 942
	return 0;
}