smart_protocol.c 19.5 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3 4 5 6
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */
7
#include "git2.h"
8
#include "git2/odb_backend.h"
9

10 11
#include "smart.h"
#include "refs.h"
12
#include "repository.h"
13 14 15
#include "push.h"
#include "pack-objects.h"
#include "remote.h"
16 17 18 19 20 21 22 23

#define NETWORK_XFER_THRESHOLD (100*1024)

int git_smart__store_refs(transport_smart *t, int flushes)
{
	gitno_buffer *buf = &t->buffer;
	git_vector *refs = &t->refs;
	int error, flush = 0, recvd;
24 25
	const char *line_end = NULL;
	git_pkt *pkt = NULL;
Edward Thomson committed
26 27
	git_pkt_ref *ref;
	size_t i;
28

29 30 31
	/* Clear existing refs in case git_remote_connect() is called again
	 * after git_remote_disconnect().
	 */
Edward Thomson committed
32 33 34 35
	git_vector_foreach(refs, i, ref) {
		git__free(ref->head.name);
		git__free(ref);
	}
36 37
	git_vector_clear(refs);

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	do {
		if (buf->offset > 0)
			error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset);
		else
			error = GIT_EBUFS;

		if (error < 0 && error != GIT_EBUFS)
			return -1;

		if (error == GIT_EBUFS) {
			if ((recvd = gitno_recv(buf)) < 0)
				return -1;

			if (recvd == 0 && !flush) {
				giterr_set(GITERR_NET, "Early EOF");
				return -1;
			}

			continue;
		}

		gitno_consume(buf, line_end);
		if (pkt->type == GIT_PKT_ERR) {
			giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
			git__free(pkt);
			return -1;
		}

		if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
			return -1;

		if (pkt->type == GIT_PKT_FLUSH) {
			flush++;
			git_pkt_free(pkt);
		}
	} while (flush < flushes);

	return flush;
}

int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
{
	const char *ptr;

	/* No refs or capabilites, odd but not a problem */
	if (pkt == NULL || pkt->capabilities == NULL)
		return 0;

	ptr = pkt->capabilities;
	while (ptr != NULL && *ptr != '\0') {
		if (*ptr == ' ')
			ptr++;

91
		if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
92 93 94 95 96
			caps->common = caps->ofs_delta = 1;
			ptr += strlen(GIT_CAP_OFS_DELTA);
			continue;
		}

97
		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
98 99 100 101 102
			caps->common = caps->multi_ack = 1;
			ptr += strlen(GIT_CAP_MULTI_ACK);
			continue;
		}

103
		if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
104 105 106 107 108 109
			caps->common = caps->include_tag = 1;
			ptr += strlen(GIT_CAP_INCLUDE_TAG);
			continue;
		}

		/* Keep side-band check after side-band-64k */
110
		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
111 112 113 114 115
			caps->common = caps->side_band_64k = 1;
			ptr += strlen(GIT_CAP_SIDE_BAND_64K);
			continue;
		}

116
		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
117 118 119 120 121
			caps->common = caps->side_band = 1;
			ptr += strlen(GIT_CAP_SIDE_BAND);
			continue;
		}

122 123 124 125 126 127
		if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
			caps->common = caps->delete_refs = 1;
			ptr += strlen(GIT_CAP_DELETE_REFS);
			continue;
		}

128 129 130 131 132 133 134 135 136 137
		/* We don't know this capability, so skip it */
		ptr = strchr(ptr, ' ');
	}

	return 0;
}

static int recv_pkt(git_pkt **out, gitno_buffer *buf)
{
	const char *ptr = buf->data, *line_end = ptr;
138
	git_pkt *pkt = NULL;
139 140 141 142 143 144 145 146 147 148 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 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
	int pkt_type, error = 0, ret;

	do {
		if (buf->offset > 0)
			error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
		else
			error = GIT_EBUFS;

		if (error == 0)
			break; /* return the pkt */

		if (error < 0 && error != GIT_EBUFS)
			return -1;

		if ((ret = gitno_recv(buf)) < 0)
			return -1;
	} while (error);

	gitno_consume(buf, line_end);
	pkt_type = pkt->type;
	if (out != NULL)
		*out = pkt;
	else
		git__free(pkt);

	return pkt_type;
}

static int store_common(transport_smart *t)
{
	git_pkt *pkt = NULL;
	gitno_buffer *buf = &t->buffer;

	do {
		if (recv_pkt(&pkt, buf) < 0)
			return -1;

		if (pkt->type == GIT_PKT_ACK) {
			if (git_vector_insert(&t->common, pkt) < 0)
				return -1;
		} else {
			git__free(pkt);
			return 0;
		}

	} while (1);

	return 0;
}

static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
{
	git_revwalk *walk;
	git_strarray refs;
	unsigned int i;
	git_reference *ref;

196
	if (git_reference_list(&refs, repo) < 0)
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
		return -1;

	if (git_revwalk_new(&walk, repo) < 0)
		return -1;

	git_revwalk_sorting(walk, GIT_SORT_TIME);

	for (i = 0; i < refs.count; ++i) {
		/* No tags */
		if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
			continue;

		if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
			goto on_error;

		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
			continue;
214
		if (git_revwalk_push(walk, git_reference_target(ref)) < 0)
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
			goto on_error;

		git_reference_free(ref);
	}

	git_strarray_free(&refs);
	*out = walk;
	return 0;

on_error:
	git_reference_free(ref);
	git_strarray_free(&refs);
	return -1;
}

int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count)
{
	transport_smart *t = (transport_smart *)transport;
	gitno_buffer *buf = &t->buffer;
	git_buf data = GIT_BUF_INIT;
	git_revwalk *walk = NULL;
	int error = -1, pkt_type;
	unsigned int i;
	git_oid oid;

	/* No own logic, do our thing */
241 242
	if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
		return error;
243

244
	if ((error = fetch_setup_walk(&walk, repo)) < 0)
245 246 247 248 249 250 251
		goto on_error;
	/*
	 * We don't support any kind of ACK extensions, so the negotiation
	 * boils down to sending what we have and listening for an ACK
	 * every once in a while.
	 */
	i = 0;
252 253 254 255 256 257 258 259 260 261
	while (true) {
		error = git_revwalk_next(&oid, walk);

		if (error < 0) {
			if (GIT_ITEROVER == error)
				break;

			goto on_error;
		}

262 263 264 265 266 267 268 269 270 271
		git_pkt_buffer_have(&oid, &data);
		i++;
		if (i % 20 == 0) {
			if (t->cancelled.val) {
				giterr_set(GITERR_NET, "The fetch was cancelled by the user");
				error = GIT_EUSER;
				goto on_error;
			}

			git_pkt_buffer_flush(&data);
272 273
			if (git_buf_oom(&data)) {
				error = -1;
274
				goto on_error;
275
			}
276

277
			if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
278 279 280 281
				goto on_error;

			git_buf_clear(&data);
			if (t->caps.multi_ack) {
282
				if ((error = store_common(t)) < 0)
283 284 285 286 287 288 289 290
					goto on_error;
			} else {
				pkt_type = recv_pkt(NULL, buf);

				if (pkt_type == GIT_PKT_ACK) {
					break;
				} else if (pkt_type == GIT_PKT_NAK) {
					continue;
291 292 293 294
				} else if (pkt_type < 0) {
					/* recv_pkt returned an error */
					error = pkt_type;
					goto on_error;
295 296
				} else {
					giterr_set(GITERR_NET, "Unexpected pkt type");
297
					error = -1;
298 299 300 301 302 303 304 305 306 307 308 309
					goto on_error;
				}
			}
		}

		if (t->common.length > 0)
			break;

		if (i % 20 == 0 && t->rpc) {
			git_pkt_ack *pkt;
			unsigned int i;

310
			if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
311 312 313
				goto on_error;

			git_vector_foreach(&t->common, i, pkt) {
314 315
				if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
					goto on_error;
316 317
			}

318 319
			if (git_buf_oom(&data)) {
				error = -1;
320
				goto on_error;
321
			}
322 323 324 325 326 327 328 329
		}
	}

	/* Tell the other end that we're done negotiating */
	if (t->rpc && t->common.length > 0) {
		git_pkt_ack *pkt;
		unsigned int i;

330
		if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
331 332 333
			goto on_error;

		git_vector_foreach(&t->common, i, pkt) {
334 335
			if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
				goto on_error;
336 337
		}

338 339
		if (git_buf_oom(&data)) {
			error = -1;
340
			goto on_error;
341
		}
342 343
	}

344 345 346
	if ((error = git_pkt_buffer_done(&data)) < 0)
		goto on_error;

347 348 349 350 351
	if (t->cancelled.val) {
		giterr_set(GITERR_NET, "The fetch was cancelled by the user");
		error = GIT_EUSER;
		goto on_error;
	}
352
	if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
353 354 355 356 357 358 359 360
		goto on_error;

	git_buf_free(&data);
	git_revwalk_free(walk);

	/* Now let's eat up whatever the server gives us */
	if (!t->caps.multi_ack) {
		pkt_type = recv_pkt(NULL, buf);
361 362 363 364

		if (pkt_type < 0) {
			return pkt_type;
		} else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
365 366 367 368 369 370
			giterr_set(GITERR_NET, "Unexpected pkt type");
			return -1;
		}
	} else {
		git_pkt_ack *pkt;
		do {
371 372
			if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
				return error;
373 374

			if (pkt->type == GIT_PKT_NAK ||
375
				(pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) {
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
				git__free(pkt);
				break;
			}

			git__free(pkt);
		} while (1);
	}

	return 0;

on_error:
	git_revwalk_free(walk);
	git_buf_free(&data);
	return error;
}

