diff options
Diffstat (limited to 'third_party/git/http-push.c')
-rw-r--r-- | third_party/git/http-push.c | 1993 |
1 files changed, 0 insertions, 1993 deletions
diff --git a/third_party/git/http-push.c b/third_party/git/http-push.c deleted file mode 100644 index 6a4a43e07f2c..000000000000 --- a/third_party/git/http-push.c +++ /dev/null @@ -1,1993 +0,0 @@ -#include "cache.h" -#include "repository.h" -#include "commit.h" -#include "tag.h" -#include "blob.h" -#include "http.h" -#include "refs.h" -#include "diff.h" -#include "revision.h" -#include "exec-cmd.h" -#include "remote.h" -#include "list-objects.h" -#include "sigchain.h" -#include "strvec.h" -#include "packfile.h" -#include "object-store.h" -#include "commit-reach.h" - -#ifdef EXPAT_NEEDS_XMLPARSE_H -#include <xmlparse.h> -#else -#include <expat.h> -#endif - -static const char http_push_usage[] = -"git http-push [--all] [--dry-run] [--force] [--verbose] <remote> [<head>...]\n"; - -#ifndef XML_STATUS_OK -enum XML_Status { - XML_STATUS_OK = 1, - XML_STATUS_ERROR = 0 -}; -#define XML_STATUS_OK 1 -#define XML_STATUS_ERROR 0 -#endif - -#define PREV_BUF_SIZE 4096 - -/* DAV methods */ -#define DAV_LOCK "LOCK" -#define DAV_MKCOL "MKCOL" -#define DAV_MOVE "MOVE" -#define DAV_PROPFIND "PROPFIND" -#define DAV_PUT "PUT" -#define DAV_UNLOCK "UNLOCK" -#define DAV_DELETE "DELETE" - -/* DAV lock flags */ -#define DAV_PROP_LOCKWR (1u << 0) -#define DAV_PROP_LOCKEX (1u << 1) -#define DAV_LOCK_OK (1u << 2) - -/* DAV XML properties */ -#define DAV_CTX_LOCKENTRY ".multistatus.response.propstat.prop.supportedlock.lockentry" -#define DAV_CTX_LOCKTYPE_WRITE ".multistatus.response.propstat.prop.supportedlock.lockentry.locktype.write" -#define DAV_CTX_LOCKTYPE_EXCLUSIVE ".multistatus.response.propstat.prop.supportedlock.lockentry.lockscope.exclusive" -#define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href" -#define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout" -#define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href" -#define DAV_PROPFIND_RESP ".multistatus.response" -#define DAV_PROPFIND_NAME ".multistatus.response.href" -#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection" - -/* DAV request body templates */ -#define PROPFIND_SUPPORTEDLOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>" -#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>" -#define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>" - -#define LOCK_TIME 600 -#define LOCK_REFRESH 30 - -/* Remember to update object flag allocation in object.h */ -#define LOCAL (1u<<11) -#define REMOTE (1u<<12) -#define FETCHING (1u<<13) -#define PUSHING (1u<<14) - -/* We allow "recursive" symbolic refs. Only within reason, though */ -#define MAXDEPTH 5 - -static int pushing; -static int aborted; -static signed char remote_dir_exists[256]; - -static int push_verbosely; -static int push_all = MATCH_REFS_NONE; -static int force_all; -static int dry_run; -static int helper_status; - -static struct object_list *objects; - -struct repo { - char *url; - char *path; - int path_len; - int has_info_refs; - int can_update_info_refs; - int has_info_packs; - struct packed_git *packs; - struct remote_lock *locks; -}; - -static struct repo *repo; - -enum transfer_state { - NEED_FETCH, - RUN_FETCH_LOOSE, - RUN_FETCH_PACKED, - NEED_PUSH, - RUN_MKCOL, - RUN_PUT, - RUN_MOVE, - ABORTED, - COMPLETE -}; - -struct transfer_request { - struct object *obj; - struct packed_git *target; - char *url; - char *dest; - struct remote_lock *lock; - struct curl_slist *headers; - struct buffer buffer; - enum transfer_state state; - CURLcode curl_result; - char errorstr[CURL_ERROR_SIZE]; - long http_code; - void *userData; - struct active_request_slot *slot; - struct transfer_request *next; -}; - -static struct transfer_request *request_queue_head; - -struct xml_ctx { - char *name; - int len; - char *cdata; - void (*userFunc)(struct xml_ctx *ctx, int tag_closed); - void *userData; -}; - -struct remote_lock { - char *url; - char *owner; - char *token; - char tmpfile_suffix[GIT_MAX_HEXSZ + 1]; - time_t start_time; - long timeout; - int refreshing; - struct remote_lock *next; -}; - -/* Flags that control remote_ls processing */ -#define PROCESS_FILES (1u << 0) -#define PROCESS_DIRS (1u << 1) -#define RECURSIVE (1u << 2) - -/* Flags that remote_ls passes to callback functions */ -#define IS_DIR (1u << 0) - -struct remote_ls_ctx { - char *path; - void (*userFunc)(struct remote_ls_ctx *ls); - void *userData; - int flags; - char *dentry_name; - int dentry_flags; - struct remote_ls_ctx *parent; -}; - -/* get_dav_token_headers options */ -enum dav_header_flag { - DAV_HEADER_IF = (1u << 0), - DAV_HEADER_LOCK = (1u << 1), - DAV_HEADER_TIMEOUT = (1u << 2) -}; - -static char *xml_entities(const char *s) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_addstr_xml_quoted(&buf, s); - return strbuf_detach(&buf, NULL); -} - -static void curl_setup_http_get(CURL *curl, const char *url, - const char *custom_req) -{ - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_null); -} - -static void curl_setup_http(CURL *curl, const char *url, - const char *custom_req, struct buffer *buffer, - curl_write_callback write_fn) -{ - curl_easy_setopt(curl, CURLOPT_PUT, 1); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_INFILE, buffer); - curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->buf.len); - curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer); -#ifndef NO_CURL_IOCTL - curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); - curl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer); -#endif - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn); - curl_easy_setopt(curl, CURLOPT_NOBODY, 0); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req); - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); -} - -static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options) -{ - struct strbuf buf = STRBUF_INIT; - struct curl_slist *dav_headers = http_copy_default_headers(); - - if (options & DAV_HEADER_IF) { - strbuf_addf(&buf, "If: (<%s>)", lock->token); - dav_headers = curl_slist_append(dav_headers, buf.buf); - strbuf_reset(&buf); - } - if (options & DAV_HEADER_LOCK) { - strbuf_addf(&buf, "Lock-Token: <%s>", lock->token); - dav_headers = curl_slist_append(dav_headers, buf.buf); - strbuf_reset(&buf); - } - if (options & DAV_HEADER_TIMEOUT) { - strbuf_addf(&buf, "Timeout: Second-%ld", lock->timeout); - dav_headers = curl_slist_append(dav_headers, buf.buf); - strbuf_reset(&buf); - } - strbuf_release(&buf); - - return dav_headers; -} - -static void finish_request(struct transfer_request *request); -static void release_request(struct transfer_request *request); - -static void process_response(void *callback_data) -{ - struct transfer_request *request = - (struct transfer_request *)callback_data; - - finish_request(request); -} - -#ifdef USE_CURL_MULTI - -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); - 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 start_mkcol(struct transfer_request *request) -{ - char *hex = oid_to_hex(&request->obj->oid); - struct active_request_slot *slot; - - request->url = get_remote_object_url(repo->url, hex, 1); - - slot = get_active_slot(); - slot->callback_func = process_response; - slot->callback_data = request; - curl_setup_http_get(slot->curl, request->url, DAV_MKCOL); - curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); - - if (start_active_slot(slot)) { - request->slot = slot; - request->state = RUN_MKCOL; - } else { - request->state = ABORTED; - FREE_AND_NULL(request->url); - } -} -#endif - -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; - } - close_pack_index(target); - request->target = target; - - fprintf(stderr, "Fetching pack %s\n", - hash_to_hex(target->hash)); - fprintf(stderr, " which contains %s\n", oid_to_hex(&request->obj->oid)); - - preq = new_http_pack_request(target->hash, repo->url); - if (preq == NULL) { - repo->can_update_info_refs = 0; - return; - } - - /* 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_put(struct transfer_request *request) -{ - char *hex = oid_to_hex(&request->obj->oid); - struct active_request_slot *slot; - struct strbuf buf = STRBUF_INIT; - enum object_type type; - char hdr[50]; - void *unpacked; - unsigned long len; - int hdrlen; - ssize_t size; - git_zstream stream; - - unpacked = read_object_file(&request->obj->oid, &type, &len); - hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1; - - /* Set it up */ - git_deflate_init(&stream, zlib_compression_level); - size = git_deflate_bound(&stream, len + hdrlen); - strbuf_init(&request->buffer.buf, size); - request->buffer.posn = 0; - - /* Compress it */ - stream.next_out = (unsigned char *)request->buffer.buf.buf; - stream.avail_out = size; - - /* First header.. */ - stream.next_in = (void *)hdr; - stream.avail_in = hdrlen; - while (git_deflate(&stream, 0) == Z_OK) - ; /* nothing */ - - /* Then the data itself.. */ - stream.next_in = unpacked; - stream.avail_in = len; - while (git_deflate(&stream, Z_FINISH) == Z_OK) - ; /* nothing */ - git_deflate_end(&stream); - free(unpacked); - - request->buffer.buf.len = stream.total_out; - - strbuf_addstr(&buf, "Destination: "); - append_remote_object_url(&buf, repo->url, hex, 0); - request->dest = strbuf_detach(&buf, NULL); - - append_remote_object_url(&buf, repo->url, hex, 0); - strbuf_add(&buf, request->lock->tmpfile_suffix, the_hash_algo->hexsz + 1); - request->url = strbuf_detach(&buf, NULL); - - slot = get_active_slot(); - slot->callback_func = process_response; - slot->callback_data = request; - curl_setup_http(slot->curl, request->url, DAV_PUT, - &request->buffer, fwrite_null); - - if (start_active_slot(slot)) { - request->slot = slot; - request->state = RUN_PUT; - } else { - request->state = ABORTED; - FREE_AND_NULL(request->url); - } -} - -static void start_move(struct transfer_request *request) -{ - struct active_request_slot *slot; - struct curl_slist *dav_headers = http_copy_default_headers(); - - slot = get_active_slot(); - slot->callback_func = process_response; - slot->callback_data = request; - curl_setup_http_get(slot->curl, request->url, DAV_MOVE); - dav_headers = curl_slist_append(dav_headers, request->dest); - dav_headers = curl_slist_append(dav_headers, "Overwrite: T"); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - - if (start_active_slot(slot)) { - request->slot = slot; - request->state = RUN_MOVE; - } else { - request->state = ABORTED; - FREE_AND_NULL(request->url); - } -} - -static int refresh_lock(struct remote_lock *lock) -{ - struct active_request_slot *slot; - struct slot_results results; - struct curl_slist *dav_headers; - int rc = 0; - - lock->refreshing = 1; - - dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF | DAV_HEADER_TIMEOUT); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http_get(slot->curl, lock->url, DAV_LOCK); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result != CURLE_OK) { - fprintf(stderr, "LOCK HTTP error %ld\n", - results.http_code); - } else { - lock->start_time = time(NULL); - rc = 1; - } - } - - lock->refreshing = 0; - curl_slist_free_all(dav_headers); - - return rc; -} - -static void check_locks(void) -{ - struct remote_lock *lock = repo->locks; - time_t current_time = time(NULL); - int time_remaining; - - while (lock) { - time_remaining = lock->start_time + lock->timeout - - current_time; - if (!lock->refreshing && time_remaining < LOCK_REFRESH) { - if (!refresh_lock(lock)) { - fprintf(stderr, - "Unable to refresh lock for %s\n", - lock->url); - aborted = 1; - return; - } - } - lock = lock->next; - } -} - -static void release_request(struct transfer_request *request) -{ - struct transfer_request *entry = request_queue_head; - - if (request == request_queue_head) { - request_queue_head = request->next; - } else { - while (entry && entry->next != request) - entry = entry->next; - if (entry) - entry->next = request->next; - } - - free(request->url); - free(request); -} - -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 and used during FETCH */ - if (request->state != RUN_PUT && request->state != RUN_FETCH_PACKED) { - 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; - else - http_install_packfile(request->target, &repo->packs); - release_request(request); - } -} - -#ifdef USE_CURL_MULTI -static int is_running_queue; -static int fill_active_slot(void *unused) -{ - struct transfer_request *request; - - if (aborted || !is_running_queue) - return 0; - - for (request = request_queue_head; request; request = request->next) { - if (request->state == NEED_FETCH) { - start_fetch_loose(request); - return 1; - } else if (pushing && request->state == NEED_PUSH) { - if (remote_dir_exists[request->obj->oid.hash[0]] == 1) { - start_put(request); - } else { - start_mkcol(request); - } - return 1; - } - } - return 0; -} -#endif - -static void get_remote_object_list(unsigned char parent); - -static void add_fetch_request(struct object *obj) -{ - struct transfer_request *request; - - check_locks(); - - /* - * Don't fetch the object if it's known to exist locally - * or is already in the request queue - */ - if (remote_dir_exists[obj->oid.hash[0]] == -1) - get_remote_object_list(obj->oid.hash[0]); - if (obj->flags & (LOCAL | FETCHING)) - return; - - obj->flags |= FETCHING; - request = xmalloc(sizeof(*request)); - request->obj = obj; - request->url = NULL; - request->lock = NULL; - request->headers = NULL; - request->state = NEED_FETCH; - request->next = request_queue_head; - request_queue_head = request; - -#ifdef USE_CURL_MULTI - fill_active_slots(); - step_active_slots(); -#endif -} - -static int add_send_request(struct object *obj, struct remote_lock *lock) -{ - struct transfer_request *request; - struct packed_git *target; - - /* Keep locks active */ - check_locks(); - - /* - * Don't push the object if it's known to exist on the remote - * or is already in the request queue - */ - if (remote_dir_exists[obj->oid.hash[0]] == -1) - get_remote_object_list(obj->oid.hash[0]); - if (obj->flags & (REMOTE | PUSHING)) - return 0; - target = find_sha1_pack(obj->oid.hash, repo->packs); - if (target) { - obj->flags |= REMOTE; - return 0; - } - - obj->flags |= PUSHING; - request = xmalloc(sizeof(*request)); - request->obj = obj; - request->url = NULL; - request->lock = lock; - request->headers = NULL; - request->state = NEED_PUSH; - request->next = request_queue_head; - request_queue_head = request; - -#ifdef USE_CURL_MULTI - fill_active_slots(); - step_active_slots(); -#endif - - return 1; -} - -static int fetch_indices(void) -{ - int ret; - - if (push_verbosely) - fprintf(stderr, "Getting pack list\n"); - - switch (http_get_info_packs(repo->url, &repo->packs)) { - case HTTP_OK: - case HTTP_MISSING_TARGET: - ret = 0; - break; - default: - ret = -1; - } - - return ret; -} - -static void one_remote_object(const struct object_id *oid) -{ - struct object *obj; - - obj = lookup_object(the_repository, oid); - if (!obj) - obj = parse_object(the_repository, oid); - - /* Ignore remote objects that don't exist locally */ - if (!obj) - return; - - obj->flags |= REMOTE; - if (!object_list_contains(objects, obj)) - object_list_insert(obj, &objects); -} - -static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) -{ - int *lock_flags = (int *)ctx->userData; - - if (tag_closed) { - if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) { - if ((*lock_flags & DAV_PROP_LOCKEX) && - (*lock_flags & DAV_PROP_LOCKWR)) { - *lock_flags |= DAV_LOCK_OK; - } - *lock_flags &= DAV_LOCK_OK; - } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) { - *lock_flags |= DAV_PROP_LOCKWR; - } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) { - *lock_flags |= DAV_PROP_LOCKEX; - } - } -} - -static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) -{ - struct remote_lock *lock = (struct remote_lock *)ctx->userData; - git_hash_ctx hash_ctx; - unsigned char lock_token_hash[GIT_MAX_RAWSZ]; - - if (tag_closed && ctx->cdata) { - if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) { - lock->owner = xstrdup(ctx->cdata); - } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) { - const char *arg; - if (skip_prefix(ctx->cdata, "Second-", &arg)) - lock->timeout = strtol(arg, NULL, 10); - } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { - lock->token = xstrdup(ctx->cdata); - - the_hash_algo->init_fn(&hash_ctx); - the_hash_algo->update_fn(&hash_ctx, lock->token, strlen(lock->token)); - the_hash_algo->final_fn(lock_token_hash, &hash_ctx); - - lock->tmpfile_suffix[0] = '_'; - memcpy(lock->tmpfile_suffix + 1, hash_to_hex(lock_token_hash), the_hash_algo->hexsz); - } - } -} - -static void one_remote_ref(const char *refname); - -static void -xml_start_tag(void *userData, const char *name, const char **atts) -{ - struct xml_ctx *ctx = (struct xml_ctx *)userData; - const char *c = strchr(name, ':'); - int old_namelen, new_len; - - if (c == NULL) - c = name; - else - c++; - - old_namelen = strlen(ctx->name); - new_len = old_namelen + strlen(c) + 2; - - if (new_len > ctx->len) { - ctx->name = xrealloc(ctx->name, new_len); - ctx->len = new_len; - } - xsnprintf(ctx->name + old_namelen, ctx->len - old_namelen, ".%s", c); - - FREE_AND_NULL(ctx->cdata); - - ctx->userFunc(ctx, 0); -} - -static void -xml_end_tag(void *userData, const char *name) -{ - struct xml_ctx *ctx = (struct xml_ctx *)userData; - const char *c = strchr(name, ':'); - char *ep; - - ctx->userFunc(ctx, 1); - - if (c == NULL) - c = name; - else - c++; - - ep = ctx->name + strlen(ctx->name) - strlen(c) - 1; - *ep = 0; -} - -static void -xml_cdata(void *userData, const XML_Char *s, int len) -{ - struct xml_ctx *ctx = (struct xml_ctx *)userData; - free(ctx->cdata); - ctx->cdata = xmemdupz(s, len); -} - -static struct remote_lock *lock_remote(const char *path, long timeout) -{ - struct active_request_slot *slot; - struct slot_results results; - struct buffer out_buffer = { STRBUF_INIT, 0 }; - struct strbuf in_buffer = STRBUF_INIT; - char *url; - char *ep; - char timeout_header[25]; - struct remote_lock *lock = NULL; - struct curl_slist *dav_headers = http_copy_default_headers(); - struct xml_ctx ctx; - char *escaped; - - url = xstrfmt("%s%s", repo->url, path); - - /* Make sure leading directories exist for the remote ref */ - ep = strchr(url + strlen(repo->url) + 1, '/'); - while (ep) { - char saved_character = ep[1]; - ep[1] = '\0'; - slot = get_active_slot(); - slot->results = &results; - curl_setup_http_get(slot->curl, url, DAV_MKCOL); - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result != CURLE_OK && - results.http_code != 405) { - fprintf(stderr, - "Unable to create branch path %s\n", - url); - free(url); - return NULL; - } - } else { - fprintf(stderr, "Unable to start MKCOL request\n"); - free(url); - return NULL; - } - ep[1] = saved_character; - ep = strchr(ep + 1, '/'); - } - - escaped = xml_entities(ident_default_email()); - strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped); - free(escaped); - - xsnprintf(timeout_header, sizeof(timeout_header), "Timeout: Second-%ld", timeout); - dav_headers = curl_slist_append(dav_headers, timeout_header); - dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http(slot->curl, url, DAV_LOCK, &out_buffer, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - - lock = xcalloc(1, sizeof(*lock)); - lock->timeout = -1; - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result == CURLE_OK) { - XML_Parser parser = XML_ParserCreate(NULL); - enum XML_Status result; - ctx.name = xcalloc(10, 1); - ctx.len = 0; - ctx.cdata = NULL; - ctx.userFunc = handle_new_lock_ctx; - ctx.userData = lock; - XML_SetUserData(parser, &ctx); - XML_SetElementHandler(parser, xml_start_tag, - xml_end_tag); - XML_SetCharacterDataHandler(parser, xml_cdata); - result = XML_Parse(parser, in_buffer.buf, - in_buffer.len, 1); - free(ctx.name); - if (result != XML_STATUS_OK) { - fprintf(stderr, "XML error: %s\n", - XML_ErrorString( - XML_GetErrorCode(parser))); - lock->timeout = -1; - } - XML_ParserFree(parser); - } else { - fprintf(stderr, - "error: curl result=%d, HTTP code=%ld\n", - results.curl_result, results.http_code); - } - } else { - fprintf(stderr, "Unable to start LOCK request\n"); - } - - curl_slist_free_all(dav_headers); - strbuf_release(&out_buffer.buf); - strbuf_release(&in_buffer); - - if (lock->token == NULL || lock->timeout <= 0) { - free(lock->token); - free(lock->owner); - free(url); - FREE_AND_NULL(lock); - } else { - lock->url = url; - lock->start_time = time(NULL); - lock->next = repo->locks; - repo->locks = lock; - } - - return lock; -} - -static int unlock_remote(struct remote_lock *lock) -{ - struct active_request_slot *slot; - struct slot_results results; - struct remote_lock *prev = repo->locks; - struct curl_slist *dav_headers; - int rc = 0; - - dav_headers = get_dav_token_headers(lock, DAV_HEADER_LOCK); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http_get(slot->curl, lock->url, DAV_UNLOCK); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result == CURLE_OK) - rc = 1; - else - fprintf(stderr, "UNLOCK HTTP error %ld\n", - results.http_code); - } else { - fprintf(stderr, "Unable to start UNLOCK request\n"); - } - - curl_slist_free_all(dav_headers); - - if (repo->locks == lock) { - repo->locks = lock->next; - } else { - while (prev && prev->next != lock) - prev = prev->next; - if (prev) - prev->next = lock->next; - } - - free(lock->owner); - free(lock->url); - free(lock->token); - free(lock); - - return rc; -} - -static void remove_locks(void) -{ - struct remote_lock *lock = repo->locks; - - fprintf(stderr, "Removing remote locks...\n"); - while (lock) { - struct remote_lock *next = lock->next; - unlock_remote(lock); - lock = next; - } -} - -static void remove_locks_on_signal(int signo) -{ - remove_locks(); - sigchain_pop(signo); - raise(signo); -} - -static void remote_ls(const char *path, int flags, - void (*userFunc)(struct remote_ls_ctx *ls), - void *userData); - -/* extract hex from sharded "xx/x{38}" filename */ -static int get_oid_hex_from_objpath(const char *path, struct object_id *oid) -{ - if (strlen(path) != the_hash_algo->hexsz + 1) - return -1; - - if (hex_to_bytes(oid->hash, path, 1)) - return -1; - path += 2; - path++; /* skip '/' */ - - return hex_to_bytes(oid->hash + 1, path, the_hash_algo->rawsz - 1); -} - -static void process_ls_object(struct remote_ls_ctx *ls) -{ - unsigned int *parent = (unsigned int *)ls->userData; - const char *path = ls->dentry_name; - struct object_id oid; - - if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) { - remote_dir_exists[*parent] = 1; - return; - } - - if (!skip_prefix(path, "objects/", &path) || - get_oid_hex_from_objpath(path, &oid)) - return; - - one_remote_object(&oid); -} - -static void process_ls_ref(struct remote_ls_ctx *ls) -{ - if (!strcmp(ls->path, ls->dentry_name) && (ls->dentry_flags & IS_DIR)) { - fprintf(stderr, " %s\n", ls->dentry_name); - return; - } - - if (!(ls->dentry_flags & IS_DIR)) - one_remote_ref(ls->dentry_name); -} - -static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed) -{ - struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData; - - if (tag_closed) { - if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) { - if (ls->dentry_flags & IS_DIR) { - - /* ensure collection names end with slash */ - str_end_url_with_slash(ls->dentry_name, &ls->dentry_name); - - if (ls->flags & PROCESS_DIRS) { - ls->userFunc(ls); - } - if (strcmp(ls->dentry_name, ls->path) && - ls->flags & RECURSIVE) { - remote_ls(ls->dentry_name, - ls->flags, - ls->userFunc, - ls->userData); - } - } else if (ls->flags & PROCESS_FILES) { - ls->userFunc(ls); - } - } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) { - char *path = ctx->cdata; - if (*ctx->cdata == 'h') { - path = strstr(path, "//"); - if (path) { - path = strchr(path+2, '/'); - } - } - if (path) { - const char *url = repo->url; - if (repo->path) - url = repo->path; - if (strncmp(path, url, repo->path_len)) - error("Parsed path '%s' does not match url: '%s'", - path, url); - else { - path += repo->path_len; - ls->dentry_name = xstrdup(path); - } - } - } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) { - ls->dentry_flags |= IS_DIR; - } - } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) { - FREE_AND_NULL(ls->dentry_name); - ls->dentry_flags = 0; - } -} - -/* - * NEEDSWORK: remote_ls() ignores info/refs on the remote side. But it - * should _only_ heed the information from that file, instead of trying to - * determine the refs from the remote file system (badly: it does not even - * know about packed-refs). - */ -static void remote_ls(const char *path, int flags, - void (*userFunc)(struct remote_ls_ctx *ls), - void *userData) -{ - char *url = xstrfmt("%s%s", repo->url, path); - struct active_request_slot *slot; - struct slot_results results; - struct strbuf in_buffer = STRBUF_INIT; - struct buffer out_buffer = { STRBUF_INIT, 0 }; - struct curl_slist *dav_headers = http_copy_default_headers(); - struct xml_ctx ctx; - struct remote_ls_ctx ls; - - ls.flags = flags; - ls.path = xstrdup(path); - ls.dentry_name = NULL; - ls.dentry_flags = 0; - ls.userData = userData; - ls.userFunc = userFunc; - - strbuf_addstr(&out_buffer.buf, PROPFIND_ALL_REQUEST); - - dav_headers = curl_slist_append(dav_headers, "Depth: 1"); - dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http(slot->curl, url, DAV_PROPFIND, - &out_buffer, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result == CURLE_OK) { - XML_Parser parser = XML_ParserCreate(NULL); - enum XML_Status result; - ctx.name = xcalloc(10, 1); - ctx.len = 0; - ctx.cdata = NULL; - ctx.userFunc = handle_remote_ls_ctx; - ctx.userData = &ls; - XML_SetUserData(parser, &ctx); - XML_SetElementHandler(parser, xml_start_tag, - xml_end_tag); - XML_SetCharacterDataHandler(parser, xml_cdata); - result = XML_Parse(parser, in_buffer.buf, - in_buffer.len, 1); - free(ctx.name); - - if (result != XML_STATUS_OK) { - fprintf(stderr, "XML error: %s\n", - XML_ErrorString( - XML_GetErrorCode(parser))); - } - XML_ParserFree(parser); - } - } else { - fprintf(stderr, "Unable to start PROPFIND request\n"); - } - - free(ls.path); - free(url); - strbuf_release(&out_buffer.buf); - strbuf_release(&in_buffer); - curl_slist_free_all(dav_headers); -} - -static void get_remote_object_list(unsigned char parent) -{ - char path[] = "objects/XX/"; - static const char hex[] = "0123456789abcdef"; - unsigned int val = parent; - - path[8] = hex[val >> 4]; - path[9] = hex[val & 0xf]; - remote_dir_exists[val] = 0; - remote_ls(path, (PROCESS_FILES | PROCESS_DIRS), - process_ls_object, &val); -} - -static int locking_available(void) -{ - struct active_request_slot *slot; - struct slot_results results; - struct strbuf in_buffer = STRBUF_INIT; - struct buffer out_buffer = { STRBUF_INIT, 0 }; - struct curl_slist *dav_headers = http_copy_default_headers(); - struct xml_ctx ctx; - int lock_flags = 0; - char *escaped; - - escaped = xml_entities(repo->url); - strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, escaped); - free(escaped); - - dav_headers = curl_slist_append(dav_headers, "Depth: 0"); - dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http(slot->curl, repo->url, DAV_PROPFIND, - &out_buffer, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result == CURLE_OK) { - XML_Parser parser = XML_ParserCreate(NULL); - enum XML_Status result; - ctx.name = xcalloc(10, 1); - ctx.len = 0; - ctx.cdata = NULL; - ctx.userFunc = handle_lockprop_ctx; - ctx.userData = &lock_flags; - XML_SetUserData(parser, &ctx); - XML_SetElementHandler(parser, xml_start_tag, - xml_end_tag); - result = XML_Parse(parser, in_buffer.buf, - in_buffer.len, 1); - free(ctx.name); - - if (result != XML_STATUS_OK) { - fprintf(stderr, "XML error: %s\n", - XML_ErrorString( - XML_GetErrorCode(parser))); - lock_flags = 0; - } - XML_ParserFree(parser); - if (!lock_flags) - error("no DAV locking support on %s", - repo->url); - - } else { - error("Cannot access URL %s, return code %d", - repo->url, results.curl_result); - lock_flags = 0; - } - } else { - error("Unable to start PROPFIND request on %s", repo->url); - } - - strbuf_release(&out_buffer.buf); - strbuf_release(&in_buffer); - curl_slist_free_all(dav_headers); - - return lock_flags; -} - -static struct object_list **add_one_object(struct object *obj, struct object_list **p) -{ - struct object_list *entry = xmalloc(sizeof(struct object_list)); - entry->item = obj; - entry->next = *p; - *p = entry; - return &entry->next; -} - -static struct object_list **process_blob(struct blob *blob, - struct object_list **p) -{ - struct object *obj = &blob->object; - - obj->flags |= LOCAL; - - if (obj->flags & (UNINTERESTING | SEEN)) - return p; - - obj->flags |= SEEN; - return add_one_object(obj, p); -} - -static struct object_list **process_tree(struct tree *tree, - struct object_list **p) -{ - struct object *obj = &tree->object; - struct tree_desc desc; - struct name_entry entry; - - obj->flags |= LOCAL; - - if (obj->flags & (UNINTERESTING | SEEN)) - return p; - if (parse_tree(tree) < 0) - die("bad tree object %s", oid_to_hex(&obj->oid)); - - obj->flags |= SEEN; - p = add_one_object(obj, p); - - init_tree_desc(&desc, tree->buffer, tree->size); - - while (tree_entry(&desc, &entry)) - switch (object_type(entry.mode)) { - case OBJ_TREE: - p = process_tree(lookup_tree(the_repository, &entry.oid), - p); - break; - case OBJ_BLOB: - p = process_blob(lookup_blob(the_repository, &entry.oid), - p); - break; - default: - /* Subproject commit - not in this repository */ - break; - } - - free_tree_buffer(tree); - return p; -} - -static int get_delta(struct rev_info *revs, struct remote_lock *lock) -{ - int i; - struct commit *commit; - struct object_list **p = &objects; - int count = 0; - - while ((commit = get_revision(revs)) != NULL) { - p = process_tree(get_commit_tree(commit), p); - commit->object.flags |= LOCAL; - if (!(commit->object.flags & UNINTERESTING)) - count += add_send_request(&commit->object, lock); - } - - for (i = 0; i < revs->pending.nr; i++) { - struct object_array_entry *entry = revs->pending.objects + i; - struct object *obj = entry->item; - const char *name = entry->name; - - if (obj->flags & (UNINTERESTING | SEEN)) - continue; - if (obj->type == OBJ_TAG) { - obj->flags |= SEEN; - p = add_one_object(obj, p); - continue; - } - if (obj->type == OBJ_TREE) { - p = process_tree((struct tree *)obj, p); - continue; - } - if (obj->type == OBJ_BLOB) { - p = process_blob((struct blob *)obj, p); - continue; - } - die("unknown pending object %s (%s)", oid_to_hex(&obj->oid), name); - } - - while (objects) { - if (!(objects->item->flags & UNINTERESTING)) - count += add_send_request(objects->item, lock); - objects = objects->next; - } - - return count; -} - -static int update_remote(const struct object_id *oid, struct remote_lock *lock) -{ - struct active_request_slot *slot; - struct slot_results results; - struct buffer out_buffer = { STRBUF_INIT, 0 }; - struct curl_slist *dav_headers; - - dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF); - - strbuf_addf(&out_buffer.buf, "%s\n", oid_to_hex(oid)); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http(slot->curl, lock->url, DAV_PUT, - &out_buffer, fwrite_null); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - - if (start_active_slot(slot)) { - run_active_slot(slot); - strbuf_release(&out_buffer.buf); - if (results.curl_result != CURLE_OK) { - fprintf(stderr, - "PUT error: curl result=%d, HTTP code=%ld\n", - results.curl_result, results.http_code); - /* We should attempt recovery? */ - return 0; - } - } else { - strbuf_release(&out_buffer.buf); - fprintf(stderr, "Unable to start PUT request\n"); - return 0; - } - - return 1; -} - -static struct ref *remote_refs; - -static void one_remote_ref(const char *refname) -{ - struct ref *ref; - struct object *obj; - - ref = alloc_ref(refname); - - if (http_fetch_ref(repo->url, ref) != 0) { - fprintf(stderr, - "Unable to fetch ref %s from %s\n", - refname, repo->url); - free(ref); - return; - } - - /* - * Fetch a copy of the object if it doesn't exist locally - it - * may be required for updating server info later. - */ - if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) { - obj = lookup_unknown_object(&ref->old_oid); - fprintf(stderr, " fetch %s for %s\n", - oid_to_hex(&ref->old_oid), refname); - add_fetch_request(obj); - } - - ref->next = remote_refs; - remote_refs = ref; -} - -static void get_dav_remote_heads(void) -{ - remote_ls("refs/", (PROCESS_FILES | PROCESS_DIRS | RECURSIVE), process_ls_ref, NULL); -} - -static void add_remote_info_ref(struct remote_ls_ctx *ls) -{ - struct strbuf *buf = (struct strbuf *)ls->userData; - struct object *o; - struct ref *ref; - - ref = alloc_ref(ls->dentry_name); - - if (http_fetch_ref(repo->url, ref) != 0) { - fprintf(stderr, - "Unable to fetch ref %s from %s\n", - ls->dentry_name, repo->url); - aborted = 1; - free(ref); - return; - } - - o = parse_object(the_repository, &ref->old_oid); - if (!o) { - fprintf(stderr, - "Unable to parse object %s for remote ref %s\n", - oid_to_hex(&ref->old_oid), ls->dentry_name); - aborted = 1; - free(ref); - return; - } - - strbuf_addf(buf, "%s\t%s\n", - oid_to_hex(&ref->old_oid), ls->dentry_name); - - if (o->type == OBJ_TAG) { - o = deref_tag(the_repository, o, ls->dentry_name, 0); - if (o) - strbuf_addf(buf, "%s\t%s^{}\n", - oid_to_hex(&o->oid), ls->dentry_name); - } - free(ref); -} - -static void update_remote_info_refs(struct remote_lock *lock) -{ - struct buffer buffer = { STRBUF_INIT, 0 }; - struct active_request_slot *slot; - struct slot_results results; - struct curl_slist *dav_headers; - - remote_ls("refs/", (PROCESS_FILES | RECURSIVE), - add_remote_info_ref, &buffer.buf); - if (!aborted) { - dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF); - - slot = get_active_slot(); - slot->results = &results; - curl_setup_http(slot->curl, lock->url, DAV_PUT, - &buffer, fwrite_null); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result != CURLE_OK) { - fprintf(stderr, - "PUT error: curl result=%d, HTTP code=%ld\n", - results.curl_result, results.http_code); - } - } - } - strbuf_release(&buffer.buf); -} - -static int remote_exists(const char *path) -{ - char *url = xstrfmt("%s%s", repo->url, path); - int ret; - - - switch (http_get_strbuf(url, NULL, NULL)) { - case HTTP_OK: - ret = 1; - break; - case HTTP_MISSING_TARGET: - ret = 0; - break; - case HTTP_ERROR: - error("unable to access '%s': %s", url, curl_errorstr); - /* fallthrough */ - default: - ret = -1; - } - free(url); - return ret; -} - -static void fetch_symref(const char *path, char **symref, struct object_id *oid) -{ - char *url = xstrfmt("%s%s", repo->url, path); - struct strbuf buffer = STRBUF_INIT; - const char *name; - - if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK) - die("Couldn't get %s for remote symref\n%s", url, - curl_errorstr); - free(url); - - FREE_AND_NULL(*symref); - oidclr(oid); - - if (buffer.len == 0) - return; - - /* Cut off trailing newline. */ - strbuf_rtrim(&buffer); - - /* If it's a symref, set the refname; otherwise try for a sha1 */ - if (skip_prefix(buffer.buf, "ref: ", &name)) { - *symref = xmemdupz(name, buffer.len - (name - buffer.buf)); - } else { - get_oid_hex(buffer.buf, oid); - } - - strbuf_release(&buffer); -} - -static int verify_merge_base(struct object_id *head_oid, struct ref *remote) -{ - struct commit *head = lookup_commit_or_die(head_oid, "HEAD"); - struct commit *branch = lookup_commit_or_die(&remote->old_oid, - remote->name); - - return in_merge_bases(branch, head); -} - -static int delete_remote_branch(const char *pattern, int force) -{ - struct ref *refs = remote_refs; - struct ref *remote_ref = NULL; - struct object_id head_oid; - char *symref = NULL; - int match; - int patlen = strlen(pattern); - int i; - struct active_request_slot *slot; - struct slot_results results; - char *url; - - /* Find the remote branch(es) matching the specified branch name */ - for (match = 0; refs; refs = refs->next) { - char *name = refs->name; - int namelen = strlen(name); - if (namelen < patlen || - memcmp(name + namelen - patlen, pattern, patlen)) - continue; - if (namelen != patlen && name[namelen - patlen - 1] != '/') - continue; - match++; - remote_ref = refs; - } - if (match == 0) - return error("No remote branch matches %s", pattern); - if (match != 1) - return error("More than one remote branch matches %s", - pattern); - - /* - * Remote HEAD must be a symref (not exactly foolproof; a remote - * symlink to a symref will look like a symref) - */ - fetch_symref("HEAD", &symref, &head_oid); - if (!symref) - return error("Remote HEAD is not a symref"); - - /* Remote branch must not be the remote HEAD */ - for (i = 0; symref && i < MAXDEPTH; i++) { - if (!strcmp(remote_ref->name, symref)) - return error("Remote branch %s is the current HEAD", - remote_ref->name); - fetch_symref(symref, &symref, &head_oid); - } - - /* Run extra sanity checks if delete is not forced */ - if (!force) { - /* Remote HEAD must resolve to a known object */ - if (symref) - return error("Remote HEAD symrefs too deep"); - if (is_null_oid(&head_oid)) - return error("Unable to resolve remote HEAD"); - if (!has_object_file(&head_oid)) - return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid)); - - /* Remote branch must resolve to a known object */ - if (is_null_oid(&remote_ref->old_oid)) - return error("Unable to resolve remote branch %s", - remote_ref->name); - if (!has_object_file(&remote_ref->old_oid)) - return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid)); - - /* Remote branch must be an ancestor of remote HEAD */ - if (!verify_merge_base(&head_oid, remote_ref)) { - return error("The branch '%s' is not an ancestor " - "of your current HEAD.\n" - "If you are sure you want to delete it," - " run:\n\t'git http-push -D %s %s'", - remote_ref->name, repo->url, pattern); - } - } - - /* Send delete request */ - fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name); - if (dry_run) - return 0; - url = xstrfmt("%s%s", repo->url, remote_ref->name); - slot = get_active_slot(); - slot->results = &results; - curl_setup_http_get(slot->curl, url, DAV_DELETE); - if (start_active_slot(slot)) { - run_active_slot(slot); - free(url); - if (results.curl_result != CURLE_OK) - return error("DELETE request failed (%d/%ld)", - results.curl_result, results.http_code); - } else { - free(url); - return error("Unable to start DELETE request"); - } - - return 0; -} - -static void run_request_queue(void) -{ -#ifdef USE_CURL_MULTI - is_running_queue = 1; - fill_active_slots(); - add_fill_function(NULL, fill_active_slot); -#endif - do { - finish_all_active_slots(); -#ifdef USE_CURL_MULTI - fill_active_slots(); -#endif - } while (request_queue_head && !aborted); - -#ifdef USE_CURL_MULTI - is_running_queue = 0; -#endif -} - -int cmd_main(int argc, const char **argv) -{ - struct transfer_request *request; - struct transfer_request *next_request; - struct refspec rs = REFSPEC_INIT_PUSH; - 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_appendn(&rs, argv, 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 && rs.nr != 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) { - const char *branch = rs.items[i].src; - if (delete_remote_branch(branch, force_delete) == -1) { - fprintf(stderr, "Unable to delete remote branch %s\n", - branch); - if (helper_status) - printf("error %s cannot remove\n", branch); - } - goto cleanup; - } - - /* match them up */ - if (match_push_refs(local_refs, &remote_refs, &rs, 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 strvec commit_argv = STRVEC_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 (oideq(&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 */ - strvec_push(&commit_argv, ""); /* ignored */ - strvec_push(&commit_argv, "--objects"); - strvec_push(&commit_argv, oid_to_hex(&ref->new_oid)); - if (!push_all && !is_null_oid(&ref->old_oid)) - strvec_pushf(&commit_argv, "^%s", - oid_to_hex(&ref->old_oid)); - repo_init_revisions(the_repository, &revs, setup_git_directory()); - setup_revisions(commit_argv.nr, commit_argv.v, &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, 0); - 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, 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(); - strvec_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; -} |