smart_protocol.c 22.3 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
#include "util.h"
17 18

#define NETWORK_XFER_THRESHOLD (100*1024)
19 20
/* The minimal interval between progress updates (in seconds). */
#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
21 22 23 24 25 26

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;
27 28
	const char *line_end = NULL;
	git_pkt *pkt = NULL;
Edward Thomson committed
29 30
	git_pkt_ref *ref;
	size_t i;
31

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

41 42 43 44 45 46 47
	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)
48
			return error;
49 50 51

		if (error == GIT_EBUFS) {
			if ((recvd = gitno_recv(buf)) < 0)
52
				return recvd;
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 91 92 93

			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++;

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

100 101 102 103 104 105 106
		/* Keep multi_ack_detailed before multi_ack */
		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
			caps->common = caps->multi_ack_detailed = 1;
			ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
			continue;
		}

107
		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
108 109 110 111 112
			caps->common = caps->multi_ack = 1;
			ptr += strlen(GIT_CAP_MULTI_ACK);
			continue;
		}

113
		if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
114 115 116 117 118 119
			caps->common = caps->include_tag = 1;
			ptr += strlen(GIT_CAP_INCLUDE_TAG);
			continue;
		}

		/* Keep side-band check after side-band-64k */
120
		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
121 122 123 124 125
			caps->common = caps->side_band_64k = 1;
			ptr += strlen(GIT_CAP_SIDE_BAND_64K);
			continue;
		}

126
		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
127 128 129 130 131
			caps->common = caps->side_band = 1;
			ptr += strlen(GIT_CAP_SIDE_BAND);
			continue;
		}

132 133 134 135 136 137
		if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
			caps->common = caps->delete_refs = 1;
			ptr += strlen(GIT_CAP_DELETE_REFS);
			continue;
		}

138 139 140 141 142 143
		if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
			caps->common = caps->thin_pack = 1;
			ptr += strlen(GIT_CAP_THIN_PACK);
			continue;
		}

144 145 146 147 148 149 150 151 152 153
		/* 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;
154
	git_pkt *pkt = NULL;
155 156 157 158 159 160 161 162 163 164 165 166
	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)
167
			return error;
168 169

		if ((ret = gitno_recv(buf)) < 0)
170
			return ret;
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	} 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;
187
	int error;
188 189

	do {
190 191
		if ((error = recv_pkt(&pkt, buf)) < 0)
			return error;
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

		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;
212
	int error;
213

214 215
	if ((error = git_reference_list(&refs, repo)) < 0)
		return error;
216

217 218
	if ((error = git_revwalk_new(&walk, repo)) < 0)
		return error;
219 220 221 222 223 224 225 226

	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;

227
		if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0)
228 229 230 231
			goto on_error;

		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
			continue;
232

233
		if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
234 235 236 237 238 239 240 241 242 243 244 245
			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);
246
	return error;
247 248
}

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
static int wait_while_ack(gitno_buffer *buf)
{
	int error;
	git_pkt_ack *pkt = NULL;

	while (1) {
		git__free(pkt);

		if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
			return error;

		if (pkt->type == GIT_PKT_NAK)
			break;

		if (pkt->type == GIT_PKT_ACK &&
		    (pkt->status != GIT_ACK_CONTINUE ||
		     pkt->status != GIT_ACK_COMMON)) {
			git__free(pkt);
Edward Thomson committed
267
			return 0;
268 269 270 271 272 273 274
		}
	}

	git__free(pkt);
	return 0;
}

275
int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
276 277 278 279 280 281 282 283 284
{
	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;

285
	if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
286
		return error;
287

288
	if ((error = fetch_setup_walk(&walk, repo)) < 0)
289
		goto on_error;
290

291
	/*
292 293 294 295
	 * Our support for ACK extensions is simply to parse them. On
	 * the first ACK we will accept that as enough common
	 * objects. We give up if we haven't found an answer in the
	 * first 256 we send.
296 297
	 */
	i = 0;
