static void start_fetch_packed(struct transfer_request *request) { struct packed_git *target; struct transfer_request *check_request = request_queue_head; struct http_pack_request *preq; target = find_sha1_pack(request->obj->oid.hash, repo->packs); if (!target) { fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", oid_to_hex(&request->obj->oid)); repo->can_update_info_refs = 0; release_request(request); return; } fprintf(stderr, "Fetching pack %s\n", sha1_to_hex(target->sha1)); fprintf(stderr, " which contains %s\n", oid_to_hex(&request->obj->oid)); preq = new_http_pack_request(target, repo->url); if (preq == NULL) { repo->can_update_info_refs = 0; return; } preq->lst = &repo->packs; /* Make sure there isn't another open request for this pack */ while (check_request) { if (check_request->state == RUN_FETCH_PACKED && !strcmp(check_request->url, preq->url)) { release_http_pack_request(preq); release_request(request); return; } check_request = check_request->next; } preq->slot->callback_func = process_response; preq->slot->callback_data = request; request->slot = preq->slot; request->userData = preq; /* Try to get the request started, abort the request on error */ request->state = RUN_FETCH_PACKED; if (!start_active_slot(preq->slot)) { fprintf(stderr, "Unable to start GET request\n"); release_http_pack_request(preq); repo->can_update_info_refs = 0; release_request(request); } }
static void start_fetch_loose(struct transfer_request *request) { struct active_request_slot *slot; struct http_object_request *obj_req; obj_req = new_http_object_request(repo->url, request->obj->oid.hash); if (obj_req == NULL) { request->state = ABORTED; return; } slot = obj_req->slot; slot->callback_func = process_response; slot->callback_data = request; request->slot = slot; request->userData = obj_req; /* Try to get the request started, abort the request on error */ request->state = RUN_FETCH_LOOSE; if (!start_active_slot(slot)) { fprintf(stderr, "Unable to start GET request\n"); repo->can_update_info_refs = 0; release_http_object_request(obj_req); release_request(request); } }
static void read_complete_cb(int16_t ll_stat, void *info, void *arg) { disk_req_t *req = (disk_req_t *)arg; outlet_t *ol = req->outlet; uint16_t async_tag = req->async_tag; uint32_t num_sectors = req->num_sectors; term_t reply_to = req->reply_to; release_request(req); // {disk_async,Port,Tag,Data} // {disk_async_error,Port,Tag,Error} proc_t *caller = scheduler_lookup(reply_to); if (caller == 0) return; int status = op_status(ll_stat); if (status == DISK_REP_OK) { uint8_t *ptr; term_t bin = heap_make_bin_N(&caller->hp, num_sectors *SECTOR_SIZE, &ptr); if (bin == noval) goto nomem; disk_retrieve_data(info, ptr, num_sectors); disk_async(caller, A_DISK_ASYNC, ol->oid, async_tag, bin); } else { term_t err = (DISK_REP_ERROR) ?A_EIO :A_ENOTSUP; disk_async(caller, A_DISK_ASYNC_ERROR, ol->oid, async_tag, err); } return; nomem: scheduler_signal_exit_N(caller, ol->oid, A_NO_MEMORY); }
static void simple_complete_cb(int16_t ll_stat, void *info, void *arg) { disk_req_t *req = (disk_req_t *)arg; outlet_t *ol = req->outlet; uint16_t async_tag = req->async_tag; term_t reply_to = req->reply_to; release_request(req); // {disk_async,Port,Tag,ok} // {disk_async_error,Port,Tag,Error} proc_t *caller = scheduler_lookup(reply_to); if (caller == 0) return; int status = op_status(ll_stat); if (status == DISK_REP_OK) disk_async(caller, A_DISK_ASYNC, ol->oid, async_tag, A_OK); else { term_t err = (DISK_REP_ERROR) ?A_EIO :A_ENOTSUP; disk_async(caller, A_DISK_ASYNC_ERROR, ol->oid, async_tag, err); } }
static void finish_request(struct transfer_request *request) { struct http_pack_request *preq; struct http_object_request *obj_req; request->curl_result = request->slot->curl_result; request->http_code = request->slot->http_code; request->slot = NULL; /* Keep locks active */ check_locks(); if (request->headers != NULL) curl_slist_free_all(request->headers); /* URL is reused for MOVE after PUT */ if (request->state != RUN_PUT) { FREE_AND_NULL(request->url); } if (request->state == RUN_MKCOL) { if (request->curl_result == CURLE_OK || request->http_code == 405) { remote_dir_exists[request->obj->oid.hash[0]] = 1; start_put(request); } else { fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n", oid_to_hex(&request->obj->oid), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_PUT) { if (request->curl_result == CURLE_OK) { start_move(request); } else { fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n", oid_to_hex(&request->obj->oid), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_MOVE) { if (request->curl_result == CURLE_OK) { if (push_verbosely) fprintf(stderr, " sent %s\n", oid_to_hex(&request->obj->oid)); request->obj->flags |= REMOTE; release_request(request); } else { fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n", oid_to_hex(&request->obj->oid), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_FETCH_LOOSE) { obj_req = (struct http_object_request *)request->userData; if (finish_http_object_request(obj_req) == 0) if (obj_req->rename == 0) request->obj->flags |= (LOCAL | REMOTE); /* Try fetching packed if necessary */ if (request->obj->flags & LOCAL) { release_http_object_request(obj_req); release_request(request); } else start_fetch_packed(request); } else if (request->state == RUN_FETCH_PACKED) { int fail = 1; if (request->curl_result != CURLE_OK) { fprintf(stderr, "Unable to get pack file %s\n%s", request->url, curl_errorstr); } else { preq = (struct http_pack_request *)request->userData; if (preq) { if (finish_http_pack_request(preq) == 0) fail = 0; release_http_pack_request(preq); } } if (fail) repo->can_update_info_refs = 0; release_request(request); } }
int cmd_main(int argc, const char **argv) { struct transfer_request *request; struct transfer_request *next_request; int nr_refspec = 0; const char **refspec = NULL; struct remote_lock *ref_lock = NULL; struct remote_lock *info_ref_lock = NULL; struct rev_info revs; int delete_branch = 0; int force_delete = 0; int objects_to_send; int rc = 0; int i; int new_refs; struct ref *ref, *local_refs; repo = xcalloc(1, sizeof(*repo)); argv++; for (i = 1; i < argc; i++, argv++) { const char *arg = *argv; if (*arg == '-') { if (!strcmp(arg, "--all")) { push_all = MATCH_REFS_ALL; continue; } if (!strcmp(arg, "--force")) { force_all = 1; continue; } if (!strcmp(arg, "--dry-run")) { dry_run = 1; continue; } if (!strcmp(arg, "--helper-status")) { helper_status = 1; continue; } if (!strcmp(arg, "--verbose")) { push_verbosely = 1; http_is_verbose = 1; continue; } if (!strcmp(arg, "-d")) { delete_branch = 1; continue; } if (!strcmp(arg, "-D")) { delete_branch = 1; force_delete = 1; continue; } if (!strcmp(arg, "-h")) usage(http_push_usage); } if (!repo->url) { char *path = strstr(arg, "//"); str_end_url_with_slash(arg, &repo->url); repo->path_len = strlen(repo->url); if (path) { repo->path = strchr(path+2, '/'); if (repo->path) repo->path_len = strlen(repo->path); } continue; } refspec = argv; nr_refspec = argc - i; break; } #ifndef USE_CURL_MULTI die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI"); #endif if (!repo->url) usage(http_push_usage); if (delete_branch && nr_refspec != 1) die("You must specify only one branch name when deleting a remote branch"); setup_git_directory(); memset(remote_dir_exists, -1, 256); http_init(NULL, repo->url, 1); #ifdef USE_CURL_MULTI is_running_queue = 0; #endif /* Verify DAV compliance/lock support */ if (!locking_available()) { rc = 1; goto cleanup; } sigchain_push_common(remove_locks_on_signal); /* Check whether the remote has server info files */ repo->can_update_info_refs = 0; repo->has_info_refs = remote_exists("info/refs"); repo->has_info_packs = remote_exists("objects/info/packs"); if (repo->has_info_refs) { info_ref_lock = lock_remote("info/refs", LOCK_TIME); if (info_ref_lock) repo->can_update_info_refs = 1; else { error("cannot lock existing info/refs"); rc = 1; goto cleanup; } } if (repo->has_info_packs) fetch_indices(); /* Get a list of all local and remote heads to validate refspecs */ local_refs = get_local_heads(); fprintf(stderr, "Fetching remote heads...\n"); get_dav_remote_heads(); run_request_queue(); /* Remove a remote branch if -d or -D was specified */ if (delete_branch) { if (delete_remote_branch(refspec[0], force_delete) == -1) { fprintf(stderr, "Unable to delete remote branch %s\n", refspec[0]); if (helper_status) printf("error %s cannot remove\n", refspec[0]); } goto cleanup; } /* match them up */ if (match_push_refs(local_refs, &remote_refs, nr_refspec, (const char **) refspec, push_all)) { rc = -1; goto cleanup; } if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); if (helper_status) printf("error null no match\n"); rc = 0; goto cleanup; } new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { struct argv_array commit_argv = ARGV_ARRAY_INIT; if (!ref->peer_ref) continue; if (is_null_oid(&ref->peer_ref->new_oid)) { if (delete_remote_branch(ref->name, 1) == -1) { error("Could not remove %s", ref->name); if (helper_status) printf("error %s cannot remove\n", ref->name); rc = -4; } else if (helper_status) printf("ok %s\n", ref->name); new_refs++; continue; } if (!oidcmp(&ref->old_oid, &ref->peer_ref->new_oid)) { if (push_verbosely) fprintf(stderr, "'%s': up-to-date\n", ref->name); if (helper_status) printf("ok %s up to date\n", ref->name); continue; } if (!force_all && !is_null_oid(&ref->old_oid) && !ref->force) { if (!has_object_file(&ref->old_oid) || !ref_newer(&ref->peer_ref->new_oid, &ref->old_oid)) { /* * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ error("remote '%s' is not an ancestor of\n" "local '%s'.\n" "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); if (helper_status) printf("error %s non-fast forward\n", ref->name); rc = -2; continue; } } oidcpy(&ref->new_oid, &ref->peer_ref->new_oid); new_refs++; fprintf(stderr, "updating '%s'", ref->name); if (strcmp(ref->name, ref->peer_ref->name)) fprintf(stderr, " using '%s'", ref->peer_ref->name); fprintf(stderr, "\n from %s\n to %s\n", oid_to_hex(&ref->old_oid), oid_to_hex(&ref->new_oid)); if (dry_run) { if (helper_status) printf("ok %s\n", ref->name); continue; } /* Lock remote branch ref */ ref_lock = lock_remote(ref->name, LOCK_TIME); if (ref_lock == NULL) { fprintf(stderr, "Unable to lock remote branch %s\n", ref->name); if (helper_status) printf("error %s lock error\n", ref->name); rc = 1; continue; } /* Set up revision info for this refspec */ argv_array_push(&commit_argv, ""); /* ignored */ argv_array_push(&commit_argv, "--objects"); argv_array_push(&commit_argv, oid_to_hex(&ref->new_oid)); if (!push_all && !is_null_oid(&ref->old_oid)) argv_array_pushf(&commit_argv, "^%s", oid_to_hex(&ref->old_oid)); init_revisions(&revs, setup_git_directory()); setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL); revs.edge_hint = 0; /* just in case */ /* Generate a list of objects that need to be pushed */ pushing = 0; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(&revs, NULL); objects_to_send = get_delta(&revs, ref_lock); finish_all_active_slots(); /* Push missing objects to remote, this would be a convenient time to pack them first if appropriate. */ pushing = 1; if (objects_to_send) fprintf(stderr, " sending %d objects\n", objects_to_send); run_request_queue(); /* Update the remote branch if all went well */ if (aborted || !update_remote(ref->new_oid.hash, ref_lock)) rc = 1; if (!rc) fprintf(stderr, " done\n"); if (helper_status) printf("%s %s\n", !rc ? "ok" : "error", ref->name); unlock_remote(ref_lock); check_locks(); argv_array_clear(&commit_argv); } /* Update remote server info if appropriate */ if (repo->has_info_refs && new_refs) { if (info_ref_lock && repo->can_update_info_refs) { fprintf(stderr, "Updating remote server info\n"); if (!dry_run) update_remote_info_refs(info_ref_lock); } else { fprintf(stderr, "Unable to update server info\n"); } } cleanup: if (info_ref_lock) unlock_remote(info_ref_lock); free(repo); http_cleanup(); request = request_queue_head; while (request != NULL) { next_request = request->next; release_request(request); request = next_request; } return rc; }