392
static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
393 394 395 396 397 398 399 400 401
{
	int recvd;

	do {
		if (t->cancelled.val) {
			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
			return GIT_EUSER;
		}

402
		if (writepack->add(writepack, buf->data, buf->offset, stats) < 0)
403 404 405 406 407 408 409 410
			return -1;

		gitno_consume_n(buf, buf->offset);

		if ((recvd = gitno_recv(buf)) < 0)
			return -1;
	} while(recvd > 0);

411
	if (writepack->commit(writepack, stats))
412 413 414 415 416
		return -1;

	return 0;
}

417
struct network_packetsize_payload
418 419 420 421
{
	git_transfer_progress_callback callback;
	void *payload;
	git_transfer_progress *stats;
422
	size_t last_fired_bytes;
423 424
};

425
static void network_packetsize(size_t received, void *payload)
426 427 428 429 430 431 432 433
{
	struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;

	/* Accumulate bytes */
	npp->stats->received_bytes += received;

	/* Fire notification if the threshold is reached */
	if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
434 435
		npp->last_fired_bytes = npp->stats->received_bytes;
		npp->callback(npp->stats, npp->payload);
436 437 438 439 440 441 442 443 444 445 446 447
	}
}

int git_smart__download_pack(
	git_transport *transport,
	git_repository *repo,
	git_transfer_progress *stats,
	git_transfer_progress_callback progress_cb,
	void *progress_payload)
{
	transport_smart *t = (transport_smart *)transport;
	gitno_buffer *buf = &t->buffer;
448 449
	git_odb *odb;
	struct git_odb_writepack *writepack = NULL;
450 451 452
	int error = -1;
	struct network_packetsize_payload npp = {0};

453 454
	memset(stats, 0, sizeof(git_transfer_progress));

455 456 457 458 459 460
	if (progress_cb) {
		npp.callback = progress_cb;
		npp.payload = progress_payload;
		npp.stats = stats;
		t->packetsize_cb = &network_packetsize;
		t->packetsize_payload = &npp;
461 462 463

		/* We might have something in the buffer already from negotiate_fetch */
		if (t->buffer.offset > 0)
464
			t->packetsize_cb(t->buffer.offset, t->packetsize_payload);
465 466
	}

467 468
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
		((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
469 470 471 472
		goto on_error;

	/*
	 * If the remote doesn't support the side-band, we can feed
473
	 * the data directly to the pack writer. Otherwise, we need to
474 475 476
	 * check which one belongs there.
	 */
	if (!t->caps.side_band && !t->caps.side_band_64k) {
477
		if (no_sideband(t, writepack, buf, stats) < 0)
478 479
			goto on_error;

480
		goto on_success;
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
	}

	do {
		git_pkt *pkt;

		if (t->cancelled.val) {
			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
			error = GIT_EUSER;
			goto on_error;
		}

		if (recv_pkt(&pkt, buf) < 0)
			goto on_error;

		if (pkt->type == GIT_PKT_PROGRESS) {
			if (t->progress_cb) {
				git_pkt_progress *p = (git_pkt_progress *) pkt;
				t->progress_cb(p->data, p->len, t->message_cb_payload);
			}
			git__free(pkt);
		} else if (pkt->type == GIT_PKT_DATA) {
			git_pkt_data *p = (git_pkt_data *) pkt;
503
			error = writepack->add(writepack, p->data, p->len, stats);
504 505

			git__free(pkt);
506 507
			if (error < 0)
				goto on_error;
508 509 510 511 512 513 514
		} else if (pkt->type == GIT_PKT_FLUSH) {
			/* A flush indicates the end of the packfile */
			git__free(pkt);
			break;
		}
	} while (1);

515
	if (writepack->commit(writepack, stats) < 0)
516 517
		goto on_error;

518 519
on_success:
	error = 0;
520 521

on_error:
522 523
	if (writepack)
		writepack->free(writepack);
524 525 526 527 528

	/* Trailing execution of progress_cb, if necessary */
	if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes)
		npp.callback(npp.stats, npp.payload);

529 530
	return error;
}
531 532 533 534

static int gen_pktline(git_buf *buf, git_push *push)
{
	push_spec *spec;
535 536 537 538
	size_t i, len;
	char old_id[41], new_id[41];

	old_id[40] = '\0'; new_id[40] = '\0';
539 540

	git_vector_foreach(&push->specs, i, spec) {
541
		len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->rref);
542 543

		if (i == 0) {
544
			++len; /* '\0' */
545
			if (push->report_status)
546 547
				len += strlen(GIT_CAP_REPORT_STATUS) + 1;
			len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
548 549
		}

550 551
		git_oid_fmt(old_id, &spec->roid);
		git_oid_fmt(new_id, &spec->loid);
552

553
		git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->rref);
554 555 556

		if (i == 0) {
			git_buf_putc(buf, '\0');
557 558 559
			/* Core git always starts their capabilities string with a space */
			if (push->report_status) {
				git_buf_putc(buf, ' ');
560
				git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
561 562 563
			}
			git_buf_putc(buf, ' ');
			git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
564 565 566 567
		}

		git_buf_putc(buf, '\n');
	}
