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 "pack-objects.h"
14 #include "repository.h"
20 #include "git2/types.h"
22 #include "git2/repository.h"
23 #include "git2/object.h"
25 #include "git2/transport.h"
26 #include "git2/revwalk.h"
27 #include "git2/odb_backend.h"
28 #include "git2/pack.h"
29 #include "git2/commit.h"
30 #include "git2/revparse.h"
31 #include "git2/sys/remote.h"
38 git_atomic32 cancelled
;
40 git_remote_connect_options connect_opts
;
42 unsigned connected
: 1,
46 static void free_head(git_remote_head
*head
)
48 git__free(head
->name
);
49 git__free(head
->symref_target
);
53 static void free_heads(git_vector
*heads
)
55 git_remote_head
*head
;
58 git_vector_foreach(heads
, i
, head
)
61 git_vector_free(heads
);
64 static int add_ref(transport_local
*t
, const char *name
)
66 const char peeled
[] = "^{}";
67 git_reference
*ref
, *resolved
;
68 git_remote_head
*head
;
70 git_object
*obj
= NULL
, *target
= NULL
;
71 git_str buf
= GIT_STR_INIT
;
74 if ((error
= git_reference_lookup(&ref
, t
->repo
, name
)) < 0)
77 error
= git_reference_resolve(&resolved
, ref
);
79 git_reference_free(ref
);
80 if (!strcmp(name
, GIT_HEAD_FILE
) && error
== GIT_ENOTFOUND
) {
81 /* This is actually okay. Empty repos often have a HEAD that
82 * points to a nonexistent "refs/heads/master". */
89 git_oid_cpy(&obj_id
, git_reference_target(resolved
));
90 git_reference_free(resolved
);
92 head
= git__calloc(1, sizeof(git_remote_head
));
93 GIT_ERROR_CHECK_ALLOC(head
);
95 head
->name
= git__strdup(name
);
96 GIT_ERROR_CHECK_ALLOC(head
->name
);
98 git_oid_cpy(&head
->oid
, &obj_id
);
100 if (git_reference_type(ref
) == GIT_REFERENCE_SYMBOLIC
) {
101 head
->symref_target
= git__strdup(git_reference_symbolic_target(ref
));
102 GIT_ERROR_CHECK_ALLOC(head
->symref_target
);
104 git_reference_free(ref
);
106 if ((error
= git_vector_insert(&t
->refs
, head
)) < 0) {
111 /* If it's not a tag, we don't need to try to peel it */
112 if (git__prefixcmp(name
, GIT_REFS_TAGS_DIR
))
115 if ((error
= git_object_lookup(&obj
, t
->repo
, &head
->oid
, GIT_OBJECT_ANY
)) < 0)
120 /* If it's not an annotated tag, or if we're mocking
121 * git-receive-pack, just get out */
122 if (git_object_type(obj
) != GIT_OBJECT_TAG
||
123 t
->direction
!= GIT_DIRECTION_FETCH
) {
124 git_object_free(obj
);
128 /* And if it's a tag, peel it, and add it to the list */
129 head
= git__calloc(1, sizeof(git_remote_head
));
130 GIT_ERROR_CHECK_ALLOC(head
);
132 if (git_str_join(&buf
, 0, name
, peeled
) < 0) {
136 head
->name
= git_str_detach(&buf
);
138 if (!(error
= git_tag_peel(&target
, (git_tag
*)obj
))) {
139 git_oid_cpy(&head
->oid
, git_object_id(target
));
141 if ((error
= git_vector_insert(&t
->refs
, head
)) < 0) {
146 git_object_free(obj
);
147 git_object_free(target
);
152 static int store_refs(transport_local
*t
)
155 git_remote_head
*head
;
156 git_strarray ref_names
= {0};
160 if (git_reference_list(&ref_names
, t
->repo
) < 0)
163 /* Clear all heads we might have fetched in a previous connect */
164 git_vector_foreach(&t
->refs
, i
, head
) {
165 git__free(head
->name
);
169 /* Clear the vector so we can reuse it */
170 git_vector_clear(&t
->refs
);
172 /* Sort the references first */
173 git__tsort((void **)ref_names
.strings
, ref_names
.count
, &git__strcmp_cb
);
175 /* Add HEAD iff direction is fetch */
176 if (t
->direction
== GIT_DIRECTION_FETCH
&& add_ref(t
, GIT_HEAD_FILE
) < 0)
179 for (i
= 0; i
< ref_names
.count
; ++i
) {
180 if (add_ref(t
, ref_names
.strings
[i
]) < 0)
185 git_strarray_dispose(&ref_names
);
189 git_vector_free(&t
->refs
);
190 git_strarray_dispose(&ref_names
);
195 * Try to open the url as a git directory. The direction doesn't
196 * matter in this case because we're calculating the heads ourselves.
198 static int local_connect(
199 git_transport
*transport
,
202 const git_remote_connect_options
*connect_opts
)
204 git_repository
*repo
;
206 transport_local
*t
= (transport_local
*)transport
;
208 git_str buf
= GIT_STR_INIT
;
213 if (git_remote_connect_options_normalize(&t
->connect_opts
, t
->owner
->repo
, connect_opts
) < 0)
216 free_heads(&t
->refs
);
218 t
->url
= git__strdup(url
);
219 GIT_ERROR_CHECK_ALLOC(t
->url
);
220 t
->direction
= direction
;
222 /* 'url' may be a url or path; convert to a path */
223 if ((error
= git_fs_path_from_url_or_path(&buf
, url
)) < 0) {
224 git_str_dispose(&buf
);
227 path
= git_str_cstr(&buf
);
229 error
= git_repository_open(&repo
, path
);
231 git_str_dispose(&buf
);
238 if (store_refs(t
) < 0)
246 static int local_set_connect_opts(
247 git_transport
*transport
,
248 const git_remote_connect_options
*connect_opts
)
250 transport_local
*t
= (transport_local
*)transport
;
253 git_error_set(GIT_ERROR_NET
, "cannot reconfigure a transport that is not connected");
257 return git_remote_connect_options_normalize(&t
->connect_opts
, t
->owner
->repo
, connect_opts
);
260 static int local_capabilities(unsigned int *capabilities
, git_transport
*transport
)
262 GIT_UNUSED(transport
);
264 *capabilities
= GIT_REMOTE_CAPABILITY_TIP_OID
|
265 GIT_REMOTE_CAPABILITY_REACHABLE_OID
;
269 static int local_ls(const git_remote_head
***out
, size_t *size
, git_transport
*transport
)
271 transport_local
*t
= (transport_local
*)transport
;
274 git_error_set(GIT_ERROR_NET
, "the transport has not yet loaded the refs");
278 *out
= (const git_remote_head
**)t
->refs
.contents
;
279 *size
= t
->refs
.length
;
284 static int local_negotiate_fetch(
285 git_transport
*transport
,
286 git_repository
*repo
,
287 const git_remote_head
* const *refs
,
290 transport_local
*t
= (transport_local
*)transport
;
291 git_remote_head
*rhead
;
297 /* Fill in the loids */
298 git_vector_foreach(&t
->refs
, i
, rhead
) {
301 int error
= git_revparse_single(&obj
, repo
, rhead
->name
);
303 git_oid_cpy(&rhead
->loid
, git_object_id(obj
));
304 else if (error
!= GIT_ENOTFOUND
)
308 git_object_free(obj
);
314 static int local_push_update_remote_ref(
315 git_repository
*remote_repo
,
322 git_reference
*remote_ref
= NULL
;
324 /* check for lhs, if it's empty it means to delete */
325 if (lref
[0] != '\0') {
326 /* Create or update a ref */
327 error
= git_reference_create(NULL
, remote_repo
, rref
, loid
,
328 !git_oid_is_zero(roid
), NULL
);
331 if ((error
= git_reference_lookup(&remote_ref
, remote_repo
, rref
)) < 0) {
332 if (error
== GIT_ENOTFOUND
)
337 error
= git_reference_delete(remote_ref
);
338 git_reference_free(remote_ref
);
344 static int transfer_to_push_transfer(const git_indexer_progress
*stats
, void *payload
)
346 const git_remote_callbacks
*cbs
= payload
;
348 if (!cbs
|| !cbs
->push_transfer_progress
)
351 return cbs
->push_transfer_progress(stats
->received_objects
, stats
->total_objects
, stats
->received_bytes
,
355 static int local_push(
356 git_transport
*transport
,
359 transport_local
*t
= (transport_local
*)transport
;
360 git_remote_callbacks
*cbs
= &t
->connect_opts
.callbacks
;
361 git_repository
*remote_repo
= NULL
;
365 git_str buf
= GIT_STR_INIT
, odb_path
= GIT_STR_INIT
;
369 /* 'push->remote->url' may be a url or path; convert to a path */
370 if ((error
= git_fs_path_from_url_or_path(&buf
, push
->remote
->url
)) < 0) {
371 git_str_dispose(&buf
);
374 path
= git_str_cstr(&buf
);
376 error
= git_repository_open(&remote_repo
, path
);
378 git_str_dispose(&buf
);
383 /* We don't currently support pushing locally to non-bare repos. Proper
384 non-bare repo push support would require checking configs to see if
385 we should override the default 'don't let this happen' behavior.
387 Note that this is only an issue when pushing to the current branch,
388 but we forbid all pushes just in case */
389 if (!remote_repo
->is_bare
) {
390 error
= GIT_EBAREREPO
;
391 git_error_set(GIT_ERROR_INVALID
, "local push doesn't (yet) support pushing to non-bare repos.");
395 if ((error
= git_repository__item_path(&odb_path
, remote_repo
, GIT_REPOSITORY_ITEM_OBJECTS
)) < 0
396 || (error
= git_str_joinpath(&odb_path
, odb_path
.ptr
, "pack")) < 0)
399 error
= git_packbuilder_write(push
->pb
, odb_path
.ptr
, 0, transfer_to_push_transfer
, (void *) cbs
);
400 git_str_dispose(&odb_path
);
407 git_vector_foreach(&push
->specs
, j
, spec
) {
409 const git_error
*last
;
410 char *ref
= spec
->refspec
.dst
;
412 status
= git__calloc(1, sizeof(push_status
));
416 status
->ref
= git__strdup(ref
);
418 git_push_status_free(status
);
422 error
= local_push_update_remote_ref(remote_repo
, spec
->refspec
.src
, spec
->refspec
.dst
,
423 &spec
->loid
, &spec
->roid
);
428 case GIT_EINVALIDSPEC
:
429 status
->msg
= git__strdup("funny refname");
432 status
->msg
= git__strdup("Remote branch not found to delete");
435 last
= git_error_last();
437 if (last
&& last
->message
)
438 status
->msg
= git__strdup(last
->message
);
440 status
->msg
= git__strdup("Unspecified error encountered");
444 /* failed to allocate memory for a status message */
445 if (error
< 0 && !status
->msg
) {
446 git_push_status_free(status
);
450 /* failed to insert the ref update status */
451 if ((error
= git_vector_insert(&push
->status
, status
)) < 0) {
452 git_push_status_free(status
);
457 if (push
->specs
.length
) {
458 url
= git__strdup(t
->url
);
460 if (!url
|| t
->parent
.close(&t
->parent
) < 0 ||
461 t
->parent
.connect(&t
->parent
, url
,
462 GIT_DIRECTION_PUSH
, NULL
))
469 git_repository_free(remote_repo
);
475 typedef struct foreach_data
{
476 git_indexer_progress
*stats
;
477 git_indexer_progress_cb progress_cb
;
478 void *progress_payload
;
479 git_odb_writepack
*writepack
;
482 static int foreach_cb(void *buf
, size_t len
, void *payload
)
484 foreach_data
*data
= (foreach_data
*)payload
;
486 data
->stats
->received_bytes
+= len
;
487 return data
->writepack
->append(data
->writepack
, buf
, len
, data
->stats
);
490 static const char *counting_objects_fmt
= "Counting objects %d\r";
491 static const char *compressing_objects_fmt
= "Compressing objects: %.0f%% (%d/%d)";
493 static int local_counting(int stage
, unsigned int current
, unsigned int total
, void *payload
)
495 git_str progress_info
= GIT_STR_INIT
;
496 transport_local
*t
= payload
;
499 if (!t
->connect_opts
.callbacks
.sideband_progress
)
502 if (stage
== GIT_PACKBUILDER_ADDING_OBJECTS
) {
503 git_str_printf(&progress_info
, counting_objects_fmt
, current
);
504 } else if (stage
== GIT_PACKBUILDER_DELTAFICATION
) {
505 float perc
= (((float) current
) / total
) * 100;
506 git_str_printf(&progress_info
, compressing_objects_fmt
, perc
, current
, total
);
507 if (current
== total
)
508 git_str_printf(&progress_info
, ", done\n");
510 git_str_putc(&progress_info
, '\r');
514 if (git_str_oom(&progress_info
))
517 if (progress_info
.size
> INT_MAX
) {
518 git_error_set(GIT_ERROR_NET
, "remote sent overly large progress data");
519 git_str_dispose(&progress_info
);
524 error
= t
->connect_opts
.callbacks
.sideband_progress(
526 (int)progress_info
.size
,
527 t
->connect_opts
.callbacks
.payload
);
529 git_str_dispose(&progress_info
);
533 static int foreach_reference_cb(git_reference
*reference
, void *payload
)
535 git_revwalk
*walk
= (git_revwalk
*)payload
;
538 if (git_reference_type(reference
) != GIT_REFERENCE_DIRECT
) {
539 git_reference_free(reference
);
543 error
= git_revwalk_hide(walk
, git_reference_target(reference
));
544 /* The reference is in the local repository, so the target may not
545 * exist on the remote. It also may not be a commit. */
546 if (error
== GIT_ENOTFOUND
|| error
== GIT_ERROR_INVALID
) {
551 git_reference_free(reference
);
556 static int local_download_pack(
557 git_transport
*transport
,
558 git_repository
*repo
,
559 git_indexer_progress
*stats
)
561 transport_local
*t
= (transport_local
*)transport
;
562 git_revwalk
*walk
= NULL
;
563 git_remote_head
*rhead
;
566 git_packbuilder
*pack
= NULL
;
567 git_odb_writepack
*writepack
= NULL
;
569 git_str progress_info
= GIT_STR_INIT
;
570 foreach_data data
= {0};
572 if ((error
= git_revwalk_new(&walk
, t
->repo
)) < 0)
575 git_revwalk_sorting(walk
, GIT_SORT_TIME
);
577 if ((error
= git_packbuilder_new(&pack
, t
->repo
)) < 0)
580 git_packbuilder_set_callbacks(pack
, local_counting
, t
);
582 stats
->total_objects
= 0;
583 stats
->indexed_objects
= 0;
584 stats
->received_objects
= 0;
585 stats
->received_bytes
= 0;
587 git_vector_foreach(&t
->refs
, i
, rhead
) {
589 if ((error
= git_object_lookup(&obj
, t
->repo
, &rhead
->oid
, GIT_OBJECT_ANY
)) < 0)
592 if (git_object_type(obj
) == GIT_OBJECT_COMMIT
) {
593 /* Revwalker includes only wanted commits */
594 error
= git_revwalk_push(walk
, &rhead
->oid
);
596 /* Tag or some other wanted object. Add it on its own */
597 error
= git_packbuilder_insert_recur(pack
, &rhead
->oid
, rhead
->name
);
599 git_object_free(obj
);
604 if ((error
= git_reference_foreach(repo
, foreach_reference_cb
, walk
)))
607 if ((error
= git_packbuilder_insert_walk(pack
, walk
)))
610 if (t
->connect_opts
.callbacks
.sideband_progress
) {
611 if ((error
= git_str_printf(
613 counting_objects_fmt
,
614 git_packbuilder_object_count(pack
))) < 0 ||
615 (error
= t
->connect_opts
.callbacks
.sideband_progress(
617 (int)progress_info
.size
,
618 t
->connect_opts
.callbacks
.payload
)) < 0)
622 /* Walk the objects, building a packfile */
623 if ((error
= git_repository_odb__weakptr(&odb
, repo
)) < 0)
626 /* One last one with the newline */
627 if (t
->connect_opts
.callbacks
.sideband_progress
) {
628 git_str_clear(&progress_info
);
630 if ((error
= git_str_printf(
632 counting_objects_fmt
,
633 git_packbuilder_object_count(pack
))) < 0 ||
634 (error
= git_str_putc(&progress_info
, '\n')) < 0 ||
635 (error
= t
->connect_opts
.callbacks
.sideband_progress(
637 (int)progress_info
.size
,
638 t
->connect_opts
.callbacks
.payload
)) < 0)
642 if ((error
= git_odb_write_pack(
645 t
->connect_opts
.callbacks
.transfer_progress
,
646 t
->connect_opts
.callbacks
.payload
)) < 0)
649 /* Write the data to the ODB */
651 data
.progress_cb
= t
->connect_opts
.callbacks
.transfer_progress
;
652 data
.progress_payload
= t
->connect_opts
.callbacks
.payload
;
653 data
.writepack
= writepack
;
656 git_packbuilder_set_threads(pack
, 0);
658 if ((error
= git_packbuilder_foreach(pack
, foreach_cb
, &data
)) != 0)
661 error
= writepack
->commit(writepack
, stats
);
664 if (writepack
) writepack
->free(writepack
);
665 git_str_dispose(&progress_info
);
666 git_packbuilder_free(pack
);
667 git_revwalk_free(walk
);
671 static int local_is_connected(git_transport
*transport
)
673 transport_local
*t
= (transport_local
*)transport
;
678 static void local_cancel(git_transport
*transport
)
680 transport_local
*t
= (transport_local
*)transport
;
682 git_atomic32_set(&t
->cancelled
, 1);
685 static int local_close(git_transport
*transport
)
687 transport_local
*t
= (transport_local
*)transport
;
692 git_repository_free(t
->repo
);
704 static void local_free(git_transport
*transport
)
706 transport_local
*t
= (transport_local
*)transport
;
708 free_heads(&t
->refs
);
710 /* Close the transport, if it's still open. */
711 local_close(transport
);
713 /* Free the transport */
721 int git_transport_local(git_transport
**out
, git_remote
*owner
, void *param
)
728 t
= git__calloc(1, sizeof(transport_local
));
729 GIT_ERROR_CHECK_ALLOC(t
);
731 t
->parent
.version
= GIT_TRANSPORT_VERSION
;
732 t
->parent
.connect
= local_connect
;
733 t
->parent
.set_connect_opts
= local_set_connect_opts
;
734 t
->parent
.capabilities
= local_capabilities
;
735 t
->parent
.negotiate_fetch
= local_negotiate_fetch
;
736 t
->parent
.download_pack
= local_download_pack
;
737 t
->parent
.push
= local_push
;
738 t
->parent
.close
= local_close
;
739 t
->parent
.free
= local_free
;
740 t
->parent
.ls
= local_ls
;
741 t
->parent
.is_connected
= local_is_connected
;
742 t
->parent
.cancel
= local_cancel
;
744 if ((error
= git_vector_init(&t
->refs
, 0, NULL
)) < 0) {
751 *out
= (git_transport
*) t
;