Blame src/transports/local.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 "common.h"
Packit Service 20376f
#include "git2/types.h"
Packit Service 20376f
#include "git2/net.h"
Packit Service 20376f
#include "git2/repository.h"
Packit Service 20376f
#include "git2/object.h"
Packit Service 20376f
#include "git2/tag.h"
Packit Service 20376f
#include "git2/transport.h"
Packit Service 20376f
#include "git2/revwalk.h"
Packit Service 20376f
#include "git2/odb_backend.h"
Packit Service 20376f
#include "git2/pack.h"
Packit Service 20376f
#include "git2/commit.h"
Packit Service 20376f
#include "git2/revparse.h"
Packit Service 20376f
#include "pack-objects.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "posix.h"
Packit Service 20376f
#include "path.h"
Packit Service 20376f
#include "buffer.h"
Packit Service 20376f
#include "repository.h"
Packit Service 20376f
#include "odb.h"
Packit Service 20376f
#include "push.h"
Packit Service 20376f
#include "remote.h"
Packit Service 20376f
#include "proxy.h"
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_transport parent;
Packit Service 20376f
	git_remote *owner;
Packit Service 20376f
	char *url;
Packit Service 20376f
	int direction;
Packit Service 20376f
	int flags;
Packit Service 20376f
	git_atomic cancelled;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	git_transport_message_cb progress_cb;
Packit Service 20376f
	git_transport_message_cb error_cb;
Packit Service 20376f
	void *message_cb_payload;
Packit Service 20376f
	git_vector refs;
Packit Service 20376f
	unsigned connected : 1,
Packit Service 20376f
		have_refs : 1;
