Commit b70bf922 by Vicent Martí

Merge pull request #1406 from cpthamilton/local_push

Implemented push on the local transport
parents eef7e80e 20858f6e
......@@ -83,18 +83,6 @@ static void free_refspec(push_spec *spec)
git__free(spec);
}
static void free_status(push_status *status)
{
if (status == NULL)
return;
if (status->msg)
git__free(status->msg);
git__free(status->ref);
git__free(status);
}
static int check_rref(char *ref)
{
if (git__prefixcmp(ref, "refs/")) {
......@@ -225,8 +213,11 @@ int git_push_update_tips(git_push *push)
error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name));
if (!error) {
if ((error = git_reference_delete(remote_ref)) < 0)
if ((error = git_reference_delete(remote_ref)) < 0) {
git_reference_free(remote_ref);
goto on_error;
}
git_reference_free(remote_ref);
} else if (error == GIT_ENOTFOUND)
giterr_clear();
else
......@@ -526,6 +517,18 @@ int git_push_status_foreach(git_push *push,
return 0;
}
void git_push_status_free(push_status *status)
{
if (status == NULL)
return;
if (status->msg)
git__free(status->msg);
git__free(status->ref);
git__free(status);
}
void git_push_free(git_push *push)
{
push_spec *spec;
......@@ -541,7 +544,7 @@ void git_push_free(git_push *push)
git_vector_free(&push->specs);
git_vector_foreach(&push->status, i, status) {
free_status(status);
git_push_status_free(status);
}
git_vector_free(&push->status);
......
......@@ -41,4 +41,11 @@ struct git_push {
unsigned pb_parallelism;
};
/**
* Free the given push status object
*
* @param status The push status object
*/
void git_push_status_free(push_status *status);
#endif
......@@ -16,6 +16,7 @@
#include "git2/pack.h"
#include "git2/commit.h"
#include "git2/revparse.h"
#include "git2/push.h"
#include "pack-objects.h"
#include "refs.h"
#include "posix.h"
......@@ -23,6 +24,8 @@
#include "buffer.h"
#include "repository.h"
#include "odb.h"
#include "push.h"
#include "remote.h"
typedef struct {
git_transport parent;
......@@ -79,8 +82,10 @@ static int add_ref(transport_local *t, const char *name)
head = NULL;
/* If it's not an annotated tag, just get out */
if (git_object_type(obj) != GIT_OBJ_TAG) {
/* If it's not an annotated tag, or if we're mocking
* git-receive-pack, just get out */
if (git_object_type(obj) != GIT_OBJ_TAG ||
t->direction != GIT_DIRECTION_FETCH) {
git_object_free(obj);
return 0;
}
......@@ -125,8 +130,8 @@ static int store_refs(transport_local *t)
/* Sort the references first */
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
/* Add HEAD */
if (add_ref(t, GIT_HEAD_FILE) < 0)
/* Add HEAD iff direction is fetch */
if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
goto on_error;
for (i = 0; i < ref_names.count; ++i) {
......@@ -245,6 +250,191 @@ static int local_negotiate_fetch(
return 0;
}
static int local_push_copy_object(
git_odb *local_odb,
git_odb *remote_odb,
git_pobject *obj)
{
int error = 0;
git_odb_object *odb_obj = NULL;
git_odb_stream *odb_stream;
size_t odb_obj_size;
git_otype odb_obj_type;
git_oid remote_odb_obj_oid;
/* Object already exists in the remote ODB; do nothing and return 0*/
if (git_odb_exists(remote_odb, &obj->id))
return 0;
if ((error = git_odb_read(&odb_obj, local_odb, &obj->id)) < 0)
return error;
odb_obj_size = git_odb_object_size(odb_obj);
odb_obj_type = git_odb_object_type(odb_obj);
if ((error = git_odb_open_wstream(&odb_stream, remote_odb,
odb_obj_size, odb_obj_type)) < 0)
goto on_error;
if (odb_stream->write(odb_stream, (char *)git_odb_object_data(odb_obj),
odb_obj_size) < 0 ||
odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
error = -1;
} else if (git_oid_cmp(&obj->id, &remote_odb_obj_oid) != 0) {
giterr_set(GITERR_ODB, "Error when writing object to remote odb "
"during local push operation. Remote odb object oid does not "
"match local oid.");
error = -1;
}
odb_stream->free(odb_stream);
on_error:
git_odb_object_free(odb_obj);
return error;
}
static int local_push_update_remote_ref(
git_repository *remote_repo,
const char *lref,
const char *rref,
git_oid *loid,
git_oid *roid)
{
int error;
git_reference *remote_ref = NULL;
/* rref will be NULL if it is implicit in the pushspec (e.g. 'b1:') */
rref = rref ? rref : lref;
if (lref) {
/* Create or update a ref */
if ((error = git_reference_create(NULL, remote_repo, rref, loid,
!git_oid_iszero(roid))) < 0)
return error;
} else {
/* Delete a ref */
if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
if (error == GIT_ENOTFOUND)
error = 0;
return error;
}
if ((error = git_reference_delete(remote_ref)) < 0)
return error;
git_reference_free(remote_ref);
}
return 0;
}
static int local_push(
git_transport *transport,
git_push *push)
{
transport_local *t = (transport_local *)transport;
git_odb *remote_odb = NULL;
git_odb *local_odb = NULL;
git_repository *remote_repo = NULL;
push_spec *spec;
char *url = NULL;
int error;
unsigned int i;
size_t j;
if ((error = git_repository_open(&remote_repo, push->remote->url)) < 0)
return error;
/* We don't currently support pushing locally to non-bare repos. Proper
non-bare repo push support would require checking configs to see if
we should override the default 'don't let this happen' behavior */
if (!remote_repo->is_bare) {
error = -1;
goto on_error;
}
if ((error = git_repository_odb__weakptr(&remote_odb, remote_repo)) < 0 ||
(error = git_repository_odb__weakptr(&local_odb, push->repo)) < 0)
goto on_error;
for (i = 0; i < push->pb->nr_objects; i++) {
if ((error = local_push_copy_object(local_odb, remote_odb,
&push->pb->object_list[i])) < 0)
goto on_error;
}
push->unpack_ok = 1;
git_vector_foreach(&push->specs, j, spec) {
push_status *status;
const git_error *last;
char *ref = spec->rref ? spec->rref : spec->lref;
status = git__calloc(sizeof(push_status), 1);
if (!status)
goto on_error;
status->ref = git__strdup(ref);
if (!status->ref) {
git_push_status_free(status);
goto on_error;
}
error = local_push_update_remote_ref(remote_repo, spec->lref, spec->rref,
&spec->loid, &spec->roid);
switch (error) {
case GIT_OK:
break;
case GIT_EINVALIDSPEC:
status->msg = git__strdup("funny refname");
break;
case GIT_ENOTFOUND:
status->msg = git__strdup("Remote branch not found to delete");
break;
default:
last = giterr_last();
if (last && last->message)
status->msg = git__strdup(last->message);
else
status->msg = git__strdup("Unspecified error encountered");
break;
}
/* failed to allocate memory for a status message */
if (error < 0 && !status->msg) {
git_push_status_free(status);
goto on_error;
}
/* failed to insert the ref update status */
if ((error = git_vector_insert(&push->status, status)) < 0) {
git_push_status_free(status);
goto on_error;
}
}
if (push->specs.length) {
int flags = t->flags;
url = git__strdup(t->url);
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error;
}
error = 0;
on_error:
git_repository_free(remote_repo);
git__free(url);
return error;
}
typedef struct foreach_data {
git_transfer_progress *stats;
git_transfer_progress_callback progress_cb;
......@@ -379,30 +569,39 @@ static void local_cancel(git_transport *transport)
static int local_close(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
size_t i;
git_remote_head *head;
t->connected = 0;
if (t->repo) {
git_repository_free(t->repo);
t->repo = NULL;
}
git_vector_foreach(&t->refs, i, head) {
git__free(head->name);
git__free(head);
}
git_vector_free(&t->refs);
if (t->url) {
git__free(t->url);
t->url = NULL;
}
return 0;
}
static void local_free(git_transport *transport)
{
unsigned int i;
transport_local *t = (transport_local *) transport;
git_vector *vec = &t->refs;
git_remote_head *head;
assert(transport);
transport_local *t = (transport_local *)transport;
git_vector_foreach (vec, i, head) {
git__free(head->name);
git__free(head);
}
git_vector_free(vec);
/* Close the transport, if it's still open. */
local_close(transport);
git__free(t->url);
/* Free the transport */
git__free(t);
}
......@@ -423,6 +622,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.connect = local_connect;
t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack;
t->parent.push = local_push;
t->parent.close = local_close;
t->parent.free = local_free;
t->parent.ls = local_ls;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment