|
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 |
}
|