Packit Service 20376f
} transport_local;
Packit Service 20376f
Packit Service 20376f
static void free_head(git_remote_head *head)
Packit Service 20376f
{
Packit Service 20376f
	git__free(head->name);
Packit Service 20376f
	git__free(head->symref_target);
Packit Service 20376f
	git__free(head);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void free_heads(git_vector *heads)
Packit Service 20376f
{
Packit Service 20376f
	git_remote_head *head;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(heads, i, head)
Packit Service 20376f
		free_head(head);
Packit Service 20376f
Packit Service 20376f
	git_vector_free(heads);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int add_ref(transport_local *t, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	const char peeled[] = "^{}";
Packit Service 20376f
	git_reference *ref, *resolved;
Packit Service 20376f
	git_remote_head *head;
Packit Service 20376f
	git_oid obj_id;
Packit Service 20376f
	git_object *obj = NULL, *target = NULL;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	error = git_reference_resolve(&resolved, ref);
Packit Service 20376f
	if (error < 0) {
Packit Service 20376f
		git_reference_free(ref);
Packit Service 20376f
		if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
Packit Service 20376f
			/* This is actually okay.  Empty repos often have a HEAD that
Packit Service 20376f
			 * points to a nonexistent "refs/heads/master". */
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&obj_id, git_reference_target(resolved));
Packit Service 20376f
	git_reference_free(resolved);
Packit Service 20376f
Packit Service 20376f
	head = git__calloc(1, sizeof(git_remote_head));
Packit Service 20376f
	GITERR_CHECK_ALLOC(head);
Packit Service 20376f
Packit Service 20376f
	head->name = git__strdup(name);
Packit Service 20376f
	GITERR_CHECK_ALLOC(head->name);
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&head->oid, &obj_id);
Packit Service 20376f
Packit Service 20376f
	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
Packit Service 20376f
		head->symref_target = git__strdup(git_reference_symbolic_target(ref));
Packit Service 20376f
		GITERR_CHECK_ALLOC(head->symref_target);
Packit Service 20376f
	}
Packit Service 20376f
	git_reference_free(ref);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_vector_insert(&t->refs, head)) < 0) {
Packit Service 20376f
		free_head(head);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If it's not a tag, we don't need to try to peel it */
Packit Service 20376f
	if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	head = NULL;
Packit Service 20376f
Packit Service 20376f
	/* If it's not an annotated tag, or if we're mocking
Packit Service 20376f
	 * git-receive-pack, just get out */
Packit Service 20376f
	if (git_object_type(obj) != GIT_OBJ_TAG ||
Packit Service 20376f
		t->direction != GIT_DIRECTION_FETCH) {
Packit Service 20376f
		git_object_free(obj);
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* And if it's a tag, peel it, and add it to the list */
Packit Service 20376f
	head = git__calloc(1, sizeof(git_remote_head));
Packit Service 20376f
	GITERR_CHECK_ALLOC(head);
Packit Service 20376f
Packit Service 20376f
	if (git_buf_join(&buf, 0, name, peeled) < 0) {
Packit Service 20376f
		free_head(head);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
	head->name = git_buf_detach(&buf;;
Packit Service 20376f
Packit Service 20376f
	if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
Packit Service 20376f
		git_oid_cpy(&head->oid, git_object_id(target));
Packit Service 20376f
Packit Service 20376f
		if ((error = git_vector_insert(&t->refs, head)) < 0) {
Packit Service 20376f
			free_head(head);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_object_free(obj);
Packit Service 20376f
	git_object_free(target);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int store_refs(transport_local *t)
Packit Service 20376f
{
Packit Service 20376f
	size_t i;
Packit Service 20376f
	git_remote_head *head;
Packit Service 20376f
	git_strarray ref_names = {0};
Packit Service 20376f
Packit Service 20376f
	assert(t);
Packit Service 20376f
Packit Service 20376f
	if (git_reference_list(&ref_names, t->repo) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	/* Clear all heads we might have fetched in a previous connect */
Packit Service 20376f
	git_vector_foreach(&t->refs, i, head) {
Packit Service 20376f
		git__free(head->name);
Packit Service 20376f
		git__free(head);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Clear the vector so we can reuse it */
Packit Service 20376f
	git_vector_clear(&t->refs);
Packit Service 20376f
Packit Service 20376f
	/* Sort the references first */
Packit Service 20376f
	git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
Packit Service 20376f
Packit Service 20376f
	/* Add HEAD iff direction is fetch */
Packit Service 20376f
	if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < ref_names.count; ++i) {
Packit Service 20376f
		if (add_ref(t, ref_names.strings[i]) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	t->have_refs = 1;
Packit Service 20376f
	git_strarray_free(&ref_names);
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_vector_free(&t->refs);
Packit Service 20376f
	git_strarray_free(&ref_names);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Try to open the url as a git directory. The direction doesn't
Packit Service 20376f
 * matter in this case because we're calculating the heads ourselves.
Packit Service 20376f
 */
Packit Service 20376f
static int local_connect(
Packit Service 20376f
	git_transport *transport,
Packit Service 20376f
	const char *url,
Packit Service 20376f
	git_cred_acquire_cb cred_acquire_cb,
Packit Service 20376f
	void *cred_acquire_payload,
Packit Service 20376f
	const git_proxy_options *proxy,
Packit Service 20376f
	int direction, int flags)
Packit Service 20376f
{
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	int error;
Packit Service 20376f
	transport_local *t = (transport_local *) transport;
Packit Service 20376f
	const char *path;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(cred_acquire_cb);
Packit Service 20376f
	GIT_UNUSED(cred_acquire_payload);
Packit Service 20376f
	GIT_UNUSED(proxy);
Packit Service 20376f
Packit Service 20376f
	if (t->connected)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	free_heads(&t->refs);
Packit Service 20376f
Packit Service 20376f
	t->url = git__strdup(url);
Packit Service 20376f
	GITERR_CHECK_ALLOC(t->url);
Packit Service 20376f
	t->direction = direction;
Packit Service 20376f
	t->flags = flags;
Packit Service 20376f
Packit Service 20376f
	/* 'url' may be a url or path; convert to a path */
Packit Service 20376f
	if ((error = git_path_from_url_or_path(&buf, url)) < 0) {
Packit Service 20376f
		git_buf_free(&buf;;
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
	path = git_buf_cstr(&buf;;
Packit Service 20376f
Packit Service 20376f
	error = git_repository_open(&repo, path);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	t->repo = repo;
Packit Service 20376f
Packit Service 20376f
	if (store_refs(t) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	t->connected = 1;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	if (!t->have_refs) {
Packit Service 20376f
		giterr_set(GITERR_NET, "the transport has not yet loaded the refs");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = (const git_remote_head **)t->refs.contents;
Packit Service 20376f
	*size = t->refs.length;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_negotiate_fetch(
Packit Service 20376f
	git_transport *transport,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_remote_head * const *refs,
Packit Service 20376f
	size_t count)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local*)transport;
Packit Service 20376f
	git_remote_head *rhead;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(refs);
Packit Service 20376f
	GIT_UNUSED(count);
Packit Service 20376f
Packit Service 20376f
	/* Fill in the loids */
Packit Service 20376f
	git_vector_foreach(&t->refs, i, rhead) {
Packit Service 20376f
		git_object *obj;
Packit Service 20376f
Packit Service 20376f
		int error = git_revparse_single(&obj, repo, rhead->name);
Packit Service 20376f
		if (!error)
Packit Service 20376f
			git_oid_cpy(&rhead->loid, git_object_id(obj));
Packit Service 20376f
		else if (error != GIT_ENOTFOUND)
Packit Service 20376f
			return error;
Packit Service 20376f
		else
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
		git_object_free(obj);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_push_update_remote_ref(
Packit Service 20376f
	git_repository *remote_repo,
Packit Service 20376f
	const char *lref,
Packit Service 20376f
	const char *rref,
Packit Service 20376f
	git_oid *loid,
Packit Service 20376f
	git_oid *roid)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_reference *remote_ref = NULL;
Packit Service 20376f
Packit Service 20376f
	/* check for lhs, if it's empty it means to delete */
Packit Service 20376f
	if (lref[0] != '\0') {
Packit Service 20376f
		/* Create or update a ref */
Packit Service 20376f
		error = git_reference_create(NULL, remote_repo, rref, loid,
Packit Service 20376f
					     !git_oid_iszero(roid), NULL);
Packit Service 20376f
	} else {
Packit Service 20376f
		/* Delete a ref */
Packit Service 20376f
		if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
Packit Service 20376f
			if (error == GIT_ENOTFOUND)
Packit Service 20376f
				error = 0;
Packit Service 20376f
			return error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		error = git_reference_delete(remote_ref);
Packit Service 20376f
		git_reference_free(remote_ref);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int transfer_to_push_transfer(const git_transfer_progress *stats, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	const git_remote_callbacks *cbs = payload;
Packit Service 20376f
Packit Service 20376f
	if (!cbs || !cbs->push_transfer_progress)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes,
Packit Service 20376f
					   cbs->payload);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_push(
Packit Service 20376f
	git_transport *transport,
Packit Service 20376f
	git_push *push,
Packit Service 20376f
	const git_remote_callbacks *cbs)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
	git_repository *remote_repo = NULL;
Packit Service 20376f
	push_spec *spec;
Packit Service 20376f
	char *url = NULL;
Packit Service 20376f
	const char *path;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT, odb_path = GIT_BUF_INIT;
Packit Service 20376f
	int error;
Packit Service 20376f
	size_t j;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(cbs);
Packit Service 20376f
Packit Service 20376f
	/* 'push->remote->url' may be a url or path; convert to a path */
Packit Service 20376f
	if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) {
Packit Service 20376f
		git_buf_free(&buf;;
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
	path = git_buf_cstr(&buf;;
Packit Service 20376f
Packit Service 20376f
	error = git_repository_open(&remote_repo, path);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* We don't currently support pushing locally to non-bare repos. Proper
Packit Service 20376f
	   non-bare repo push support would require checking configs to see if
Packit Service 20376f
	   we should override the default 'don't let this happen' behavior.
Packit Service 20376f
Packit Service 20376f
	   Note that this is only an issue when pushing to the current branch,
Packit Service 20376f
	   but we forbid all pushes just in case */
Packit Service 20376f
	if (!remote_repo->is_bare) {
Packit Service 20376f
		error = GIT_EBAREREPO;
Packit Service 20376f
		giterr_set(GITERR_INVALID, "local push doesn't (yet) support pushing to non-bare repos.");
Packit Service 20376f
		goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
Packit Service 20376f
		|| (error = git_buf_joinpath(&odb_path, odb_path.ptr, "pack")) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
Packit Service 20376f
	git_buf_free(&odb_path);
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	push->unpack_ok = 1;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&push->specs, j, spec) {
Packit Service 20376f
		push_status *status;
Packit Service 20376f
		const git_error *last;
Packit Service 20376f
		char *ref = spec->refspec.dst;
Packit Service 20376f
Packit Service 20376f
		status = git__calloc(1, sizeof(push_status));
Packit Service 20376f
		if (!status)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		status->ref = git__strdup(ref);
Packit Service 20376f
		if (!status->ref) {
Packit Service 20376f
			git_push_status_free(status);
Packit Service 20376f
			goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
Packit Service 20376f
			&spec->loid, &spec->roid);
Packit Service 20376f
Packit Service 20376f
		switch (error) {
Packit Service 20376f
			case GIT_OK:
Packit Service 20376f
				break;
Packit Service 20376f
			case GIT_EINVALIDSPEC:
Packit Service 20376f
				status->msg = git__strdup("funny refname");
Packit Service 20376f
				break;
Packit Service 20376f
			case GIT_ENOTFOUND:
Packit Service 20376f
				status->msg = git__strdup("Remote branch not found to delete");
Packit Service 20376f
				break;
Packit Service 20376f
			default:
Packit Service 20376f
				last = giterr_last();
Packit Service 20376f
Packit Service 20376f
				if (last && last->message)
Packit Service 20376f
					status->msg = git__strdup(last->message);
Packit Service 20376f
				else
Packit Service 20376f
					status->msg = git__strdup("Unspecified error encountered");
Packit Service 20376f
				break;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* failed to allocate memory for a status message */
Packit Service 20376f
		if (error < 0 && !status->msg) {
Packit Service 20376f
			git_push_status_free(status);
Packit Service 20376f
			goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* failed to insert the ref update status */
Packit Service 20376f
		if ((error = git_vector_insert(&push->status, status)) < 0) {
Packit Service 20376f
			git_push_status_free(status);
Packit Service 20376f
			goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (push->specs.length) {
Packit Service 20376f
		int flags = t->flags;
Packit Service 20376f
		url = git__strdup(t->url);
Packit Service 20376f
Packit Service 20376f
		if (!url || t->parent.close(&t->parent) < 0 ||
Packit Service 20376f
			t->parent.connect(&t->parent, url,
Packit Service 20376f
			NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
Packit Service 20376f
			goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_repository_free(remote_repo);
Packit Service 20376f
	git__free(url);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct foreach_data {
Packit Service 20376f
	git_transfer_progress *stats;
Packit Service 20376f
	git_transfer_progress_cb progress_cb;
Packit Service 20376f
	void *progress_payload;
Packit Service 20376f
	git_odb_writepack *writepack;
Packit Service 20376f
} foreach_data;
Packit Service 20376f
Packit Service 20376f
static int foreach_cb(void *buf, size_t len, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	foreach_data *data = (foreach_data*)payload;
Packit Service 20376f
Packit Service 20376f
	data->stats->received_bytes += len;
Packit Service 20376f
	return data->writepack->append(data->writepack, buf, len, data->stats);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static const char *counting_objects_fmt = "Counting objects %d\r";
Packit Service 20376f
static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)";
Packit Service 20376f
Packit Service 20376f
static int local_counting(int stage, unsigned int current, unsigned int total, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	git_buf progress_info = GIT_BUF_INIT;
Packit Service 20376f
	transport_local *t = payload;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (!t->progress_cb)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
Packit Service 20376f
		git_buf_printf(&progress_info, counting_objects_fmt, current);
Packit Service 20376f
	} else if (stage == GIT_PACKBUILDER_DELTAFICATION) {
Packit Service 20376f
		float perc = (((float) current) / total) * 100;
Packit Service 20376f
		git_buf_printf(&progress_info, compressing_objects_fmt, perc, current, total);
Packit Service 20376f
		if (current == total)
Packit Service 20376f
			git_buf_printf(&progress_info, ", done\n");
Packit Service 20376f
		else
Packit Service 20376f
			git_buf_putc(&progress_info, '\r');
Packit Service 20376f
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_buf_oom(&progress_info))
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload);
Packit Service 20376f
	git_buf_free(&progress_info);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_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 progress_cb,
Packit Service 20376f
		void *progress_payload)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local*)transport;
Packit Service 20376f
	git_revwalk *walk = NULL;
Packit Service 20376f
	git_remote_head *rhead;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
	git_packbuilder *pack = NULL;
Packit Service 20376f
	git_odb_writepack *writepack = NULL;
Packit Service 20376f
	git_odb *odb = NULL;
Packit Service 20376f
	git_buf progress_info = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_revwalk_new(&walk, t->repo)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	git_revwalk_sorting(walk, GIT_SORT_TIME);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	git_packbuilder_set_callbacks(pack, local_counting, t);
Packit Service 20376f
Packit Service 20376f
	stats->total_objects = 0;
Packit Service 20376f
	stats->indexed_objects = 0;
Packit Service 20376f
	stats->received_objects = 0;
Packit Service 20376f
	stats->received_bytes = 0;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&t->refs, i, rhead) {
Packit Service 20376f
		git_object *obj;
Packit Service 20376f
		if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJ_ANY)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		if (git_object_type(obj) == GIT_OBJ_COMMIT) {
Packit Service 20376f
			/* Revwalker includes only wanted commits */
Packit Service 20376f
			error = git_revwalk_push(walk, &rhead->oid);
Packit Service 20376f
			if (!error && !git_oid_iszero(&rhead->loid)) {
Packit Service 20376f
				error = git_revwalk_hide(walk, &rhead->loid);
Packit Service 20376f
				if (error == GIT_ENOTFOUND)
Packit Service 20376f
					error = 0;
Packit Service 20376f
			}
Packit Service 20376f
		} else {
Packit Service 20376f
			/* Tag or some other wanted object. Add it on its own */
Packit Service 20376f
			error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
Packit Service 20376f
		}
Packit Service 20376f
		git_object_free(obj);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_packbuilder_insert_walk(pack, walk)))
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (t->progress_cb &&
Packit Service 20376f
	    (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* Walk the objects, building a packfile */
Packit Service 20376f
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* One last one with the newline */
Packit Service 20376f
	git_buf_clear(&progress_info);
Packit Service 20376f
	git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack));
Packit Service 20376f
	if ((error = git_buf_putc(&progress_info, '\n')) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (t->progress_cb &&
Packit Service 20376f
	    (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* Write the data to the ODB */
Packit Service 20376f
	{
Packit Service 20376f
		foreach_data data = {0};
Packit Service 20376f
		data.stats = stats;
Packit Service 20376f
		data.progress_cb = progress_cb;
Packit Service 20376f
		data.progress_payload = progress_payload;
Packit Service 20376f
		data.writepack = writepack;
Packit Service 20376f
Packit Service 20376f
		/* autodetect */
Packit Service 20376f
		git_packbuilder_set_threads(pack, 0);
Packit Service 20376f
Packit Service 20376f
		if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = writepack->commit(writepack, stats);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (writepack) writepack->free(writepack);
Packit Service 20376f
	git_buf_free(&progress_info);
Packit Service 20376f
	git_packbuilder_free(pack);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_set_callbacks(
Packit Service 20376f
	git_transport *transport,
Packit Service 20376f
	git_transport_message_cb progress_cb,
Packit Service 20376f
	git_transport_message_cb error_cb,
Packit Service 20376f
	git_transport_certificate_check_cb certificate_check_cb,
Packit Service 20376f
	void *message_cb_payload)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(certificate_check_cb);
Packit Service 20376f
Packit Service 20376f
	t->progress_cb = progress_cb;
Packit Service 20376f
	t->error_cb = error_cb;
Packit Service 20376f
	t->message_cb_payload = message_cb_payload;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_is_connected(git_transport *transport)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	return t->connected;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_read_flags(git_transport *transport, int *flags)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	*flags = t->flags;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void local_cancel(git_transport *transport)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	git_atomic_set(&t->cancelled, 1);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int local_close(git_transport *transport)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	t->connected = 0;
Packit Service 20376f
Packit Service 20376f
	if (t->repo) {
Packit Service 20376f
		git_repository_free(t->repo);
Packit Service 20376f
		t->repo = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (t->url) {
Packit Service 20376f
		git__free(t->url);
Packit Service 20376f
		t->url = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void local_free(git_transport *transport)
Packit Service 20376f
{
Packit Service 20376f
	transport_local *t = (transport_local *)transport;
Packit Service 20376f
Packit Service 20376f
	free_heads(&t->refs);
Packit Service 20376f
Packit Service 20376f
	/* Close the transport, if it's still open. */
Packit Service 20376f
	local_close(transport);
Packit Service 20376f
Packit Service 20376f
	/* Free the transport */
Packit Service 20376f
	git__free(t);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**************
Packit Service 20376f
 * Public API *
Packit Service 20376f
 **************/
Packit Service 20376f
Packit Service 20376f
int git_transport_local(git_transport **out, git_remote *owner, void *param)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	transport_local *t;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(param);
Packit Service 20376f
Packit Service 20376f
	t = git__calloc(1, sizeof(transport_local));
Packit Service 20376f
	GITERR_CHECK_ALLOC(t);
Packit Service 20376f
Packit Service 20376f
	t->parent.version = GIT_TRANSPORT_VERSION;
Packit Service 20376f
	t->parent.set_callbacks = local_set_callbacks;
Packit Service 20376f
	t->parent.connect = local_connect;
Packit Service 20376f
	t->parent.negotiate_fetch = local_negotiate_fetch;
Packit Service 20376f
	t->parent.download_pack = local_download_pack;
Packit Service 20376f
	t->parent.push = local_push;
Packit Service 20376f
	t->parent.close = local_close;
Packit Service 20376f
	t->parent.free = local_free;
Packit Service 20376f
	t->parent.ls = local_ls;
Packit Service 20376f
	t->parent.is_connected = local_is_connected;
Packit Service 20376f
	t->parent.read_flags = local_read_flags;
Packit Service 20376f
	t->parent.cancel = local_cancel;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
Packit Service 20376f
		git__free(t);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	t->owner = owner;
Packit Service 20376f
Packit Service 20376f
	*out = (git_transport *) t;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}