Skip to content

Commit b70bf92

Browse files
author
Vicent Martí
committed
Merge pull request nodegit#1406 from cpthamilton/local_push
Implemented push on the local transport
2 parents eef7e80 + 20858f6 commit b70bf92

3 files changed

Lines changed: 242 additions & 32 deletions

File tree

src/push.c

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,6 @@ static void free_refspec(push_spec *spec)
8383
git__free(spec);
8484
}
8585

86-
static void free_status(push_status *status)
87-
{
88-
if (status == NULL)
89-
return;
90-
91-
if (status->msg)
92-
git__free(status->msg);
93-
94-
git__free(status->ref);
95-
git__free(status);
96-
}
97-
9886
static int check_rref(char *ref)
9987
{
10088
if (git__prefixcmp(ref, "refs/")) {
@@ -225,8 +213,11 @@ int git_push_update_tips(git_push *push)
225213
error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name));
226214

227215
if (!error) {
228-
if ((error = git_reference_delete(remote_ref)) < 0)
216+
if ((error = git_reference_delete(remote_ref)) < 0) {
217+
git_reference_free(remote_ref);
229218
goto on_error;
219+
}
220+
git_reference_free(remote_ref);
230221
} else if (error == GIT_ENOTFOUND)
231222
giterr_clear();
232223
else
@@ -526,6 +517,18 @@ int git_push_status_foreach(git_push *push,
526517
return 0;
527518
}
528519