568

569 570 571 572
	git_buf_puts(buf, "0000");
	return git_buf_oom(buf) ? -1 : 0;
}

573 574 575 576 577 578
static int add_push_report_pkt(git_push *push, git_pkt *pkt)
{
	push_status *status;

	switch (pkt->type) {
		case GIT_PKT_OK:
Linquize committed
579
			status = git__calloc(1, sizeof(push_status));
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
			GITERR_CHECK_ALLOC(status);
			status->msg = NULL;
			status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
			if (!status->ref ||
				git_vector_insert(&push->status, status) < 0) {
				git_push_status_free(status);
				return -1;
			}
			break;
		case GIT_PKT_NG:
			status = git__calloc(sizeof(push_status), 1);
			GITERR_CHECK_ALLOC(status);
			status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
			status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
			if (!status->ref || !status->msg ||
				git_vector_insert(&push->status, status) < 0) {
				git_push_status_free(status);
				return -1;
			}
			break;
		case GIT_PKT_UNPACK:
			push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
			break;
		case GIT_PKT_FLUSH:
			return GIT_ITEROVER;
		default:
			giterr_set(GITERR_NET, "report-status: protocol error");
			return -1;
	}

	return 0;
}

static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
{
	git_pkt *pkt;
	const char *line = data_pkt->data, *line_end;
	size_t line_len = data_pkt->len;
	int error;

	while (line_len > 0) {
		error = git_pkt_parse_line(&pkt, line, &line_end, line_len);

		if (error < 0)
			return error;

		/* Advance in the buffer */
		line_len -= (line_end - line);
		line = line_end;

		error = add_push_report_pkt(push, pkt);

		git_pkt_free(pkt);

634
		if (error < 0 && error != GIT_ITEROVER)
635 636 637 638 639 640
			return error;
	}

	return 0;
}

