Blame src/transports/smart_protocol.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
#include "git2.h"
Packit Service 20376f
#include "git2/odb_backend.h"
Packit Service 20376f
Packit Service 20376f
#include "smart.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "repository.h"
Packit Service 20376f
#include "push.h"
Packit Service 20376f
#include "pack-objects.h"
Packit Service 20376f
#include "remote.h"
Packit Service 20376f
#include "util.h"
Packit Service 20376f
Packit Service 20376f
#define NETWORK_XFER_THRESHOLD (100*1024)
Packit Service 20376f
/* The minimal interval between progress updates (in seconds). */
Packit Service 20376f
#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
Packit Service 20376f
Packit Service 20376f
bool git_smart__ofs_delta_enabled = true;
Packit Service 20376f
Packit Service 20376f
int git_smart__store_refs(transport_smart *t, int flushes)
Packit Service 20376f
{
Packit Service 20376f
	gitno_buffer *buf = &t->buffer;
Packit Service 20376f
	git_vector *refs = &t->refs;
Packit Service 20376f
	int error, flush = 0, recvd;
Packit Service 20376f
	const char *line_end = NULL;
Packit Service 20376f
	git_pkt *pkt = NULL;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	/* Clear existing refs in case git_remote_connect() is called again
Packit Service 20376f
	 * after git_remote_disconnect().
Packit Service 20376f
	 */
Packit Service 20376f
	git_vector_foreach(refs, i, pkt) {
Packit Service 20376f
		git_pkt_free(pkt);
Packit Service 20376f
	}
Packit Service 20376f
	git_vector_clear(refs);
Packit Service 20376f
	pkt = NULL;
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		if (buf->offset > 0)
Packit Service 20376f
			error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
Packit Service 20376f
		else
Packit Service 20376f
			error = GIT_EBUFS;
Packit Service 20376f
Packit Service 20376f
		if (error < 0 && error != GIT_EBUFS)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (error == GIT_EBUFS) {
Packit Service 20376f
			if ((recvd = gitno_recv(buf)) < 0)
Packit Service 20376f
				return recvd;
Packit Service 20376f
Packit Service 20376f
			if (recvd == 0) {
Packit Service 20376f
				giterr_set(GITERR_NET, "early EOF");
Packit Service 20376f
				return GIT_EEOF;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		gitno_consume(buf, line_end);
Packit Service 20376f
		if (pkt->type == GIT_PKT_ERR) {
Packit Service 20376f
			giterr_set(GITERR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
Packit Service 20376f
			git__free(pkt);
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		if (pkt->type == GIT_PKT_FLUSH) {
Packit Service 20376f
			flush++;
Packit Service 20376f
			git_pkt_free(pkt);
Packit Service 20376f
		}
Packit Service 20376f
	} while (flush < flushes);
Packit Service 20376f
Packit Service 20376f
	return flush;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	const char *end;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	git_refspec *mapping = NULL;
Packit Service 20376f
Packit Service 20376f
	ptr += strlen(GIT_CAP_SYMREF);
Packit Service 20376f
	if (*ptr != '=')
Packit Service 20376f
		goto on_invalid;
Packit Service 20376f
Packit Service 20376f
	ptr++;
Packit Service 20376f
	if (!(end = strchr(ptr, ' ')) &&
Packit Service 20376f
	    !(end = strchr(ptr, '\0')))
Packit Service 20376f
		goto on_invalid;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* symref mapping has refspec format */
Packit Service 20376f
	mapping = git__calloc(1, sizeof(git_refspec));
Packit Service 20376f
	GITERR_CHECK_ALLOC(mapping);
Packit Service 20376f
Packit Service 20376f
	error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
Packit Service 20376f
	/* if the error isn't OOM, then it's a parse error; let's use a nicer message */
Packit Service 20376f
	if (error < 0) {
Packit Service 20376f
		if (giterr_last()->klass != GITERR_NOMEMORY)
Packit Service 20376f
			goto on_invalid;
Packit Service 20376f
Packit Service 20376f
		git__free(mapping);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_vector_insert(symrefs, mapping)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	*out = end;
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_invalid:
Packit Service 20376f
	giterr_set(GITERR_NET, "remote sent invalid symref");
Packit Service 20376f
	git_refspec__free(mapping);
Packit Service 20376f
	git__free(mapping);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
Packit Service 20376f
{
Packit Service 20376f
	const char *ptr;
Packit Service 20376f
Packit Service 20376f
	/* No refs or capabilites, odd but not a problem */
Packit Service 20376f
	if (pkt == NULL || pkt->capabilities == NULL)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	ptr = pkt->capabilities;
Packit Service 20376f
	while (ptr != NULL && *ptr != '\0') {
Packit Service 20376f
		if (*ptr == ' ')
Packit Service 20376f
			ptr++;
Packit Service 20376f
Packit Service 20376f
		if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
Packit Service 20376f
			caps->common = caps->ofs_delta = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_OFS_DELTA);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* Keep multi_ack_detailed before multi_ack */
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
Packit Service 20376f
			caps->common = caps->multi_ack_detailed = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
Packit Service 20376f
			caps->common = caps->multi_ack = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_MULTI_ACK);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
Packit Service 20376f
			caps->common = caps->include_tag = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_INCLUDE_TAG);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* Keep side-band check after side-band-64k */
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
Packit Service 20376f
			caps->common = caps->side_band_64k = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_SIDE_BAND_64K);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
Packit Service 20376f
			caps->common = caps->side_band = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_SIDE_BAND);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
Packit Service 20376f
			caps->common = caps->delete_refs = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_DELETE_REFS);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
Packit Service 20376f
			caps->common = caps->thin_pack = 1;
Packit Service 20376f
			ptr += strlen(GIT_CAP_THIN_PACK);
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
Packit Service 20376f
			int error;
Packit Service 20376f
Packit Service 20376f
			if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
Packit Service 20376f
				return error;
Packit Service 20376f
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* We don't know this capability, so skip it */
Packit Service 20376f
		ptr = strchr(ptr, ' ');
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int recv_pkt(git_pkt **out, git_pkt_type *pkt_type, gitno_buffer *buf)
Packit Service 20376f
{
Packit Service 20376f
	const char *ptr = buf->data, *line_end = ptr;
Packit Service 20376f
	git_pkt *pkt = NULL;
Packit Service 20376f
	int error = 0, ret;
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		if (buf->offset > 0)
Packit Service 20376f
			error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
Packit Service 20376f
		else
Packit Service 20376f
			error = GIT_EBUFS;
Packit Service 20376f
Packit Service 20376f
		if (error == 0)
Packit Service 20376f
			break; /* return the pkt */
Packit Service 20376f
Packit Service 20376f
		if (error < 0 && error != GIT_EBUFS)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if ((ret = gitno_recv(buf)) < 0) {
Packit Service 20376f
			return ret;
Packit Service 20376f
		} else if (ret == 0) {
Packit Service 20376f
			giterr_set(GITERR_NET, "early EOF");
Packit Service 20376f
			return GIT_EEOF;
Packit Service 20376f
		}
Packit Service 20376f
	} while (error);
Packit Service 20376f
Packit Service 20376f
	gitno_consume(buf, line_end);
Packit Service 20376f
	if (pkt_type)
Packit Service 20376f
		*pkt_type = pkt->type;
Packit Service 20376f
	if (out != NULL)
Packit Service 20376f
		*out = pkt;
Packit Service 20376f
	else
Packit Service 20376f
		git__free(pkt);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int store_common(transport_smart *t)
Packit Service 20376f
{
Packit Service 20376f
	git_pkt *pkt = NULL;
Packit Service 20376f
	gitno_buffer *buf = &t->buffer;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (pkt->type == GIT_PKT_ACK) {
Packit Service 20376f
			if (git_vector_insert(&t->common, pkt) < 0)
Packit Service 20376f
				return -1;
Packit Service 20376f
		} else {
Packit Service 20376f
			git__free(pkt);
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
	} while (1);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	git_revwalk *walk = NULL;
Packit Service 20376f
	git_strarray refs;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
	git_reference *ref = NULL;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_list(&refs, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_revwalk_new(&walk, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_revwalk_sorting(walk, GIT_SORT_TIME);
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < refs.count; ++i) {
Packit Service 20376f
		git_reference_free(ref);
Packit Service 20376f
		ref = NULL;
Packit Service 20376f
Packit Service 20376f
		/* No tags */
Packit Service 20376f
		if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = walk;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	if (error)
Packit Service 20376f
		git_revwalk_free(walk);
Packit Service 20376f
	git_reference_free(ref);
Packit Service 20376f
	git_strarray_free(&refs);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int wait_while_ack(gitno_buffer *buf)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_pkt_ack *pkt = NULL;
Packit Service 20376f
Packit Service 20376f
	while (1) {
Packit Service 20376f
		git__free(pkt);
Packit Service 20376f
Packit Service 20376f
		if ((error = recv_pkt((git_pkt **)&pkt, NULL, buf)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (pkt->type == GIT_PKT_NAK)
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		if (pkt->type == GIT_PKT_ACK &&
Packit Service 20376f
		    (pkt->status != GIT_ACK_CONTINUE &&
Packit Service 20376f
		     pkt->status != GIT_ACK_COMMON &&
Packit Service 20376f
		     pkt->status != GIT_ACK_READY)) {
Packit Service 20376f
			git__free(pkt);
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git__free(pkt);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
Packit Service 20376f
{
Packit Service 20376f
	transport_smart *t = (transport_smart *)transport;
Packit Service 20376f
	gitno_buffer *buf = &t->buffer;
Packit Service 20376f
	git_buf data = GIT_BUF_INIT;
Packit Service 20376f
	git_revwalk *walk = NULL;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
	git_pkt_type pkt_type;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
	git_oid oid;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = fetch_setup_walk(&walk, repo)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Our support for ACK extensions is simply to parse them. On
Packit Service 20376f
	 * the first ACK we will accept that as enough common
Packit Service 20376f
	 * objects. We give up if we haven't found an answer in the
Packit Service 20376f
	 * first 256 we send.
Packit Service 20376f
	 */
Packit Service 20376f
	i = 0;
Packit Service 20376f
	while (i < 256) {
Packit Service 20376f
		error = git_revwalk_next(&oid, walk);
Packit Service 20376f
Packit Service 20376f
		if (error < 0) {
Packit Service 20376f
			if (GIT_ITEROVER == error)
Packit Service 20376f
				break;
Packit Service 20376f
Packit Service 20376f
			goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_pkt_buffer_have(&oid, &data);
Packit Service 20376f
		i++;
Packit Service 20376f
		if (i % 20 == 0) {
Packit Service 20376f
			if (t->cancelled.val) {
Packit Service 20376f
				giterr_set(GITERR_NET, "The fetch was cancelled by the user");
Packit Service 20376f
				error = GIT_EUSER;
Packit Service 20376f
				goto on_error;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			git_pkt_buffer_flush(&data);
Packit Service 20376f
			if (git_buf_oom(&data)) {
Packit Service 20376f
				error = -1;
Packit Service 20376f
				goto on_error;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
Packit Service 20376f
				goto on_error;
Packit Service 20376f
Packit Service 20376f
			git_buf_clear(&data);
Packit Service 20376f
			if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
Packit Service 20376f
				if ((error = store_common(t)) < 0)
Packit Service 20376f
					goto on_error;
Packit Service 20376f
			} else {
Packit Service 20376f
				error = recv_pkt(NULL, &pkt_type, buf);
Packit Service 20376f
				if (error < 0) {
Packit Service 20376f
					goto on_error;
Packit Service 20376f
				} else if (pkt_type == GIT_PKT_ACK) {
Packit Service 20376f
					break;
Packit Service 20376f
				} else if (pkt_type == GIT_PKT_NAK) {
Packit Service 20376f
					continue;
Packit Service 20376f
				} else {
Packit Service 20376f
					giterr_set(GITERR_NET, "Unexpected pkt type");
Packit Service 20376f
					error = -1;
Packit Service 20376f
					goto on_error;
Packit Service 20376f
				}
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (t->common.length > 0)
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		if (i % 20 == 0 && t->rpc) {
Packit Service 20376f
			git_pkt_ack *pkt;
Packit Service 20376f
			unsigned int j;
Packit Service 20376f
Packit Service 20376f
			if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
Packit Service 20376f
				goto on_error;
Packit Service 20376f
Packit Service 20376f
			git_vector_foreach(&t->common, j, pkt) {
Packit Service 20376f
				if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
Packit Service 20376f
					goto on_error;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			if (git_buf_oom(&data)) {
Packit Service 20376f
				error = -1;
Packit Service 20376f
				goto on_error;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Tell the other end that we're done negotiating */
Packit Service 20376f
	if (t->rpc && t->common.length > 0) {
Packit Service 20376f
		git_pkt_ack *pkt;
Packit Service 20376f
		unsigned int j;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		git_vector_foreach(&t->common, j, pkt) {
Packit Service 20376f
			if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
Packit Service 20376f
				goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (git_buf_oom(&data)) {
Packit Service 20376f
			error = -1;
Packit Service 20376f
			goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_pkt_buffer_done(&data)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (t->cancelled.val) {
Packit Service 20376f
		giterr_set(GITERR_NET, "The fetch was cancelled by the user");
Packit Service 20376f
		error = GIT_EUSER;
Packit Service 20376f
		goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
	if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&data);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
Packit Service 20376f
	/* Now let's eat up whatever the server gives us */
Packit Service 20376f
	if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
Packit Service 20376f
		error = recv_pkt(NULL, &pkt_type, buf);
Packit Service 20376f
Packit Service 20376f
		if (error < 0) {
Packit Service 20376f
			return error;
Packit Service 20376f
		} else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
Packit Service 20376f
			giterr_set(GITERR_NET, "Unexpected pkt type");
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
	} else {
Packit Service 20376f
		error = wait_while_ack(buf);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
	git_buf_free(&data);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
Packit Service 20376f
{
Packit Service 20376f
	int recvd;
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		if (t->cancelled.val) {
Packit Service 20376f
			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
Packit Service 20376f
			return GIT_EUSER;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		gitno_consume_n(buf, buf->offset);
Packit Service 20376f
Packit Service 20376f
		if ((recvd = gitno_recv(buf)) < 0)
Packit Service 20376f
			return recvd;
Packit Service 20376f
	} while(recvd > 0);
Packit Service 20376f
Packit Service 20376f
	if (writepack->commit(writepack, stats) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct network_packetsize_payload
Packit Service 20376f
{
Packit Service 20376f
	git_transfer_progress_cb callback;
Packit Service 20376f
	void *payload;
Packit Service 20376f
	git_transfer_progress *stats;
Packit Service 20376f
	size_t last_fired_bytes;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int network_packetsize(size_t received, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
Packit Service 20376f
Packit Service 20376f
	/* Accumulate bytes */
Packit Service 20376f
	npp->stats->received_bytes += received;
Packit Service 20376f
Packit Service 20376f
	/* Fire notification if the threshold is reached */
Packit Service 20376f
	if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
Packit Service 20376f
		npp->last_fired_bytes = npp->stats->received_bytes;
Packit Service 20376f
Packit Service 20376f
		if (npp->callback(npp->stats, npp->payload))
Packit Service 20376f
			return GIT_EUSER;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_smart__download_pack(
Packit Service 20376f
	git_transport *transport,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_transfer_progress *stats,
Packit Service 20376f
	git_transfer_progress_cb transfer_progress_cb,
Packit Service 20376f
	void *progress_payload)
Packit Service 20376f
{
Packit Service 20376f
	transport_smart *t = (transport_smart *)transport;
Packit Service 20376f
	gitno_buffer *buf = &t->buffer;
Packit Service 20376f
	git_odb *odb;
Packit Service 20376f
	struct git_odb_writepack *writepack = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	struct network_packetsize_payload npp = {0};
Packit Service 20376f
Packit Service 20376f
	memset(stats, 0, sizeof(git_transfer_progress));
Packit Service 20376f
Packit Service 20376f
	if (transfer_progress_cb) {
Packit Service 20376f
		npp.callback = transfer_progress_cb;
Packit Service 20376f
		npp.payload = progress_payload;
Packit Service 20376f
		npp.stats = stats;
Packit Service 20376f
		t->packetsize_cb = &network_packetsize;
Packit Service 20376f
		t->packetsize_payload = &npp;
Packit Service 20376f
Packit Service 20376f
		/* We might have something in the buffer already from negotiate_fetch */
Packit Service 20376f
		if (t->buffer.offset > 0 && !t->cancelled.val)
Packit Service 20376f
			if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
Packit Service 20376f
				git_atomic_set(&t->cancelled, 1);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
Packit Service 20376f
		((error = git_odb_write_pack(&writepack, odb, transfer_progress_cb, progress_payload)) != 0))
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * If the remote doesn't support the side-band, we can feed
Packit Service 20376f
	 * the data directly to the pack writer. Otherwise, we need to
Packit Service 20376f
	 * check which one belongs there.
Packit Service 20376f
	 */
Packit Service 20376f
	if (!t->caps.side_band && !t->caps.side_band_64k) {
Packit Service 20376f
		error = no_sideband(t, writepack, buf, stats);
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		git_pkt *pkt = NULL;
Packit Service 20376f
Packit Service 20376f
		/* Check cancellation before network call */
Packit Service 20376f
		if (t->cancelled.val) {
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
			error = GIT_EUSER;
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
Packit Service 20376f
			/* Check cancellation after network call */
Packit Service 20376f
			if (t->cancelled.val) {
Packit Service 20376f
				giterr_clear();
Packit Service 20376f
				error = GIT_EUSER;
Packit Service 20376f
			} else if (pkt->type == GIT_PKT_PROGRESS) {
Packit Service 20376f
				if (t->progress_cb) {
Packit Service 20376f
					git_pkt_progress *p = (git_pkt_progress *) pkt;
Packit Service 20376f
					error = t->progress_cb(p->data, p->len, t->message_cb_payload);
Packit Service 20376f
				}
Packit Service 20376f
			} else if (pkt->type == GIT_PKT_DATA) {
Packit Service 20376f
				git_pkt_data *p = (git_pkt_data *) pkt;
Packit Service 20376f
Packit Service 20376f
				if (p->len)
Packit Service 20376f
					error = writepack->append(writepack, p->data, p->len, stats);
Packit Service 20376f
			} else if (pkt->type == GIT_PKT_FLUSH) {
Packit Service 20376f
				/* A flush indicates the end of the packfile */
Packit Service 20376f
				git__free(pkt);
Packit Service 20376f
				break;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git__free(pkt);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
	} while (1);
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Trailing execution of transfer_progress_cb, if necessary...
Packit Service 20376f
	 * Only the callback through the npp datastructure currently
Packit Service 20376f
	 * updates the last_fired_bytes value. It is possible that
Packit Service 20376f
	 * progress has already been reported with the correct
Packit Service 20376f
	 * "received_bytes" value, but until (if?) this is unified
Packit Service 20376f
	 * then we will report progress again to be sure that the
Packit Service 20376f
	 * correct last received_bytes value is reported.
Packit Service 20376f
	 */
Packit Service 20376f
	if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
Packit Service 20376f
		error = npp.callback(npp.stats, npp.payload);
Packit Service 20376f
		if (error != 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = writepack->commit(writepack, stats);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (writepack)
Packit Service 20376f
		writepack->free(writepack);
Packit Service 20376f
	if (transfer_progress_cb) {
Packit Service 20376f
		t->packetsize_cb = NULL;
Packit Service 20376f
		t->packetsize_payload = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int gen_pktline(git_buf *buf, git_push *push)
Packit Service 20376f
{
Packit Service 20376f
	push_spec *spec;
Packit Service 20376f
	size_t i, len;
Packit Service 20376f
	char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
Packit Service 20376f
Packit Service 20376f
	old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&push->specs, i, spec) {
Packit Service 20376f
		len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst);
Packit Service 20376f
Packit Service 20376f
		if (i == 0) {
Packit Service 20376f
			++len; /* '\0' */
Packit Service 20376f
			if (push->report_status)
Packit Service 20376f
				len += strlen(GIT_CAP_REPORT_STATUS) + 1;
Packit Service 20376f
			len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_oid_fmt(old_id, &spec->roid);
Packit Service 20376f
		git_oid_fmt(new_id, &spec->loid);
Packit Service 20376f
Packit Service 20376f
		git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
Packit Service 20376f
Packit Service 20376f
		if (i == 0) {
Packit Service 20376f
			git_buf_putc(buf, '\0');
Packit Service 20376f
			/* Core git always starts their capabilities string with a space */
Packit Service 20376f
			if (push->report_status) {
Packit Service 20376f
				git_buf_putc(buf, ' ');
Packit Service 20376f
				git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
Packit Service 20376f
			}
Packit Service 20376f
			git_buf_putc(buf, ' ');
Packit Service 20376f
			git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_buf_putc(buf, '\n');
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_puts(buf, "0000");
Packit Service 20376f
	return git_buf_oom(buf) ? -1 : 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int add_push_report_pkt(git_push *push, git_pkt *pkt)
Packit Service 20376f
{
Packit Service 20376f
	push_status *status;
Packit Service 20376f
Packit Service 20376f
	switch (pkt->type) {
Packit Service 20376f
		case GIT_PKT_OK:
Packit Service 20376f
			status = git__calloc(1, sizeof(push_status));
Packit Service 20376f
			GITERR_CHECK_ALLOC(status);
Packit Service 20376f
			status->msg = NULL;
Packit Service 20376f
			status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
Packit Service 20376f
			if (!status->ref ||
Packit Service 20376f
				git_vector_insert(&push->status, status) < 0) {
Packit Service 20376f
				git_push_status_free(status);
Packit Service 20376f
				return -1;
Packit Service 20376f
			}
Packit Service 20376f
			break;
Packit Service 20376f
		case GIT_PKT_NG:
Packit Service 20376f
			status = git__calloc(1, sizeof(push_status));
Packit Service 20376f
			GITERR_CHECK_ALLOC(status);
Packit Service 20376f
			status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
Packit Service 20376f
			status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
Packit Service 20376f
			if (!status->ref || !status->msg ||
Packit Service 20376f
				git_vector_insert(&push->status, status) < 0) {
Packit Service 20376f
				git_push_status_free(status);
Packit Service 20376f
				return -1;
Packit Service 20376f
			}
Packit Service 20376f
			break;
Packit Service 20376f
		case GIT_PKT_UNPACK:
Packit Service 20376f
			push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
Packit Service 20376f
			break;
Packit Service 20376f
		case GIT_PKT_FLUSH:
Packit Service 20376f
			return GIT_ITEROVER;
Packit Service 20376f
		default:
Packit Service 20376f
			giterr_set(GITERR_NET, "report-status: protocol error");
Packit Service 20376f
			return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf)
Packit Service 20376f
{
Packit Service 20376f
	git_pkt *pkt;
Packit Service 20376f
	const char *line, *line_end = NULL;
Packit Service 20376f
	size_t line_len;
Packit Service 20376f
	int error;
Packit Service 20376f
	int reading_from_buf = data_pkt_buf->size > 0;
Packit Service 20376f
Packit Service 20376f
	if (reading_from_buf) {
Packit Service 20376f
		/* We had an existing partial packet, so add the new
Packit Service 20376f
		 * packet to the buffer and parse the whole thing */
Packit Service 20376f
		git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len);
Packit Service 20376f
		line = data_pkt_buf->ptr;
Packit Service 20376f
		line_len = data_pkt_buf->size;
Packit Service 20376f
	}
Packit Service 20376f
	else {
Packit Service 20376f
		line = data_pkt->data;
Packit Service 20376f
		line_len = data_pkt->len;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	while (line_len > 0) {
Packit Service 20376f
		error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
Packit Service 20376f
Packit Service 20376f
		if (error == GIT_EBUFS) {
Packit Service 20376f
			/* Buffer the data when the inner packet is split
Packit Service 20376f
			 * across multiple sideband packets */
Packit Service 20376f
			if (!reading_from_buf)
Packit Service 20376f
				git_buf_put(data_pkt_buf, line, line_len);
Packit Service 20376f
			error = 0;
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
		else if (error < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		/* Advance in the buffer */
Packit Service 20376f
		line_len -= (line_end - line);
Packit Service 20376f
		line = line_end;
Packit Service 20376f
Packit Service 20376f
		error = add_push_report_pkt(push, pkt);
Packit Service 20376f
Packit Service 20376f
		git_pkt_free(pkt);
Packit Service 20376f
Packit Service 20376f
		if (error < 0 && error != GIT_ITEROVER)
Packit Service 20376f
			goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = 0;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (reading_from_buf)
Packit Service 20376f
		git_buf_consume(data_pkt_buf, line_end);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int parse_report(transport_smart *transport, git_push *push)
Packit Service 20376f
{
Packit Service 20376f
	git_pkt *pkt = NULL;
Packit Service 20376f
	const char *line_end = NULL;
Packit Service 20376f
	gitno_buffer *buf = &transport->buffer;
Packit Service 20376f
	int error, recvd;
Packit Service 20376f
	git_buf data_pkt_buf = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	for (;;) {
Packit Service 20376f
		if (buf->offset > 0)
Packit Service 20376f
			error = git_pkt_parse_line(&pkt, &line_end,
Packit Service 20376f
						   buf->data, buf->offset);
Packit Service 20376f
		else
Packit Service 20376f
			error = GIT_EBUFS;
Packit Service 20376f
Packit Service 20376f
		if (error < 0 && error != GIT_EBUFS) {
Packit Service 20376f
			error = -1;
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (error == GIT_EBUFS) {
Packit Service 20376f
			if ((recvd = gitno_recv(buf)) < 0) {
Packit Service 20376f
				error = recvd;
Packit Service 20376f
				goto done;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			if (recvd == 0) {
Packit Service 20376f
				giterr_set(GITERR_NET, "early EOF");
Packit Service 20376f
				error = GIT_EEOF;
Packit Service 20376f
				goto done;
Packit Service 20376f
			}
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		gitno_consume(buf, line_end);
Packit Service 20376f
Packit Service 20376f
		error = 0;
Packit Service 20376f
Packit Service 20376f
		switch (pkt->type) {
Packit Service 20376f
			case GIT_PKT_DATA:
Packit Service 20376f
				/* This is a sideband packet which contains other packets */
Packit Service 20376f
				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
Packit Service 20376f
				break;
Packit Service 20376f
			case GIT_PKT_ERR:
Packit Service 20376f
				giterr_set(GITERR_NET, "report-status: Error reported: %s",
Packit Service 20376f
					((git_pkt_err *)pkt)->error);
Packit Service 20376f
				error = -1;
Packit Service 20376f
				break;
Packit Service 20376f
			case GIT_PKT_PROGRESS:
Packit Service 20376f
				if (transport->progress_cb) {
Packit Service 20376f
					git_pkt_progress *p = (git_pkt_progress *) pkt;
Packit Service 20376f
					error = transport->progress_cb(p->data, p->len, transport->message_cb_payload);
Packit Service 20376f
				}
Packit Service 20376f
				break;
Packit Service 20376f
			default:
Packit Service 20376f
				error = add_push_report_pkt(push, pkt);
Packit Service 20376f
				break;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_pkt_free(pkt);
Packit Service 20376f
Packit Service 20376f
		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
Packit Service 20376f
		if (error == GIT_ITEROVER) {
Packit Service 20376f
			error = 0;
Packit Service 20376f
			if (data_pkt_buf.size > 0) {
Packit Service 20376f
				/* If there was data remaining in the pack data buffer,
Packit Service 20376f
				 * then the server sent a partial pkt-line */
Packit Service 20376f
				giterr_set(GITERR_NET, "Incomplete pack data pkt-line");
Packit Service 20376f
				error = GIT_ERROR;
Packit Service 20376f
			}
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (error < 0) {
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&data_pkt_buf);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
Packit Service 20376f
{
Packit Service 20376f
	git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
Packit Service 20376f
	GITERR_CHECK_ALLOC(added);
Packit Service 20376f
Packit Service 20376f
	added->type = GIT_PKT_REF;
Packit Service 20376f
	git_oid_cpy(&added->head.oid, &push_spec->loid);
Packit Service 20376f
	added->head.name = git__strdup(push_spec->refspec.dst);
Packit Service 20376f
Packit Service 20376f
	if (!added->head.name ||
Packit Service 20376f
		git_vector_insert(refs, added) < 0) {
Packit Service 20376f
		git_pkt_free((git_pkt *)added);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int update_refs_from_report(
Packit Service 20376f
	git_vector *refs,
Packit Service 20376f
	git_vector *push_specs,
Packit Service 20376f
	git_vector *push_report)
Packit Service 20376f
{
Packit Service 20376f
	git_pkt_ref *ref;
Packit Service 20376f
	push_spec *push_spec;
Packit Service 20376f
	push_status *push_status;
Packit Service 20376f
	size_t i, j, refs_len;
Packit Service 20376f
	int cmp;
Packit Service 20376f
Packit Service 20376f
	/* For each push spec we sent to the server, we should have
Packit Service 20376f
	 * gotten back a status packet in the push report */
Packit Service 20376f
	if (push_specs->length != push_report->length) {
Packit Service 20376f
		giterr_set(GITERR_NET, "report-status: protocol error");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* We require that push_specs be sorted with push_spec_rref_cmp,
Packit Service 20376f
	 * and that push_report be sorted with push_status_ref_cmp */
Packit Service 20376f
	git_vector_sort(push_specs);
Packit Service 20376f
	git_vector_sort(push_report);
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(push_specs, i, push_spec) {
Packit Service 20376f
		push_status = git_vector_get(push_report, i);
Packit Service 20376f
Packit Service 20376f
		/* For each push spec we sent to the server, we should have
Packit Service 20376f
		 * gotten back a status packet in the push report which matches */
Packit Service 20376f
		if (strcmp(push_spec->refspec.dst, push_status->ref)) {
Packit Service 20376f
			giterr_set(GITERR_NET, "report-status: protocol error");
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* We require that refs be sorted with ref_name_cmp */
Packit Service 20376f
	git_vector_sort(refs);
Packit Service 20376f
	i = j = 0;
Packit Service 20376f
	refs_len = refs->length;
Packit Service 20376f
Packit Service 20376f
	/* Merge join push_specs with refs */
Packit Service 20376f
	while (i < push_specs->length && j < refs_len) {
Packit Service 20376f
		push_spec = git_vector_get(push_specs, i);
Packit Service 20376f
		push_status = git_vector_get(push_report, i);
Packit Service 20376f
		ref = git_vector_get(refs, j);
Packit Service 20376f
Packit Service 20376f
		cmp = strcmp(push_spec->refspec.dst, ref->head.name);
Packit Service 20376f
Packit Service 20376f
		/* Iterate appropriately */
Packit Service 20376f
		if (cmp <= 0) i++;
Packit Service 20376f
		if (cmp >= 0) j++;
Packit Service 20376f
Packit Service 20376f
		/* Add case */
Packit Service 20376f
		if (cmp < 0 &&
Packit Service 20376f
			!push_status->msg &&
Packit Service 20376f
			add_ref_from_push_spec(refs, push_spec) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		/* Update case, delete case */
Packit Service 20376f
		if (cmp == 0 &&
Packit Service 20376f
			!push_status->msg)
Packit Service 20376f
			git_oid_cpy(&ref->head.oid, &push_spec->loid);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	for (; i < push_specs->length; i++) {
Packit Service 20376f
		push_spec = git_vector_get(push_specs, i);
Packit Service 20376f
		push_status = git_vector_get(push_report, i);
Packit Service 20376f
Packit Service 20376f
		/* Add case */
Packit Service 20376f
		if (!push_status->msg &&
Packit Service 20376f
			add_ref_from_push_spec(refs, push_spec) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Remove any refs which we updated to have a zero OID. */
Packit Service 20376f
	git_vector_rforeach(refs, i, ref) {
Packit Service 20376f
		if (git_oid_iszero(&ref->head.oid)) {
Packit Service 20376f
			git_vector_remove(refs, i);
Packit Service 20376f
			git_pkt_free((git_pkt *)ref);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_sort(refs);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct push_packbuilder_payload
Packit Service 20376f
{
Packit Service 20376f
	git_smart_subtransport_stream *stream;
Packit Service 20376f
	git_packbuilder *pb;
Packit Service 20376f
	git_push_transfer_progress cb;
Packit Service 20376f
	void *cb_payload;
Packit Service 20376f
	size_t last_bytes;
Packit Service 20376f
	double last_progress_report_time;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int stream_thunk(void *buf, size_t size, void *data)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	struct push_packbuilder_payload *payload = data;
Packit Service 20376f
Packit Service 20376f
	if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if (payload->cb) {
Packit Service 20376f
		double current_time = git__timer();
Packit Service 20376f
		payload->last_bytes += size;
Packit Service 20376f
Packit Service 20376f
		if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
Packit Service 20376f
			payload->last_progress_report_time = current_time;
Packit Service 20376f
			error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs)
Packit Service 20376f
{
Packit Service 20376f
	transport_smart *t = (transport_smart *)transport;
Packit Service 20376f
	struct push_packbuilder_payload packbuilder_payload = {0};
Packit Service 20376f
	git_buf pktline = GIT_BUF_INIT;
Packit Service 20376f
	int error = 0, need_pack = 0;
Packit Service 20376f
	push_spec *spec;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
Packit Service 20376f
	packbuilder_payload.pb = push->pb;
Packit Service 20376f
Packit Service 20376f
	if (cbs && cbs->push_transfer_progress) {
Packit Service 20376f
		packbuilder_payload.cb = cbs->push_transfer_progress;
Packit Service 20376f
		packbuilder_payload.cb_payload = cbs->payload;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
#ifdef PUSH_DEBUG
Packit Service 20376f
{
Packit Service 20376f
	git_remote_head *head;
Packit Service 20376f
	char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&push->remote->refs, i, head) {
Packit Service 20376f
		git_oid_fmt(hex, &head->oid);
Packit Service 20376f
		fprintf(stderr, "%s (%s)\n", hex, head->name);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&push->specs, i, spec) {
Packit Service 20376f
		git_oid_fmt(hex, &spec->roid);
Packit Service 20376f
		fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
Packit Service 20376f
		git_oid_fmt(hex, &spec->loid);
Packit Service 20376f
		fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
Packit Service 20376f
			spec->rref : spec->lref);
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
#endif
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Figure out if we need to send a packfile; which is in all
Packit Service 20376f
	 * cases except when we only send delete commands
Packit Service 20376f
	 */
Packit Service 20376f
	git_vector_foreach(&push->specs, i, spec) {
Packit Service 20376f
		if (spec->refspec.src && spec->refspec.src[0] != '\0') {
Packit Service 20376f
			need_pack = 1;
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
Packit Service 20376f
		(error = gen_pktline(&pktline, push)) < 0 ||
Packit Service 20376f
		(error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (need_pack &&
Packit Service 20376f
		(error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* If we sent nothing or the server doesn't support report-status, then
Packit Service 20376f
	 * we consider the pack to have been unpacked successfully */
Packit Service 20376f
	if (!push->specs.length || !push->report_status)
Packit Service 20376f
		push->unpack_ok = 1;
Packit Service 20376f
	else if ((error = parse_report(t, push)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* If progress is being reported write the final report */
Packit Service 20376f
	if (cbs && cbs->push_transfer_progress) {
Packit Service 20376f
		error = cbs->push_transfer_progress(
Packit Service 20376f
					push->pb->nr_written,
Packit Service 20376f
					push->pb->nr_objects,
Packit Service 20376f
					packbuilder_payload.last_bytes,
Packit Service 20376f
					cbs->payload);
Packit Service 20376f
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (push->status.length) {
Packit Service 20376f
		error = update_refs_from_report(&t->refs, &push->specs, &push->status);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		error = git_smart__update_heads(t, NULL);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&pktline);
Packit Service 20376f
	return error;
Packit Service 20376f
}