520+
void git_push_status_free(push_status *status)
521+
{
522+
if (status == NULL)
523+
return;
524+
525+
if (status->msg)
526+
git__free(status->msg);
527+
528+
git__free(status->ref);
529+
git__free(status);
530+
}
531+
529532
void git_push_free(git_push *push)
530533
{
531534
push_spec *spec;
@@ -541,7 +544,7 @@ void git_push_free(git_push *push)
541544
git_vector_free(&push->specs);
542545

543546
git_vector_foreach(&push->status, i, status) {
544-
free_status(status);
547+
git_push_status_free(status);
545548
}
546549
git_vector_free(&push->status);
547550

src/push.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,11 @@ struct git_push {
4141
unsigned pb_parallelism;
4242
};
4343

44+
/**
45+
* Free the given push status object
46+
*
47+
* @param status The push status object
48+
*/
49+
void git_push_status_free(push_status *status);
50+
4451
#endif

src/transports/local.c

Lines changed: 218 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
#include "git2/pack.h"
1717
#include "git2/commit.h"
1818
#include "git2/revparse.h"
19+
#include "git2/push.h"
1920
#include "pack-objects.h"
2021
#include "refs.h"
2122
#include "posix.h"
2223
#include "path.h"
2324
#include "buffer.h"
2425
#include "repository.h"
2526
#include "odb.h"
27+
#include "push.h"
28+
#include "remote.h"
2629

2730
typedef struct {
2831
git_transport parent;
@@ -79,8 +82,10 @@ static int add_ref(transport_local *t, const char *name)
7982

8083
head = NULL;
8184

82-
/* If it's not an annotated tag, just get out */
83-
if (git_object_type(obj) != GIT_OBJ_TAG) {
85+
/* If it's not an annotated tag, or if we're mocking
86+
* git-receive-pack, just get out */
87+
if (git_object_type(obj) != GIT_OBJ_TAG ||
88+
t->direction != GIT_DIRECTION_FETCH) {
8489
git_object_free(obj);
8590
return 0;
8691
}
@@ -125,8 +130,8 @@ static int store_refs(transport_local *t)
125130
/* Sort the references first */
126131
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
127132

128-
/* Add HEAD */
129-
if (add_ref(t, GIT_HEAD_FILE) < 0)
133+
/* Add HEAD iff direction is fetch */
134+
if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
130135
goto on_error;
131136

132137
for (i = 0; i < ref_names.count; ++i) {
@@ -245,6 +250,191 @@ static int local_negotiate_fetch(
245250
return 0;
246251
}
247252

253+
static int local_push_copy_object(
254+
git_odb *local_odb,
255+
git_odb *remote_odb,
256+
git_pobject *obj)
257+
{
258+
int error = 0;
259+
git_odb_object *odb_obj = NULL;
260+
git_odb_stream *odb_stream;
261+
size_t odb_obj_size;
262+
git_otype odb_obj_type;
263+
git_oid remote_odb_obj_oid;
264+
265+
/* Object already exists in the remote ODB; do nothing and return 0*/
266+
if (git_odb_exists(remote_odb, &obj->id))
267+
return 0;
268+
269+
if ((error = git_odb_read(&odb_obj, local_odb, &obj->id)) < 0)
270+
return error;
271+
272+
odb_obj_size = git_odb_object_size(odb_obj);
273+
odb_obj_type = git_odb_object_type(odb_obj);
274+
275+
if ((error = git_odb_open_wstream(&odb_stream, remote_odb,
276+
odb_obj_size, odb_obj_type)) < 0)
277+
goto on_error;
278+
279+
if (odb_stream->write(odb_stream, (char *)git_odb_object_data(odb_obj),
280+
odb_obj_size) < 0 ||
281+
odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
282+
error = -1;
283+
} else if (git_oid_cmp(&obj->id, &remote_odb_obj_oid) != 0) {
284+
giterr_set(GITERR_ODB, "Error when writing object to remote odb "
285+
"during local push operation. Remote odb object oid does not "
286+
"match local oid.");
287+
error = -1;
288+
}
289+
290+
odb_stream->free(odb_stream);
291+
292+
on_error:
293+
git_odb_object_free(odb_obj);
294+
return error;
295+
}
296+
297+
static int local_push_update_remote_ref(
298+
git_repository *remote_repo,
299+
const char *lref,
300+
const char *rref,
301+
git_oid *loid,
302+
git_oid *roid)
303+
{
304+
int error;
305+
git_reference *remote_ref = NULL;
306+
307+
/* rref will be NULL if it is implicit in the pushspec (e.g. 'b1:') */
308+
rref = rref ? rref : lref;
309+
310+
if (lref) {
311+
/* Create or update a ref */
312+
if ((error = git_reference_create(NULL, remote_repo, rref, loid,
313+
!git_oid_iszero(roid))) < 0)
314+
return error;
315+
} else {
316+
/* Delete a ref */
317+
if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
318+
if (error == GIT_ENOTFOUND)
319+
error = 0;
320+
return error;
321+
}
322+
323+
if ((error = git_reference_delete(remote_ref)) < 0)
324+
return error;
325+
326+
git_reference_free(remote_ref);
327+
}
328+
329+
return 0;
330+
}
331+
332+
static int local_push(
333+
git_transport *transport,
334+
git_push *push)
335+
{
336+
transport_local *t = (transport_local *)transport;
337+
git_odb *remote_odb = NULL;
338+
git_odb *local_odb = NULL;
339+
git_repository *remote_repo = NULL;
340+
push_spec *spec;
341+
char *url = NULL;
342+
int error;
343+
unsigned int i;
344+
size_t j;
345+
346+
if ((error = git_repository_open(&remote_repo, push->remote->url)) < 0)
347+
return error;
348+
349+
/* We don't currently support pushing locally to non-bare repos. Proper
350+
non-bare repo push support would require checking configs to see if
351+
we should override the default 'don't let this happen' behavior */
352+
if (!remote_repo->is_bare) {
353+
error = -1;
354+
goto on_error;
355+
}
356+
357+
if ((error = git_repository_odb__weakptr(&remote_odb, remote_repo)) < 0 ||
358+
(error = git_repository_odb__weakptr(&local_odb, push->repo)) < 0)
359+
goto on_error;
360+
361+
for (i = 0; i < push->pb->nr_objects; i++) {
362+
if ((error = local_push_copy_object(local_odb, remote_odb,
363+
&push->pb->object_list[i])) < 0)
364+
goto on_error;
365+
}
366+
367+
push->unpack_ok = 1;
368+
369+
git_vector_foreach(&push->specs, j, spec) {
370+
push_status *status;
371+
const git_error *last;
372+
char *ref = spec->rref ? spec->rref : spec->lref;
373+
374+
status = git__calloc(sizeof(push_status), 1);
375+
if (!status)
376+
goto on_error;
377+
378+
status->ref = git__strdup(ref);
379+
if (!status->ref) {
380+
git_push_status_free(status);
381+
goto on_error;
382+
}
383+
384+
error = local_push_update_remote_ref(remote_repo, spec->lref, spec->rref,
385+
&spec->loid, &spec->roid);
386+
387+
switch (error) {
388+
case GIT_OK:
389+
break;
390+
case GIT_EINVALIDSPEC:
391+
status->msg = git__strdup("funny refname");
392+
break;
393+
case GIT_ENOTFOUND:
394+
status->msg = git__strdup("Remote branch not found to delete");
395+
break;
396+
default:
397+
last = giterr_last();
398+
399+
if (last && last->message)
400+
status->msg = git__strdup(last->message);
401+
else
402+
status->msg = git__strdup("Unspecified error encountered");
403+
break;
404+
}
405+
406+
/* failed to allocate memory for a status message */
407+
if (error < 0 && !status->msg) {
408+
git_push_status_free(status);
409+
goto on_error;
410+
}
411+
412+
/* failed to insert the ref update status */
413+
if ((error = git_vector_insert(&push->status, status)) < 0) {
414+
git_push_status_free(status);
415+
goto on_error;
416+
}
417+
}
418+
419+
if (push->specs.length) {
420+
int flags = t->flags;
421+
url = git__strdup(t->url);
422+
423+
if (!url || t->parent.close(&t->parent) < 0 ||
424+
t->parent.connect(&t->parent, url,
425+
push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
426+
goto on_error;
427+
}
428+
429+
error = 0;
430+
431+
on_error:
432+
git_repository_free(remote_repo);
433+
git__free(url);
434+
435+
return error;
436+
}
437+
248438
typedef struct foreach_data {
249439
git_transfer_progress *stats;
250440
git_transfer_progress_callback progress_cb;
@@ -379,30 +569,39 @@ static void local_cancel(git_transport *transport)
379569
static int local_close(git_transport *transport)
380570
{
381571
transport_local *t = (transport_local *)transport;
572+
size_t i;
573+
git_remote_head *head;
382574

383575
t->connected = 0;
384-
git_repository_free(t->repo);
385-
t->repo = NULL;
576+
577+
if (t->repo) {
578+
git_repository_free(t->repo);
579+
t->repo = NULL;
580+
}
581+
582+
git_vector_foreach(&t->refs, i, head) {
583+
git__free(head->name);
584+
git__free(head);
585+
}
586+
587+
git_vector_free(&t->refs);
588+
589+
if (t->url) {
590+
git__free(t->url);
591+
t->url = NULL;
592+
}
386593

387594
return 0;
388595
}
389596

390597
static void local_free(git_transport *transport)
391598
{
392-
unsigned int i;
393-
transport_local *t = (transport_local *) transport;
394-
git_vector *vec = &t->refs;
395-
git_remote_head *head;
396-
397-
assert(transport);
599+
transport_local *t = (transport_local *)transport;
398600

399-
git_vector_foreach (vec, i, head) {
400-
git__free(head->name);
401-
git__free(head);
402-
}
403-
git_vector_free(vec);
601+
/* Close the transport, if it's still open. */
602+
local_close(transport);
404603

405-
git__free(t->url);
604+
/* Free the transport */
406605
git__free(t);
407606
}
408607

@@ -423,6 +622,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
423622
t->parent.connect = local_connect;
424623
t->parent.negotiate_fetch = local_negotiate_fetch;
425624
t->parent.download_pack = local_download_pack;
625+
t->parent.push = local_push;
426626
t->parent.close = local_close;
427627
t->parent.free = local_free;
428628
t->parent.ls = local_ls;

0 commit comments

Comments
 (0)