298
	while (i < 256) {
299 300 301 302 303 304 305 306 307
		error = git_revwalk_next(&oid, walk);

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

			goto on_error;
		}

308 309 310 311 312 313 314 315 316 317
		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);
318 319
			if (git_buf_oom(&data)) {
				error = -1;
320
				goto on_error;
321
			}
322

323
			if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
324 325 326
				goto on_error;

			git_buf_clear(&data);
327
			if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
328
				if ((error = store_common(t)) < 0)
329 330 331 332 333 334 335 336
					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;
337 338 339 340
				} else if (pkt_type < 0) {
					/* recv_pkt returned an error */
					error = pkt_type;
					goto on_error;
341 342
				} else {
					giterr_set(GITERR_NET, "Unexpected pkt type");
343
					error = -1;
344 345 346 347 348 349 350 351 352 353 354 355
					goto on_error;
				}
			}
		}

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

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

356
			if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
357 358 359
				goto on_error;

			git_vector_foreach(&t->common, i, pkt) {
360 361
				if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
					goto on_error;
362 363
			}

364 365
			if (git_buf_oom(&data)) {
				error = -1;
366
				goto on_error;
367
			}
368 369 370 371 372 373 374 375
		}
	}

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

376
		if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
377 378 379
			goto on_error;

		git_vector_foreach(&t->common, i, pkt) {
380 381
			if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
				goto on_error;
382 383
		}

384 385
		if (git_buf_oom(&data)) {
			error = -1;
386
			goto on_error;
387
		}
388 389
	}

390 391 392
	if ((error = git_pkt_buffer_done(&data)) < 0)
		goto on_error;

393 394 395 396 397
	if (t->cancelled.val) {
		giterr_set(GITERR_NET, "The fetch was cancelled by the user");
		error = GIT_EUSER;
		goto on_error;
	}
398
	if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
399 400 401 402 403 404
		goto on_error;

	git_buf_free(&data);
	git_revwalk_free(walk);

	/* Now let's eat up whatever the server gives us */
405
	if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
406
		pkt_type = recv_pkt(NULL, buf);
407 408 409 410

		if (pkt_type < 0) {
			return pkt_type;
		} else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
411 412 413 414
			giterr_set(GITERR_NET, "Unexpected pkt type");
			return -1;
		}
	} else {
415
		error = wait_while_ack(buf);
416 417
	}

418
	return error;
419 420 421 422 423 424 425

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

426
static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
427 428 429 430 431 432 433 434 435
{
	int recvd;

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

436
		if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
437 438 439 440 441
			return -1;

		gitno_consume_n(buf, buf->offset);

		if ((recvd = gitno_recv(buf)) < 0)
442
			return recvd;
443 444
	} while(recvd > 0);

445
	if (writepack->commit(writepack, stats) < 0)
446 447 448 449 450
		return -1;

	return 0;
}

451
struct network_packetsize_payload
452 453 454 455
{
	git_transfer_progress_callback callback;
	void *payload;
	git_transfer_progress *stats;
456
	size_t last_fired_bytes;
457 458
};

459
static int network_packetsize(size_t received, void *payload)
460 461 462 463 464 465 466 467
{
	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) {
468
		npp->last_fired_bytes = npp->stats->received_bytes;
469

470
		if (npp->callback(npp->stats, npp->payload))
471
			return GIT_EUSER;
472
	}
473 474

	return 0;
475 476 477 478 479 480 481 482 483 484 485
}

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;
486 487
	git_odb *odb;
	struct git_odb_writepack *writepack = NULL;
488
	int error = 0;
489 490
	struct network_packetsize_payload npp = {0};

491 492
	memset(stats, 0, sizeof(git_transfer_progress));

493 494 495 496 497 498
	if (progress_cb) {
		npp.callback = progress_cb;
		npp.payload = progress_payload;
		npp.stats = stats;
		t->packetsize_cb = &network_packetsize;
		t->packetsize_payload = &npp;
499 500

		/* We might have something in the buffer already from negotiate_fetch */
501
		if (t->buffer.offset > 0 && !t->cancelled.val)
502
			if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
503
				git_atomic_set(&t->cancelled, 1);
504 505
	}

506
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
507
		((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0))
508
		goto done;
509 510 511

	/*
	 * If the remote doesn't support the side-band, we can feed
512
	 * the data directly to the pack writer. Otherwise, we need to
513 514 515
	 * check which one belongs there.
	 */
	if (!t->caps.side_band && !t->caps.side_band_64k) {
516 517
		error = no_sideband(t, writepack, buf, stats);
		goto done;
518 519 520 521 522
	}

	do {
		git_pkt *pkt;

523
		/* Check cancellation before network call */
524
		if (t->cancelled.val) {
525
			giterr_clear();
526
			error = GIT_EUSER;
527
			goto done;
528 529
		}

530 531 532 533 534
		if ((error = recv_pkt(&pkt, buf)) < 0)
			goto done;

		/* Check cancellation after network call */
		if (t->cancelled.val) {
535
			giterr_clear();
536 537 538
			error = GIT_EUSER;
			goto done;
		}
539 540 541 542

		if (pkt->type == GIT_PKT_PROGRESS) {
			if (t->progress_cb) {
				git_pkt_progress *p = (git_pkt_progress *) pkt;
543 544
				error = t->progress_cb(p->data, p->len, t->message_cb_payload);
				if (error)
545
					goto done;
546 547 548 549
			}
			git__free(pkt);
		} else if (pkt->type == GIT_PKT_DATA) {
			git_pkt_data *p = (git_pkt_data *) pkt;
550
			error = writepack->append(writepack, p->data, p->len, stats);
551 552

			git__free(pkt);
553
			if (error != 0)
554
				goto done;
555 556 557 558 559 560 561
		} else if (pkt->type == GIT_PKT_FLUSH) {
			/* A flush indicates the end of the packfile */
			git__free(pkt);
			break;
		}
	} while (1);

562 563 564 565
	/*
	 * Trailing execution of progress_cb, if necessary...
	 * Only the callback through the npp datastructure currently
	 * updates the last_fired_bytes value. It is possible that
566
	 * progress has already been reported with the correct
567 568 569 570 571
	 * "received_bytes" value, but until (if?) this is unified
	 * then we will report progress again to be sure that the
	 * correct last received_bytes value is reported.
	 */
	if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
572 573
		error = npp.callback(npp.stats, npp.payload);
		if (error != 0)
574 575 576
			goto done;
	}

577
	error = writepack->commit(writepack, stats);
578

579
done:
580 581
	if (writepack)
		writepack->free(writepack);
582 583 584 585
	if (progress_cb) {
		t->packetsize_cb = NULL;
		t->packetsize_payload = NULL;
	}
586

587 588
	return error;
}
589 590 591 592

static int gen_pktline(git_buf *buf, git_push *push)
{
	push_spec *spec;
593 594 595 596
	size_t i, len;
	char old_id[41], new_id[41];

	old_id[40] = '\0'; new_id[40] = '\0';
597 598

	git_vector_foreach(&push->specs, i, spec) {
599
		len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->rref);
600 601

		if (i == 0) {
602
			++len; /* '\0' */
603
			if (push->report_status)
604 605
				len += strlen(GIT_CAP_REPORT_STATUS) + 1;
			len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
606 607
		}

608 609
		git_oid_fmt(old_id, &spec->roid);
		git_oid_fmt(new_id, &spec->loid);
610

611
		git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->rref);
612 613 614

		if (i == 0) {
			git_buf_putc(buf, '\0');
615 616 617
			/* Core git always starts their capabilities string with a space */
			if (push->report_status) {
				git_buf_putc(buf, ' ');
618
				git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
619 620 621
			}
			git_buf_putc(buf, ' ');
			git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
622 623 624 625
		}

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

