]> git.proxmox.com Git - libgit2.git/blob - src/transports/local.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / transports / local.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
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.
6 */
7
8 #include "common.h"
9
10 #include "pack-objects.h"
11 #include "refs.h"
12 #include "posix.h"
13 #include "fs_path.h"
14 #include "repository.h"
15 #include "odb.h"
16 #include "push.h"
17 #include "remote.h"
18 #include "proxy.h"
19
20 #include "git2/types.h"
21 #include "git2/net.h"
22 #include "git2/repository.h"
23 #include "git2/object.h"
24 #include "git2/tag.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"
32
33 typedef struct {
34 git_transport parent;
35 git_remote *owner;
36 char *url;
37 int direction;
38 git_atomic32 cancelled;
39 git_repository *repo;
40 git_remote_connect_options connect_opts;
41 git_vector refs;
42 unsigned connected : 1,
43 have_refs : 1;
44 } transport_local;
45
46 static void free_head(git_remote_head *head)
47 {
48 git__free(head->name);
49 git__free(head->symref_target);
50 git__free(head);
51 }
52
53 static void free_heads(git_vector *heads)
54 {
55 git_remote_head *head;
56 size_t i;
57
58 git_vector_foreach(heads, i, head)
59 free_head(head);
60
61 git_vector_free(heads);
62 }
63
64 static int add_ref(transport_local *t, const char *name)
65 {
66 const char peeled[] = "^{}";
67 git_reference *ref, *resolved;
68 git_remote_head *head;
69 git_oid obj_id;
70 git_object *obj = NULL, *target = NULL;
71 git_str buf = GIT_STR_INIT;
72 int error;
73
74 if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
75 return error;
76
77 error = git_reference_resolve(&resolved, ref);
78 if (error < 0) {
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". */
83 git_error_clear();
84 return 0;
85 }
86 return error;
87 }
88
89 git_oid_cpy(&obj_id, git_reference_target(resolved));
90 git_reference_free(resolved);
91
92 head = git__calloc(1, sizeof(git_remote_head));
93 GIT_ERROR_CHECK_ALLOC(head);
94
95 head->name = git__strdup(name);
96 GIT_ERROR_CHECK_ALLOC(head->name);
97
98 git_oid_cpy(&head->oid, &obj_id);
99
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);
103 }
104 git_reference_free(ref);
105
106 if ((error = git_vector_insert(&t->refs, head)) < 0) {
107 free_head(head);
108 return error;
109 }
110
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))
113 return 0;
114
115 if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJECT_ANY)) < 0)
116 return error;
117
118 head = NULL;
119
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);
125 return 0;
126 }
127
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);
131
132 if (git_str_join(&buf, 0, name, peeled) < 0) {
133 free_head(head);
134 return -1;
135 }
136 head->name = git_str_detach(&buf);
137
138 if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
139 git_oid_cpy(&head->oid, git_object_id(target));
140
141 if ((error = git_vector_insert(&t->refs, head)) < 0) {
142 free_head(head);
143 }
144 }
145
146 git_object_free(obj);
147 git_object_free(target);
148
149 return error;
150 }
151
152 static int store_refs(transport_local *t)
153 {
154 size_t i;
155 git_remote_head *head;
156 git_strarray ref_names = {0};
157
158 GIT_ASSERT_ARG(t);
159
160 if (git_reference_list(&ref_names, t->repo) < 0)
161 goto on_error;
162
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);
166 git__free(head);
167 }
168
169 /* Clear the vector so we can reuse it */
170 git_vector_clear(&t->refs);
171
172 /* Sort the references first */
173 git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
174
175 /* Add HEAD iff direction is fetch */
176 if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
177 goto on_error;
178
179 for (i = 0; i < ref_names.count; ++i) {
180 if (add_ref(t, ref_names.strings[i]) < 0)
181 goto on_error;
182 }
183
184 t->have_refs = 1;
185 git_strarray_dispose(&ref_names);
186 return 0;
187
188 on_error:
189 git_vector_free(&t->refs);
190 git_strarray_dispose(&ref_names);
191 return -1;
192 }
193
194 /*
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.
197 */
198 static int local_connect(
199 git_transport *transport,
200 const char *url,
201 int direction,
202 const git_remote_connect_options *connect_opts)
203 {
204 git_repository *repo;
205 int error;
206 transport_local *t = (transport_local *)transport;
207 const char *path;
208 git_str buf = GIT_STR_INIT;
209
210 if (t->connected)
211 return 0;
212
213 if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
214 return -1;
215
216 free_heads(&t->refs);
217
218 t->url = git__strdup(url);
219 GIT_ERROR_CHECK_ALLOC(t->url);
220 t->direction = direction;
221
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);
225 return error;
226 }
227 path = git_str_cstr(&buf);
228
229 error = git_repository_open(&repo, path);
230
231 git_str_dispose(&buf);
232
233 if (error < 0)
234 return -1;
235
236 t->repo = repo;
237
238 if (store_refs(t) < 0)
239 return -1;
240
241 t->connected = 1;
242
243 return 0;
244 }
245
246 static int local_set_connect_opts(
247 git_transport *transport,
248 const git_remote_connect_options *connect_opts)
249 {
250 transport_local *t = (transport_local *)transport;
251
252 if (!t->connected) {
253 git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
254 return -1;
255 }
256
257 return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts);
258 }
259
260 static int local_capabilities(unsigned int *capabilities, git_transport *transport)
261 {
262 GIT_UNUSED(transport);
263
264 *capabilities = GIT_REMOTE_CAPABILITY_TIP_OID |
265 GIT_REMOTE_CAPABILITY_REACHABLE_OID;
266 return 0;
267 }
268
269 static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
270 {
271 transport_local *t = (transport_local *)transport;
272
273 if (!t->have_refs) {
274 git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
275 return -1;
276 }
277
278 *out = (const git_remote_head **)t->refs.contents;
279 *size = t->refs.length;
280
281 return 0;
282 }
283
284 static int local_negotiate_fetch(
285 git_transport *transport,
286 git_repository *repo,
287 const git_remote_head * const *refs,
288 size_t count)
289 {
290 transport_local *t = (transport_local*)transport;
291 git_remote_head *rhead;
292 unsigned int i;
293
294 GIT_UNUSED(refs);
295 GIT_UNUSED(count);
296
297 /* Fill in the loids */
298 git_vector_foreach(&t->refs, i, rhead) {
299 git_object *obj;
300
301 int error = git_revparse_single(&obj, repo, rhead->name);
302 if (!error)
303 git_oid_cpy(&rhead->loid, git_object_id(obj));
304 else if (error != GIT_ENOTFOUND)
305 return error;
306 else
307 git_error_clear();
308 git_object_free(obj);
309 }
310
311 return 0;
312 }
313
314 static int local_push_update_remote_ref(
315 git_repository *remote_repo,
316 const char *lref,
317 const char *rref,
318 git_oid *loid,
319 git_oid *roid)
320 {
321 int error;
322 git_reference *remote_ref = NULL;
323
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);
329 } else {
330 /* Delete a ref */
331 if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
332 if (error == GIT_ENOTFOUND)
333 error = 0;
334 return error;
335 }
336
337 error = git_reference_delete(remote_ref);
338 git_reference_free(remote_ref);
339 }
340
341 return error;
342 }
343
344 static int transfer_to_push_transfer(const git_indexer_progress *stats, void *payload)
345 {
346 const git_remote_callbacks *cbs = payload;
347
348 if (!cbs || !cbs->push_transfer_progress)
349 return 0;
350
351 return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes,
352 cbs->payload);
353 }
354
355 static int local_push(
356 git_transport *transport,
357 git_push *push)
358 {
359 transport_local *t = (transport_local *)transport;
360 git_remote_callbacks *cbs = &t->connect_opts.callbacks;
361 git_repository *remote_repo = NULL;
362 push_spec *spec;
363 char *url = NULL;
364 const char *path;
365 git_str buf = GIT_STR_INIT, odb_path = GIT_STR_INIT;
366 int error;
367 size_t j;
368
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);
372 return error;
373 }
374 path = git_str_cstr(&buf);
375
376 error = git_repository_open(&remote_repo, path);
377
378 git_str_dispose(&buf);
379
380 if (error < 0)
381 return error;
382
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.
386
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.");
392 goto on_error;
393 }
394
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)
397 goto on_error;
398
399 error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
400 git_str_dispose(&odb_path);
401
402 if (error < 0)
403 goto on_error;
404
405 push->unpack_ok = 1;
406
407 git_vector_foreach(&push->specs, j, spec) {
408 push_status *status;
409 const git_error *last;
410 char *ref = spec->refspec.dst;
411
412 status = git__calloc(1, sizeof(push_status));
413 if (!status)
414 goto on_error;
415
416 status->ref = git__strdup(ref);
417 if (!status->ref) {
418 git_push_status_free(status);
419 goto on_error;
420 }
421
422 error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
423 &spec->loid, &spec->roid);
424
425 switch (error) {
426 case GIT_OK:
427 break;
428 case GIT_EINVALIDSPEC:
429 status->msg = git__strdup("funny refname");
430 break;
431 case GIT_ENOTFOUND:
432 status->msg = git__strdup("Remote branch not found to delete");
433 break;
434 default:
435 last = git_error_last();
436
437 if (last && last->message)
438 status->msg = git__strdup(last->message);
439 else
440 status->msg = git__strdup("Unspecified error encountered");
441 break;
442 }
443
444 /* failed to allocate memory for a status message */
445 if (error < 0 && !status->msg) {
446 git_push_status_free(status);
447 goto on_error;
448 }
449
450 /* failed to insert the ref update status */
451 if ((error = git_vector_insert(&push->status, status)) < 0) {
452 git_push_status_free(status);
453 goto on_error;
454 }
455 }
456
457 if (push->specs.length) {
458 url = git__strdup(t->url);
459
460 if (!url || t->parent.close(&t->parent) < 0 ||
461 t->parent.connect(&t->parent, url,
462 GIT_DIRECTION_PUSH, NULL))
463 goto on_error;
464 }
465
466 error = 0;
467
468 on_error:
469 git_repository_free(remote_repo);
470 git__free(url);
471
472 return error;
473 }
474
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;
480 } foreach_data;
481
482 static int foreach_cb(void *buf, size_t len, void *payload)
483 {
484 foreach_data *data = (foreach_data*)payload;
485
486 data->stats->received_bytes += len;
487 return data->writepack->append(data->writepack, buf, len, data->stats);
488 }
489
490 static const char *counting_objects_fmt = "Counting objects %d\r";
491 static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)";
492
493 static int local_counting(int stage, unsigned int current, unsigned int total, void *payload)
494 {
495 git_str progress_info = GIT_STR_INIT;
496 transport_local *t = payload;
497 int error;
498
499 if (!t->connect_opts.callbacks.sideband_progress)
500 return 0;
501
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");
509 else
510 git_str_putc(&progress_info, '\r');
511
512 }
513
514 if (git_str_oom(&progress_info))
515 return -1;
516
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);
520 return -1;
521 }
522
523
524 error = t->connect_opts.callbacks.sideband_progress(
525 progress_info.ptr,
526 (int)progress_info.size,
527 t->connect_opts.callbacks.payload);
528
529 git_str_dispose(&progress_info);
530 return error;
531 }
532
533 static int foreach_reference_cb(git_reference *reference, void *payload)
534 {
535 git_revwalk *walk = (git_revwalk *)payload;
536 int error;
537
538 if (git_reference_type(reference) != GIT_REFERENCE_DIRECT) {
539 git_reference_free(reference);
540 return 0;
541 }
542
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) {
547 git_error_clear();
548 error = 0;
549 }
550
551 git_reference_free(reference);
552
553 return error;
554 }
555
556 static int local_download_pack(
557 git_transport *transport,
558 git_repository *repo,
559 git_indexer_progress *stats)
560 {
561 transport_local *t = (transport_local*)transport;
562 git_revwalk *walk = NULL;
563 git_remote_head *rhead;
564 unsigned int i;
565 int error = -1;
566 git_packbuilder *pack = NULL;
567 git_odb_writepack *writepack = NULL;
568 git_odb *odb = NULL;
569 git_str progress_info = GIT_STR_INIT;
570 foreach_data data = {0};
571
572 if ((error = git_revwalk_new(&walk, t->repo)) < 0)
573 goto cleanup;
574
575 git_revwalk_sorting(walk, GIT_SORT_TIME);
576
577 if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
578 goto cleanup;
579
580 git_packbuilder_set_callbacks(pack, local_counting, t);
581
582 stats->total_objects = 0;
583 stats->indexed_objects = 0;
584 stats->received_objects = 0;
585 stats->received_bytes = 0;
586
587 git_vector_foreach(&t->refs, i, rhead) {
588 git_object *obj;
589 if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJECT_ANY)) < 0)
590 goto cleanup;
591
592 if (git_object_type(obj) == GIT_OBJECT_COMMIT) {
593 /* Revwalker includes only wanted commits */
594 error = git_revwalk_push(walk, &rhead->oid);
595 } else {
596 /* Tag or some other wanted object. Add it on its own */
597 error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
598 }
599 git_object_free(obj);
600 if (error < 0)
601 goto cleanup;
602 }
603
604 if ((error = git_reference_foreach(repo, foreach_reference_cb, walk)))
605 goto cleanup;
606
607 if ((error = git_packbuilder_insert_walk(pack, walk)))
608 goto cleanup;
609
610 if (t->connect_opts.callbacks.sideband_progress) {
611 if ((error = git_str_printf(
612 &progress_info,
613 counting_objects_fmt,
614 git_packbuilder_object_count(pack))) < 0 ||
615 (error = t->connect_opts.callbacks.sideband_progress(
616 progress_info.ptr,
617 (int)progress_info.size,
618 t->connect_opts.callbacks.payload)) < 0)
619 goto cleanup;
620 }
621
622 /* Walk the objects, building a packfile */
623 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
624 goto cleanup;
625
626 /* One last one with the newline */
627 if (t->connect_opts.callbacks.sideband_progress) {
628 git_str_clear(&progress_info);
629
630 if ((error = git_str_printf(
631 &progress_info,
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(
636 progress_info.ptr,
637 (int)progress_info.size,
638 t->connect_opts.callbacks.payload)) < 0)
639 goto cleanup;
640 }
641
642 if ((error = git_odb_write_pack(
643 &writepack,
644 odb,
645 t->connect_opts.callbacks.transfer_progress,
646 t->connect_opts.callbacks.payload)) < 0)
647 goto cleanup;
648
649 /* Write the data to the ODB */
650 data.stats = stats;
651 data.progress_cb = t->connect_opts.callbacks.transfer_progress;
652 data.progress_payload = t->connect_opts.callbacks.payload;
653 data.writepack = writepack;
654
655 /* autodetect */
656 git_packbuilder_set_threads(pack, 0);
657
658 if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
659 goto cleanup;
660
661 error = writepack->commit(writepack, stats);
662
663 cleanup:
664 if (writepack) writepack->free(writepack);
665 git_str_dispose(&progress_info);
666 git_packbuilder_free(pack);
667 git_revwalk_free(walk);
668 return error;
669 }
670
671 static int local_is_connected(git_transport *transport)
672 {
673 transport_local *t = (transport_local *)transport;
674
675 return t->connected;
676 }
677
678 static void local_cancel(git_transport *transport)
679 {
680 transport_local *t = (transport_local *)transport;
681
682 git_atomic32_set(&t->cancelled, 1);
683 }
684
685 static int local_close(git_transport *transport)
686 {
687 transport_local *t = (transport_local *)transport;
688
689 t->connected = 0;
690
691 if (t->repo) {
692 git_repository_free(t->repo);
693 t->repo = NULL;
694 }
695
696 if (t->url) {
697 git__free(t->url);
698 t->url = NULL;
699 }
700
701 return 0;
702 }
703
704 static void local_free(git_transport *transport)
705 {
706 transport_local *t = (transport_local *)transport;
707
708 free_heads(&t->refs);
709
710 /* Close the transport, if it's still open. */
711 local_close(transport);
712
713 /* Free the transport */
714 git__free(t);
715 }
716
717 /**************
718 * Public API *
719 **************/
720
721 int git_transport_local(git_transport **out, git_remote *owner, void *param)
722 {
723 int error;
724 transport_local *t;
725
726 GIT_UNUSED(param);
727
728 t = git__calloc(1, sizeof(transport_local));
729 GIT_ERROR_CHECK_ALLOC(t);
730
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;
743
744 if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
745 git__free(t);
746 return error;
747 }
748
749 t->owner = owner;
750
751 *out = (git_transport *) t;
752
753 return 0;
754 }