2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
10 #include "git2/types.h"
12 #include "git2/repository.h"
13 #include "git2/object.h"
15 #include "git2/transport.h"
16 #include "git2/revwalk.h"
17 #include "git2/odb_backend.h"
18 #include "git2/pack.h"
19 #include "git2/commit.h"
20 #include "git2/revparse.h"
22 #include "pack-objects.h"
27 #include "repository.h"
41 git_transport_message_cb progress_cb
;
42 git_transport_message_cb error_cb
;
43 void *message_cb_payload
;
45 unsigned connected
: 1,
49 static void free_head(git_remote_head
*head
)
51 git__free(head
->name
);
52 git__free(head
->symref_target
);
56 static void free_heads(git_vector
*heads
)
58 git_remote_head
*head
;
61 git_vector_foreach(heads
, i
, head
)
64 git_vector_free(heads
);
67 static int add_ref(transport_local
*t
, const char *name
)
69 const char peeled
[] = "^{}";
70 git_reference
*ref
, *resolved
;
71 git_remote_head
*head
;
73 git_object
*obj
= NULL
, *target
= NULL
;
74 git_buf buf
= GIT_BUF_INIT
;
77 if ((error
= git_reference_lookup(&ref
, t
->repo
, name
)) < 0)
80 error
= git_reference_resolve(&resolved
, ref
);
82 git_reference_free(ref
);
83 if (!strcmp(name
, GIT_HEAD_FILE
) && error
== GIT_ENOTFOUND
) {
84 /* This is actually okay. Empty repos often have a HEAD that
85 * points to a nonexistent "refs/heads/master". */
92 git_oid_cpy(&obj_id
, git_reference_target(resolved
));
93 git_reference_free(resolved
);
95 head
= git__calloc(1, sizeof(git_remote_head
));
96 GIT_ERROR_CHECK_ALLOC(head
);
98 head
->name
= git__strdup(name
);
99 GIT_ERROR_CHECK_ALLOC(head
->name
);
101 git_oid_cpy(&head
->oid
, &obj_id
);
103 if (git_reference_type(ref
) == GIT_REFERENCE_SYMBOLIC
) {
104 head
->symref_target
= git__strdup(git_reference_symbolic_target(ref
));
105 GIT_ERROR_CHECK_ALLOC(head
->symref_target
);
107 git_reference_free(ref
);
109 if ((error
= git_vector_insert(&t
->refs
, head
)) < 0) {
114 /* If it's not a tag, we don't need to try to peel it */
115 if (git__prefixcmp(name
, GIT_REFS_TAGS_DIR
))
118 if ((error
= git_object_lookup(&obj
, t
->repo
, &head
->oid
, GIT_OBJECT_ANY
)) < 0)
123 /* If it's not an annotated tag, or if we're mocking
124 * git-receive-pack, just get out */
125 if (git_object_type(obj
) != GIT_OBJECT_TAG
||
126 t
->direction
!= GIT_DIRECTION_FETCH
) {
127 git_object_free(obj
);
131 /* And if it's a tag, peel it, and add it to the list */
132 head
= git__calloc(1, sizeof(git_remote_head
));
133 GIT_ERROR_CHECK_ALLOC(head
);
135 if (git_buf_join(&buf
, 0, name
, peeled
) < 0) {
139 head
->name
= git_buf_detach(&buf
);
141 if (!(error
= git_tag_peel(&target
, (git_tag
*)obj
))) {
142 git_oid_cpy(&head
->oid
, git_object_id(target
));
144 if ((error
= git_vector_insert(&t
->refs
, head
)) < 0) {
149 git_object_free(obj
);
150 git_object_free(target
);
155 static int store_refs(transport_local
*t
)
158 git_remote_head
*head
;
159 git_strarray ref_names
= {0};
163 if (git_reference_list(&ref_names
, t
->repo
) < 0)
166 /* Clear all heads we might have fetched in a previous connect */
167 git_vector_foreach(&t
->refs
, i
, head
) {
168 git__free(head
->name
);
172 /* Clear the vector so we can reuse it */
173 git_vector_clear(&t
->refs
);
175 /* Sort the references first */
176 git__tsort((void **)ref_names
.strings
, ref_names
.count
, &git__strcmp_cb
);
178 /* Add HEAD iff direction is fetch */
179 if (t
->direction
== GIT_DIRECTION_FETCH
&& add_ref(t
, GIT_HEAD_FILE
) < 0)
182 for (i
= 0; i
< ref_names
.count
; ++i
) {
183 if (add_ref(t
, ref_names
.strings
[i
]) < 0)
188 git_strarray_dispose(&ref_names
);
192 git_vector_free(&t
->refs
);
193 git_strarray_dispose(&ref_names
);
198 * Try to open the url as a git directory. The direction doesn't
199 * matter in this case because we're calculating the heads ourselves.
201 static int local_connect(
202 git_transport
*transport
,
204 git_credential_acquire_cb cred_acquire_cb
,
205 void *cred_acquire_payload
,
206 const git_proxy_options
*proxy
,
207 int direction
, int flags
)
209 git_repository
*repo
;
211 transport_local
*t
= (transport_local
*) transport
;
213 git_buf buf
= GIT_BUF_INIT
;
215 GIT_UNUSED(cred_acquire_cb
);
216 GIT_UNUSED(cred_acquire_payload
);
222 free_heads(&t
->refs
);
224 t
->url
= git__strdup(url
);
225 GIT_ERROR_CHECK_ALLOC(t
->url
);
226 t
->direction
= direction
;
229 /* 'url' may be a url or path; convert to a path */
230 if ((error
= git_path_from_url_or_path(&buf
, url
)) < 0) {
231 git_buf_dispose(&buf
);
234 path
= git_buf_cstr(&buf
);
236 error
= git_repository_open(&repo
, path
);
238 git_buf_dispose(&buf
);
245 if (store_refs(t
) < 0)
253 static int local_ls(const git_remote_head
***out
, size_t *size
, git_transport
*transport
)
255 transport_local
*t
= (transport_local
*)transport
;
258 git_error_set(GIT_ERROR_NET
, "the transport has not yet loaded the refs");
262 *out
= (const git_remote_head
**)t
->refs
.contents
;
263 *size
= t
->refs
.length
;
268 static int local_negotiate_fetch(
269 git_transport
*transport
,
270 git_repository
*repo
,
271 const git_remote_head
* const *refs
,
274 transport_local
*t
= (transport_local
*)transport
;
275 git_remote_head
*rhead
;
281 /* Fill in the loids */
282 git_vector_foreach(&t
->refs
, i
, rhead
) {
285 int error
= git_revparse_single(&obj
, repo
, rhead
->name
);
287 git_oid_cpy(&rhead
->loid
, git_object_id(obj
));
288 else if (error
!= GIT_ENOTFOUND
)
292 git_object_free(obj
);
298 static int local_push_update_remote_ref(
299 git_repository
*remote_repo
,
306 git_reference
*remote_ref
= NULL
;
308 /* check for lhs, if it's empty it means to delete */
309 if (lref
[0] != '\0') {
310 /* Create or update a ref */
311 error
= git_reference_create(NULL
, remote_repo
, rref
, loid
,
312 !git_oid_is_zero(roid
), NULL
);
315 if ((error
= git_reference_lookup(&remote_ref
, remote_repo
, rref
)) < 0) {
316 if (error
== GIT_ENOTFOUND
)
321 error
= git_reference_delete(remote_ref
);
322 git_reference_free(remote_ref
);
328 static int transfer_to_push_transfer(const git_indexer_progress
*stats
, void *payload
)
330 const git_remote_callbacks
*cbs
= payload
;
332 if (!cbs
|| !cbs
->push_transfer_progress
)
335 return cbs
->push_transfer_progress(stats
->received_objects
, stats
->total_objects
, stats
->received_bytes
,
339 static int local_push(
340 git_transport
*transport
,
342 const git_remote_callbacks
*cbs
)
344 transport_local
*t
= (transport_local
*)transport
;
345 git_repository
*remote_repo
= NULL
;
349 git_buf buf
= GIT_BUF_INIT
, odb_path
= GIT_BUF_INIT
;
355 /* 'push->remote->url' may be a url or path; convert to a path */
356 if ((error
= git_path_from_url_or_path(&buf
, push
->remote
->url
)) < 0) {
357 git_buf_dispose(&buf
);
360 path
= git_buf_cstr(&buf
);
362 error
= git_repository_open(&remote_repo
, path
);
364 git_buf_dispose(&buf
);
369 /* We don't currently support pushing locally to non-bare repos. Proper
370 non-bare repo push support would require checking configs to see if
371 we should override the default 'don't let this happen' behavior.
373 Note that this is only an issue when pushing to the current branch,
374 but we forbid all pushes just in case */
375 if (!remote_repo
->is_bare
) {
376 error
= GIT_EBAREREPO
;
377 git_error_set(GIT_ERROR_INVALID
, "local push doesn't (yet) support pushing to non-bare repos.");
381 if ((error
= git_repository_item_path(&odb_path
, remote_repo
, GIT_REPOSITORY_ITEM_OBJECTS
)) < 0
382 || (error
= git_buf_joinpath(&odb_path
, odb_path
.ptr
, "pack")) < 0)
385 error
= git_packbuilder_write(push
->pb
, odb_path
.ptr
, 0, transfer_to_push_transfer
, (void *) cbs
);
386 git_buf_dispose(&odb_path
);
393 git_vector_foreach(&push
->specs
, j
, spec
) {
395 const git_error
*last
;
396 char *ref
= spec
->refspec
.dst
;
398 status
= git__calloc(1, sizeof(push_status
));
402 status
->ref
= git__strdup(ref
);
404 git_push_status_free(status
);
408 error
= local_push_update_remote_ref(remote_repo
, spec
->refspec
.src
, spec
->refspec
.dst
,
409 &spec
->loid
, &spec
->roid
);
414 case GIT_EINVALIDSPEC
:
415 status
->msg
= git__strdup("funny refname");
418 status
->msg
= git__strdup("Remote branch not found to delete");
421 last
= git_error_last();
423 if (last
&& last
->message
)
424 status
->msg
= git__strdup(last
->message
);
426 status
->msg
= git__strdup("Unspecified error encountered");
430 /* failed to allocate memory for a status message */
431 if (error
< 0 && !status
->msg
) {
432 git_push_status_free(status
);
436 /* failed to insert the ref update status */
437 if ((error
= git_vector_insert(&push
->status
, status
)) < 0) {
438 git_push_status_free(status
);
443 if (push
->specs
.length
) {
444 int flags
= t
->flags
;
445 url
= git__strdup(t
->url
);
447 if (!url
|| t
->parent
.close(&t
->parent
) < 0 ||
448 t
->parent
.connect(&t
->parent
, url
,
449 NULL
, NULL
, NULL
, GIT_DIRECTION_PUSH
, flags
))
456 git_repository_free(remote_repo
);
462 typedef struct foreach_data
{
463 git_indexer_progress
*stats
;
464 git_indexer_progress_cb progress_cb
;
465 void *progress_payload
;
466 git_odb_writepack
*writepack
;
469 static int foreach_cb(void *buf
, size_t len
, void *payload
)
471 foreach_data
*data
= (foreach_data
*)payload
;
473 data
->stats
->received_bytes
+= len
;
474 return data
->writepack
->append(data
->writepack
, buf
, len
, data
->stats
);
477 static const char *counting_objects_fmt
= "Counting objects %d\r";
478 static const char *compressing_objects_fmt
= "Compressing objects: %.0f%% (%d/%d)";
480 static int local_counting(int stage
, unsigned int current
, unsigned int total
, void *payload
)
482 git_buf progress_info
= GIT_BUF_INIT
;
483 transport_local
*t
= payload
;
489 if (stage
== GIT_PACKBUILDER_ADDING_OBJECTS
) {
490 git_buf_printf(&progress_info
, counting_objects_fmt
, current
);
491 } else if (stage
== GIT_PACKBUILDER_DELTAFICATION
) {
492 float perc
= (((float) current
) / total
) * 100;
493 git_buf_printf(&progress_info
, compressing_objects_fmt
, perc
, current
, total
);
494 if (current
== total
)
495 git_buf_printf(&progress_info
, ", done\n");
497 git_buf_putc(&progress_info
, '\r');
501 if (git_buf_oom(&progress_info
))
504 error
= t
->progress_cb(git_buf_cstr(&progress_info
), (int)git_buf_len(&progress_info
), t
->message_cb_payload
);
505 git_buf_dispose(&progress_info
);
510 static int foreach_reference_cb(git_reference
*reference
, void *payload
)
512 git_revwalk
*walk
= (git_revwalk
*)payload
;
515 if (git_reference_type(reference
) != GIT_REFERENCE_DIRECT
) {
516 git_reference_free(reference
);
520 error
= git_revwalk_hide(walk
, git_reference_target(reference
));
521 /* The reference is in the local repository, so the target may not
522 * exist on the remote. It also may not be a commit. */
523 if (error
== GIT_ENOTFOUND
|| error
== GIT_ERROR_INVALID
) {
528 git_reference_free(reference
);
533 static int local_download_pack(
534 git_transport
*transport
,
535 git_repository
*repo
,
536 git_indexer_progress
*stats
,
537 git_indexer_progress_cb progress_cb
,
538 void *progress_payload
)
540 transport_local
*t
= (transport_local
*)transport
;
541 git_revwalk
*walk
= NULL
;
542 git_remote_head
*rhead
;
545 git_packbuilder
*pack
= NULL
;
546 git_odb_writepack
*writepack
= NULL
;
548 git_buf progress_info
= GIT_BUF_INIT
;
550 if ((error
= git_revwalk_new(&walk
, t
->repo
)) < 0)
552 git_revwalk_sorting(walk
, GIT_SORT_TIME
);
554 if ((error
= git_packbuilder_new(&pack
, t
->repo
)) < 0)
557 git_packbuilder_set_callbacks(pack
, local_counting
, t
);
559 stats
->total_objects
= 0;
560 stats
->indexed_objects
= 0;
561 stats
->received_objects
= 0;
562 stats
->received_bytes
= 0;
564 git_vector_foreach(&t
->refs
, i
, rhead
) {
566 if ((error
= git_object_lookup(&obj
, t
->repo
, &rhead
->oid
, GIT_OBJECT_ANY
)) < 0)
569 if (git_object_type(obj
) == GIT_OBJECT_COMMIT
) {
570 /* Revwalker includes only wanted commits */
571 error
= git_revwalk_push(walk
, &rhead
->oid
);
573 /* Tag or some other wanted object. Add it on its own */
574 error
= git_packbuilder_insert_recur(pack
, &rhead
->oid
, rhead
->name
);
576 git_object_free(obj
);
581 if ((error
= git_reference_foreach(repo
, foreach_reference_cb
, walk
)))
584 if ((error
= git_packbuilder_insert_walk(pack
, walk
)))
587 if ((error
= git_buf_printf(&progress_info
, counting_objects_fmt
, git_packbuilder_object_count(pack
))) < 0)
590 if (t
->progress_cb
&&
591 (error
= t
->progress_cb(git_buf_cstr(&progress_info
), (int)git_buf_len(&progress_info
), t
->message_cb_payload
)) < 0)
594 /* Walk the objects, building a packfile */
595 if ((error
= git_repository_odb__weakptr(&odb
, repo
)) < 0)
598 /* One last one with the newline */
599 git_buf_clear(&progress_info
);
600 git_buf_printf(&progress_info
, counting_objects_fmt
, git_packbuilder_object_count(pack
));
601 if ((error
= git_buf_putc(&progress_info
, '\n')) < 0)
604 if (t
->progress_cb
&&
605 (error
= t
->progress_cb(git_buf_cstr(&progress_info
), (int)git_buf_len(&progress_info
), t
->message_cb_payload
)) < 0)
608 if ((error
= git_odb_write_pack(&writepack
, odb
, progress_cb
, progress_payload
)) != 0)
611 /* Write the data to the ODB */
613 foreach_data data
= {0};
615 data
.progress_cb
= progress_cb
;
616 data
.progress_payload
= progress_payload
;
617 data
.writepack
= writepack
;
620 git_packbuilder_set_threads(pack
, 0);
622 if ((error
= git_packbuilder_foreach(pack
, foreach_cb
, &data
)) != 0)
626 error
= writepack
->commit(writepack
, stats
);
629 if (writepack
) writepack
->free(writepack
);
630 git_buf_dispose(&progress_info
);
631 git_packbuilder_free(pack
);
632 git_revwalk_free(walk
);
636 static int local_set_callbacks(
637 git_transport
*transport
,
638 git_transport_message_cb progress_cb
,
639 git_transport_message_cb error_cb
,
640 git_transport_certificate_check_cb certificate_check_cb
,
641 void *message_cb_payload
)
643 transport_local
*t
= (transport_local
*)transport
;
645 GIT_UNUSED(certificate_check_cb
);
647 t
->progress_cb
= progress_cb
;
648 t
->error_cb
= error_cb
;
649 t
->message_cb_payload
= message_cb_payload
;
654 static int local_is_connected(git_transport
*transport
)
656 transport_local
*t
= (transport_local
*)transport
;
661 static int local_read_flags(git_transport
*transport
, int *flags
)
663 transport_local
*t
= (transport_local
*)transport
;
670 static void local_cancel(git_transport
*transport
)
672 transport_local
*t
= (transport_local
*)transport
;
674 git_atomic_set(&t
->cancelled
, 1);
677 static int local_close(git_transport
*transport
)
679 transport_local
*t
= (transport_local
*)transport
;
684 git_repository_free(t
->repo
);
696 static void local_free(git_transport
*transport
)
698 transport_local
*t
= (transport_local
*)transport
;
700 free_heads(&t
->refs
);
702 /* Close the transport, if it's still open. */
703 local_close(transport
);
705 /* Free the transport */
713 int git_transport_local(git_transport
**out
, git_remote
*owner
, void *param
)
720 t
= git__calloc(1, sizeof(transport_local
));
721 GIT_ERROR_CHECK_ALLOC(t
);
723 t
->parent
.version
= GIT_TRANSPORT_VERSION
;
724 t
->parent
.set_callbacks
= local_set_callbacks
;
725 t
->parent
.connect
= local_connect
;
726 t
->parent
.negotiate_fetch
= local_negotiate_fetch
;
727 t
->parent
.download_pack
= local_download_pack
;
728 t
->parent
.push
= local_push
;
729 t
->parent
.close
= local_close
;
730 t
->parent
.free
= local_free
;
731 t
->parent
.ls
= local_ls
;
732 t
->parent
.is_connected
= local_is_connected
;
733 t
->parent
.read_flags
= local_read_flags
;
734 t
->parent
.cancel
= local_cancel
;
736 if ((error
= git_vector_init(&t
->refs
, 0, NULL
)) < 0) {
743 *out
= (git_transport
*) t
;