627 628 629 630
	git_buf_puts(buf, "0000");
	return git_buf_oom(buf) ? -1 : 0;
}

631 632 633 634 635 636
static int add_push_report_pkt(git_push *push, git_pkt *pkt)
{
	push_status *status;

	switch (pkt->type) {
		case GIT_PKT_OK:
Linquize committed
637
			status = git__calloc(1, sizeof(push_status));
638 639 640 641 642 643 644 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 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
			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);

692
		if (error < 0 && error != GIT_ITEROVER)
693 694 695 696 697 698
			return error;
	}

	return 0;
}

699 700
static int parse_report(gitno_buffer *buf, git_push *push)
{
701 702
	git_pkt *pkt = NULL;
	const char *line_end = NULL;
703 704 705 706 707 708 709 710 711 712 713 714 715 716
	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)
717
				return recvd;
718 719 720 721 722 723 724 725 726 727

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

		gitno_consume(buf, line_end);

728
		error = 0;
729

730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
		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;
745 746
		}

747
		git_pkt_free(pkt);
748

749
		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
750
		if (error == GIT_ITEROVER)
751 752
			return 0;

753 754
		if (error < 0)
			return error;
755 756 757
	}
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
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;
783
	push_status *push_status;
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
	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);
818
		push_status = git_vector_get(push_report, i);
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
		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);
841
		push_status = git_vector_get(push_report, i);
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861

		/* 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;
}

862 863 864 865 866 867 868 869 870 871
struct push_packbuilder_payload
{
	git_smart_subtransport_stream *stream;
	git_packbuilder *pb;
	git_push_transfer_progress cb;
	void *cb_payload;
	size_t last_bytes;
	double last_progress_report_time;
};

872 873
static int stream_thunk(void *buf, size_t size, void *data)
{
874 875 876 877 878 879 880 881 882
	int error = 0;
	struct push_packbuilder_payload *payload = data;

	if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
		return error;

	if (payload->cb) {
		double current_time = git__timer();
		payload->last_bytes += size;
883

884 885
		if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
			payload->last_progress_report_time = current_time;
886
			if (payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload)) {
887 888 889
				giterr_clear();
				error = GIT_EUSER;
			}
890 891 892 893
		}
	}

	return error;
894 895 896 897 898
}

int git_smart__push(git_transport *transport, git_push *push)
{
	transport_smart *t = (transport_smart *)transport;
899
	struct push_packbuilder_payload packbuilder_payload = {0};
900
	git_buf pktline = GIT_BUF_INIT;
901
	int error = 0, need_pack = 0;
902 903
	push_spec *spec;
	unsigned int i;
904

905 906 907 908 909 910 911
	packbuilder_payload.pb = push->pb;

	if (push->transfer_progress_cb) {
		packbuilder_payload.cb = push->transfer_progress_cb;
		packbuilder_payload.cb_payload = push->transfer_progress_cb_payload;
	}

912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
#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

932 933 934 935 936 937 938 939 940 941 942
	/*
	 * 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;
		}
	}

943 944 945 946
	if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
		(error = gen_pktline(&pktline, push)) < 0 ||
		(error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
		goto done;
947

948 949 950
	if (need_pack &&
		(error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
		goto done;
951 952 953 954 955

	/* 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;
956 957
	else if ((error = parse_report(&t->buffer, push)) < 0)
		goto done;
958

959 960 961 962 963
	/* If progress is being reported write the final report */
	if (push->transfer_progress_cb) {
		push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
	}

964
	if (push->status.length) {
965
		error = update_refs_from_report(&t->refs, &push->specs, &push->status);
966 967 968 969 970
		if (error < 0)
			goto done;

		error = git_smart__update_heads(t);
	}
971

972
done:
973 974 975
	git_buf_free(&pktline);
	return error;
}