641 642
static int parse_report(gitno_buffer *buf, git_push *push)
{
643 644
	git_pkt *pkt = NULL;
	const char *line_end = NULL;
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
	int error, recvd;

	for (;;) {
		if (buf->offset > 0)
			error = git_pkt_parse_line(&pkt, buf->data,
						   &line_end, buf->offset);
		else
			error = GIT_EBUFS;

		if (error < 0 && error != GIT_EBUFS)
			return -1;

		if (error == GIT_EBUFS) {
			if ((recvd = gitno_recv(buf)) < 0)
				return -1;

			if (recvd == 0) {
				giterr_set(GITERR_NET, "Early EOF");
				return -1;
			}
			continue;
		}

		gitno_consume(buf, line_end);

670
		error = 0;
671

672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
		switch (pkt->type) {
			case GIT_PKT_DATA:
				/* This is a sideband packet which contains other packets */
				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
				break;
			case GIT_PKT_ERR:
				giterr_set(GITERR_NET, "report-status: Error reported: %s",
					((git_pkt_err *)pkt)->error);
				error = -1;
				break;
			case GIT_PKT_PROGRESS:
				break;
			default:
				error = add_push_report_pkt(push, pkt);
				break;
687 688
		}

689
		git_pkt_free(pkt);
690

691
		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
692
		if (error == GIT_ITEROVER)
693 694
			return 0;

695 696
		if (error < 0)
			return error;
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 722 723 724
static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
{
	git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
	GITERR_CHECK_ALLOC(added);

	added->type = GIT_PKT_REF;
	git_oid_cpy(&added->head.oid, &push_spec->loid);
	added->head.name = git__strdup(push_spec->rref);

	if (!added->head.name ||
		git_vector_insert(refs, added) < 0) {
		git_pkt_free((git_pkt *)added);
		return -1;
	}

	return 0;
}

static int update_refs_from_report(
	git_vector *refs,
	git_vector *push_specs,
	git_vector *push_report)
{
	git_pkt_ref *ref;
	push_spec *push_spec;
725
	push_status *push_status;
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
	size_t i, j, refs_len;
	int cmp;

	/* For each push spec we sent to the server, we should have
	 * gotten back a status packet in the push report */
	if (push_specs->length != push_report->length) {
		giterr_set(GITERR_NET, "report-status: protocol error");
		return -1;
	}

	/* We require that push_specs be sorted with push_spec_rref_cmp,
	 * and that push_report be sorted with push_status_ref_cmp */
	git_vector_sort(push_specs);
	git_vector_sort(push_report);

	git_vector_foreach(push_specs, i, push_spec) {
		push_status = git_vector_get(push_report, i);

		/* For each push spec we sent to the server, we should have
		 * gotten back a status packet in the push report which matches */
		if (strcmp(push_spec->rref, push_status->ref)) {
			giterr_set(GITERR_NET, "report-status: protocol error");
			return -1;
		}
	}

	/* We require that refs be sorted with ref_name_cmp */
	git_vector_sort(refs);
	i = j = 0;
	refs_len = refs->length;

	/* Merge join push_specs with refs */
	while (i < push_specs->length && j < refs_len) {
		push_spec = git_vector_get(push_specs, i);
760
		push_status = git_vector_get(push_report, i);
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
		ref = git_vector_get(refs, j);

		cmp = strcmp(push_spec->rref, ref->head.name);

		/* Iterate appropriately */
		if (cmp <= 0) i++;
		if (cmp >= 0) j++;

		/* Add case */
		if (cmp < 0 &&
			!push_status->msg &&
			add_ref_from_push_spec(refs, push_spec) < 0)
			return -1;

		/* Update case, delete case */
		if (cmp == 0 &&
			!push_status->msg)
			git_oid_cpy(&ref->head.oid, &push_spec->loid);
	}

	for (; i < push_specs->length; i++) {
		push_spec = git_vector_get(push_specs, i);
783
		push_status = git_vector_get(push_report, i);
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803

		/* Add case */
		if (!push_status->msg &&
			add_ref_from_push_spec(refs, push_spec) < 0)
			return -1;
	}

	/* Remove any refs which we updated to have a zero OID. */
	git_vector_rforeach(refs, i, ref) {
		if (git_oid_iszero(&ref->head.oid)) {
			git_vector_remove(refs, i);
			git_pkt_free((git_pkt *)ref);
		}
	}

	git_vector_sort(refs);

	return 0;
}

804 805 806 807 808 809 810 811 812 813 814 815
static int stream_thunk(void *buf, size_t size, void *data)
{
	git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;

	return s->write(s, (const char *)buf, size);
}

int git_smart__push(git_transport *transport, git_push *push)
{
	transport_smart *t = (transport_smart *)transport;
	git_smart_subtransport_stream *s;
	git_buf pktline = GIT_BUF_INIT;
816 817 818
	int error = -1, need_pack = 0;
	push_spec *spec;
	unsigned int i;
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839

#ifdef PUSH_DEBUG
{
	git_remote_head *head;
	char hex[41]; hex[40] = '\0';

	git_vector_foreach(&push->remote->refs, i, head) {
		git_oid_fmt(hex, &head->oid);
		fprintf(stderr, "%s (%s)\n", hex, head->name);
	}

	git_vector_foreach(&push->specs, i, spec) {
		git_oid_fmt(hex, &spec->roid);
		fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
		git_oid_fmt(hex, &spec->loid);
		fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
			spec->rref : spec->lref);
	}
}
#endif

840 841 842 843 844 845 846 847 848 849 850
	/*
	 * Figure out if we need to send a packfile; which is in all
	 * cases except when we only send delete commands
	 */
	git_vector_foreach(&push->specs, i, spec) {
		if (spec->lref) {
			need_pack = 1;
			break;
		}
	}

851 852
	if (git_smart__get_push_stream(t, &s) < 0 ||
		gen_pktline(&pktline, push) < 0 ||
853 854 855 856
		s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
		goto on_error;

	if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
857 858 859 860 861 862 863 864 865
		goto on_error;

	/* If we sent nothing or the server doesn't support report-status, then
	 * we consider the pack to have been unpacked successfully */
	if (!push->specs.length || !push->report_status)
		push->unpack_ok = 1;
	else if (parse_report(&t->buffer, push) < 0)
		goto on_error;

866 867 868
	if (push->status.length &&
		update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
		goto on_error;
869 870 871 872 873 874 875 876

	error = 0;

on_error:
	git_buf_free(&pktline);

	return error;
}