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 "git2/types.h" 11 #include "git2/net.h" 12 #include "git2/repository.h" 13 #include "git2/object.h" 14 #include "git2/tag.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" 21 22 #include "pack-objects.h" 23 #include "refs.h" 24 #include "posix.h" 25 #include "path.h" 26 #include "buffer.h" 27 #include "repository.h" 28 #include "odb.h" 29 #include "push.h" 30 #include "remote.h" 31 #include "proxy.h" 32 33 typedef struct { 34 git_transport parent; 35 git_remote *owner; 36 char *url; 37 int direction; 38 int flags; 39 git_atomic32 cancelled; 40 git_repository *repo; 41 git_transport_message_cb progress_cb; 42 git_transport_message_cb error_cb; 43 void *message_cb_payload; 44 git_vector refs; 45 unsigned connected : 1, 46 have_refs : 1; 47 } transport_local; 48 49 static void free_head(git_remote_head *head) 50 { 51 git__free(head->name); 52 git__free(head->symref_target); 53 git__free(head); 54 } 55 56 static void free_heads(git_vector *heads) 57 { 58 git_remote_head *head; 59 size_t i; 60 61 git_vector_foreach(heads, i, head) 62 free_head(head); 63 64 git_vector_free(heads); 65 } 66 67 static int add_ref(transport_local *t, const char *name) 68 { 69 const char peeled[] = "^{}"; 70 git_reference *ref, *resolved; 71 git_remote_head *head; 72 git_oid obj_id; 73 git_object *obj = NULL, *target = NULL; 74 git_buf buf = GIT_BUF_INIT; 75 int error; 76 77 if ((error = git_reference_lookup(&ref, t->repo, name)) < 0) 78 return error; 79 80 error = git_reference_resolve(&resolved, ref); 81 if (error < 0) { 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". */ 86 git_error_clear(); 87 return 0; 88 } 89 return error; 90 } 91 92 git_oid_cpy(&obj_id, git_reference_target(resolved)); 93 git_reference_free(resolved); 94 95 head = git__calloc(1, sizeof(git_remote_head)); 96 GIT_ERROR_CHECK_ALLOC(head); 97 98 head->name = git__strdup(name); 99 GIT_ERROR_CHECK_ALLOC(head->name); 100 101 git_oid_cpy(&head->oid, &obj_id); 102 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); 106 } 107 git_reference_free(ref); 108 109 if ((error = git_vector_insert(&t->refs, head)) < 0) { 110 free_head(head); 111 return error; 112 } 113 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)) 116 return 0; 117 118 if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJECT_ANY)) < 0) 119 return error; 120 121 head = NULL; 122 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); 128 return 0; 129 } 130 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); 134 135 if (git_buf_join(&buf, 0, name, peeled) < 0) { 136 free_head(head); 137 return -1; 138 } 139 head->name = git_buf_detach(&buf); 140 141 if (!(error = git_tag_peel(&target, (git_tag *)obj))) { 142 git_oid_cpy(&head->oid, git_object_id(target)); 143 144 if ((error = git_vector_insert(&t->refs, head)) < 0) { 145 free_head(head); 146 } 147 } 148 149 git_object_free(obj); 150 git_object_free(target); 151 152 return error; 153 } 154 155 static int store_refs(transport_local *t) 156 { 157 size_t i; 158 git_remote_head *head; 159 git_strarray ref_names = {0}; 160 161 GIT_ASSERT_ARG(t); 162 163 if (git_reference_list(&ref_names, t->repo) < 0) 164 goto on_error; 165 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); 169 git__free(head); 170 } 171 172 /* Clear the vector so we can reuse it */ 173 git_vector_clear(&t->refs); 174 175 /* Sort the references first */ 176 git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb); 177 178 /* Add HEAD iff direction is fetch */ 179 if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0) 180 goto on_error; 181 182 for (i = 0; i < ref_names.count; ++i) { 183 if (add_ref(t, ref_names.strings[i]) < 0) 184 goto on_error; 185 } 186 187 t->have_refs = 1; 188 git_strarray_dispose(&ref_names); 189 return 0; 190 191 on_error: 192 git_vector_free(&t->refs); 193 git_strarray_dispose(&ref_names); 194 return -1; 195 } 196 197 /* 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. 200 */ 201 static int local_connect( 202 git_transport *transport, 203 const char *url, 204 git_credential_acquire_cb cred_acquire_cb, 205 void *cred_acquire_payload, 206 const git_proxy_options *proxy, 207 int direction, int flags) 208 { 209 git_repository *repo; 210 int error; 211 transport_local *t = (transport_local *) transport; 212 const char *path; 213 git_buf buf = GIT_BUF_INIT; 214 215 GIT_UNUSED(cred_acquire_cb); 216 GIT_UNUSED(cred_acquire_payload); 217 GIT_UNUSED(proxy); 218 219 if (t->connected) 220 return 0; 221 222 free_heads(&t->refs); 223 224 t->url = git__strdup(url); 225 GIT_ERROR_CHECK_ALLOC(t->url); 226 t->direction = direction; 227 t->flags = flags; 228 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); 232 return error; 233 } 234 path = git_buf_cstr(&buf); 235 236 error = git_repository_open(&repo, path); 237 238 git_buf_dispose(&buf); 239 240 if (error < 0) 241 return -1; 242 243 t->repo = repo; 244 245 if (store_refs(t) < 0) 246 return -1; 247 248 t->connected = 1; 249 250 return 0; 251 } 252 253 static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport) 254 { 255 transport_local *t = (transport_local *)transport; 256 257 if (!t->have_refs) { 258 git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs"); 259 return -1; 260 } 261 262 *out = (const git_remote_head **)t->refs.contents; 263 *size = t->refs.length; 264 265 return 0; 266 } 267 268 static int local_negotiate_fetch( 269 git_transport *transport, 270 git_repository *repo, 271 const git_remote_head * const *refs, 272 size_t count) 273 { 274 transport_local *t = (transport_local*)transport; 275 git_remote_head *rhead; 276 unsigned int i; 277 278 GIT_UNUSED(refs); 279 GIT_UNUSED(count); 280 281 /* Fill in the loids */ 282 git_vector_foreach(&t->refs, i, rhead) { 283 git_object *obj; 284 285 int error = git_revparse_single(&obj, repo, rhead->name); 286 if (!error) 287 git_oid_cpy(&rhead->loid, git_object_id(obj)); 288 else if (error != GIT_ENOTFOUND) 289 return error; 290 else 291 git_error_clear(); 292 git_object_free(obj); 293 } 294 295 return 0; 296 } 297 298 static int local_push_update_remote_ref( 299 git_repository *remote_repo, 300 const char *lref, 301 const char *rref, 302 git_oid *loid, 303 git_oid *roid) 304 { 305 int error; 306 git_reference *remote_ref = NULL; 307 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); 313 } else { 314 /* Delete a ref */ 315 if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) { 316 if (error == GIT_ENOTFOUND) 317 error = 0; 318 return error; 319 } 320 321 error = git_reference_delete(remote_ref); 322 git_reference_free(remote_ref); 323 } 324 325 return error; 326 } 327 328 static int transfer_to_push_transfer(const git_indexer_progress *stats, void *payload) 329 { 330 const git_remote_callbacks *cbs = payload; 331 332 if (!cbs || !cbs->push_transfer_progress) 333 return 0; 334 335 return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes, 336 cbs->payload); 337 } 338 339 static int local_push( 340 git_transport *transport, 341 git_push *push, 342 const git_remote_callbacks *cbs) 343 { 344 transport_local *t = (transport_local *)transport; 345 git_repository *remote_repo = NULL; 346 push_spec *spec; 347 char *url = NULL; 348 const char *path; 349 git_buf buf = GIT_BUF_INIT, odb_path = GIT_BUF_INIT; 350 int error; 351 size_t j; 352 353 GIT_UNUSED(cbs); 354 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); 358 return error; 359 } 360 path = git_buf_cstr(&buf); 361 362 error = git_repository_open(&remote_repo, path); 363 364 git_buf_dispose(&buf); 365 366 if (error < 0) 367 return error; 368 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. 372 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."); 378 goto on_error; 379 } 380 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) 383 goto on_error; 384 385 error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs); 386 git_buf_dispose(&odb_path); 387 388 if (error < 0) 389 goto on_error; 390 391 push->unpack_ok = 1; 392 393 git_vector_foreach(&push->specs, j, spec) { 394 push_status *status; 395 const git_error *last; 396 char *ref = spec->refspec.dst; 397 398 status = git__calloc(1, sizeof(push_status)); 399 if (!status) 400 goto on_error; 401 402 status->ref = git__strdup(ref); 403 if (!status->ref) { 404 git_push_status_free(status); 405 goto on_error; 406 } 407 408 error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst, 409 &spec->loid, &spec->roid); 410 411 switch (error) { 412 case GIT_OK: 413 break; 414 case GIT_EINVALIDSPEC: 415 status->msg = git__strdup("funny refname"); 416 break; 417 case GIT_ENOTFOUND: 418 status->msg = git__strdup("Remote branch not found to delete"); 419 break; 420 default: 421 last = git_error_last(); 422 423 if (last && last->message) 424 status->msg = git__strdup(last->message); 425 else 426 status->msg = git__strdup("Unspecified error encountered"); 427 break; 428 } 429 430 /* failed to allocate memory for a status message */ 431 if (error < 0 && !status->msg) { 432 git_push_status_free(status); 433 goto on_error; 434 } 435 436 /* failed to insert the ref update status */ 437 if ((error = git_vector_insert(&push->status, status)) < 0) { 438 git_push_status_free(status); 439 goto on_error; 440 } 441 } 442 443 if (push->specs.length) { 444 int flags = t->flags; 445 url = git__strdup(t->url); 446 447 if (!url || t->parent.close(&t->parent) < 0 || 448 t->parent.connect(&t->parent, url, 449 NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags)) 450 goto on_error; 451 } 452 453 error = 0; 454 455 on_error: 456 git_repository_free(remote_repo); 457 git__free(url); 458 459 return error; 460 } 461 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; 467 } foreach_data; 468 469 static int foreach_cb(void *buf, size_t len, void *payload) 470 { 471 foreach_data *data = (foreach_data*)payload; 472 473 data->stats->received_bytes += len; 474 return data->writepack->append(data->writepack, buf, len, data->stats); 475 } 476 477 static const char *counting_objects_fmt = "Counting objects %d\r"; 478 static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)"; 479 480 static int local_counting(int stage, unsigned int current, unsigned int total, void *payload) 481 { 482 git_buf progress_info = GIT_BUF_INIT; 483 transport_local *t = payload; 484 int error; 485 486 if (!t->progress_cb) 487 return 0; 488 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"); 496 else 497 git_buf_putc(&progress_info, '\r'); 498 499 } 500 501 if (git_buf_oom(&progress_info)) 502 return -1; 503 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); 506 507 return error; 508 } 509 510 static int foreach_reference_cb(git_reference *reference, void *payload) 511 { 512 git_revwalk *walk = (git_revwalk *)payload; 513 int error; 514 515 if (git_reference_type(reference) != GIT_REFERENCE_DIRECT) { 516 git_reference_free(reference); 517 return 0; 518 } 519 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) { 524 git_error_clear(); 525 error = 0; 526 } 527 528 git_reference_free(reference); 529 530 return error; 531 } 532 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) 539 { 540 transport_local *t = (transport_local*)transport; 541 git_revwalk *walk = NULL; 542 git_remote_head *rhead; 543 unsigned int i; 544 int error = -1; 545 git_packbuilder *pack = NULL; 546 git_odb_writepack *writepack = NULL; 547 git_odb *odb = NULL; 548 git_buf progress_info = GIT_BUF_INIT; 549 550 if ((error = git_revwalk_new(&walk, t->repo)) < 0) 551 goto cleanup; 552 git_revwalk_sorting(walk, GIT_SORT_TIME); 553 554 if ((error = git_packbuilder_new(&pack, t->repo)) < 0) 555 goto cleanup; 556 557 git_packbuilder_set_callbacks(pack, local_counting, t); 558 559 stats->total_objects = 0; 560 stats->indexed_objects = 0; 561 stats->received_objects = 0; 562 stats->received_bytes = 0; 563 564 git_vector_foreach(&t->refs, i, rhead) { 565 git_object *obj; 566 if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJECT_ANY)) < 0) 567 goto cleanup; 568 569 if (git_object_type(obj) == GIT_OBJECT_COMMIT) { 570 /* Revwalker includes only wanted commits */ 571 error = git_revwalk_push(walk, &rhead->oid); 572 } else { 573 /* Tag or some other wanted object. Add it on its own */ 574 error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name); 575 } 576 git_object_free(obj); 577 if (error < 0) 578 goto cleanup; 579 } 580 581 if ((error = git_reference_foreach(repo, foreach_reference_cb, walk))) 582 goto cleanup; 583 584 if ((error = git_packbuilder_insert_walk(pack, walk))) 585 goto cleanup; 586 587 if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0) 588 goto cleanup; 589 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) 592 goto cleanup; 593 594 /* Walk the objects, building a packfile */ 595 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) 596 goto cleanup; 597 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) 602 goto cleanup; 603 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) 606 goto cleanup; 607 608 if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0) 609 goto cleanup; 610 611 /* Write the data to the ODB */ 612 { 613 foreach_data data = {0}; 614 data.stats = stats; 615 data.progress_cb = progress_cb; 616 data.progress_payload = progress_payload; 617 data.writepack = writepack; 618 619 /* autodetect */ 620 git_packbuilder_set_threads(pack, 0); 621 622 if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0) 623 goto cleanup; 624 } 625 626 error = writepack->commit(writepack, stats); 627 628 cleanup: 629 if (writepack) writepack->free(writepack); 630 git_buf_dispose(&progress_info); 631 git_packbuilder_free(pack); 632 git_revwalk_free(walk); 633 return error; 634 } 635 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) 642 { 643 transport_local *t = (transport_local *)transport; 644 645 GIT_UNUSED(certificate_check_cb); 646 647 t->progress_cb = progress_cb; 648 t->error_cb = error_cb; 649 t->message_cb_payload = message_cb_payload; 650 651 return 0; 652 } 653 654 static int local_is_connected(git_transport *transport) 655 { 656 transport_local *t = (transport_local *)transport; 657 658 return t->connected; 659 } 660 661 static int local_read_flags(git_transport *transport, int *flags) 662 { 663 transport_local *t = (transport_local *)transport; 664 665 *flags = t->flags; 666 667 return 0; 668 } 669 670 static void local_cancel(git_transport *transport) 671 { 672 transport_local *t = (transport_local *)transport; 673 674 git_atomic32_set(&t->cancelled, 1); 675 } 676 677 static int local_close(git_transport *transport) 678 { 679 transport_local *t = (transport_local *)transport; 680 681 t->connected = 0; 682 683 if (t->repo) { 684 git_repository_free(t->repo); 685 t->repo = NULL; 686 } 687 688 if (t->url) { 689 git__free(t->url); 690 t->url = NULL; 691 } 692 693 return 0; 694 } 695 696 static void local_free(git_transport *transport) 697 { 698 transport_local *t = (transport_local *)transport; 699 700 free_heads(&t->refs); 701 702 /* Close the transport, if it's still open. */ 703 local_close(transport); 704 705 /* Free the transport */ 706 git__free(t); 707 } 708 709 /************** 710 * Public API * 711 **************/ 712 713 int git_transport_local(git_transport **out, git_remote *owner, void *param) 714 { 715 int error; 716 transport_local *t; 717 718 GIT_UNUSED(param); 719 720 t = git__calloc(1, sizeof(transport_local)); 721 GIT_ERROR_CHECK_ALLOC(t); 722 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; 735 736 if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) { 737 git__free(t); 738 return error; 739 } 740 741 t->owner = owner; 742 743 *out = (git_transport *) t; 744 745 return 0; 746 } 747