git
/
http-push.c
1987 строк · 50.3 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "environment.h"5#include "hex.h"6#include "repository.h"7#include "commit.h"8#include "tag.h"9#include "blob.h"10#include "http.h"11#include "diff.h"12#include "revision.h"13#include "remote.h"14#include "list-objects.h"15#include "setup.h"16#include "sigchain.h"17#include "strvec.h"18#include "tree.h"19#include "tree-walk.h"20#include "url.h"21#include "packfile.h"22#include "object-store-ll.h"23#include "commit-reach.h"24
25#ifdef EXPAT_NEEDS_XMLPARSE_H26#include <xmlparse.h>27#else28#include <expat.h>29#endif30
31static const char http_push_usage[] =32"git http-push [--all] [--dry-run] [--force] [--verbose] <remote> [<head>...]\n";33
34#ifndef XML_STATUS_OK35enum XML_Status {36XML_STATUS_OK = 1,37XML_STATUS_ERROR = 038};39#define XML_STATUS_OK 140#define XML_STATUS_ERROR 041#endif42
43#define PREV_BUF_SIZE 409644
45/* DAV methods */
46#define DAV_LOCK "LOCK"47#define DAV_MKCOL "MKCOL"48#define DAV_MOVE "MOVE"49#define DAV_PROPFIND "PROPFIND"50#define DAV_PUT "PUT"51#define DAV_UNLOCK "UNLOCK"52#define DAV_DELETE "DELETE"53
54/* DAV lock flags */
55#define DAV_PROP_LOCKWR (1u << 0)56#define DAV_PROP_LOCKEX (1u << 1)57#define DAV_LOCK_OK (1u << 2)58
59/* DAV XML properties */
60#define DAV_CTX_LOCKENTRY ".multistatus.response.propstat.prop.supportedlock.lockentry"61#define DAV_CTX_LOCKTYPE_WRITE ".multistatus.response.propstat.prop.supportedlock.lockentry.locktype.write"62#define DAV_CTX_LOCKTYPE_EXCLUSIVE ".multistatus.response.propstat.prop.supportedlock.lockentry.lockscope.exclusive"63#define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href"64#define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout"65#define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href"66#define DAV_PROPFIND_RESP ".multistatus.response"67#define DAV_PROPFIND_NAME ".multistatus.response.href"68#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"69
70/* DAV request body templates */
71#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>"72#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"73#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>"74
75#define LOCK_TIME 60076#define LOCK_REFRESH 3077
78/* Remember to update object flag allocation in object.h */
79#define LOCAL (1u<<11)80#define REMOTE (1u<<12)81#define FETCHING (1u<<13)82#define PUSHING (1u<<14)83
84/* We allow "recursive" symbolic refs. Only within reason, though */
85#define MAXDEPTH 586
87static int pushing;88static int aborted;89static signed char remote_dir_exists[256];90
91static int push_verbosely;92static int push_all = MATCH_REFS_NONE;93static int force_all;94static int dry_run;95static int helper_status;96
97static struct object_list *objects;98
99struct repo {100char *url;101char *path;102int path_len;103int has_info_refs;104int can_update_info_refs;105int has_info_packs;106struct packed_git *packs;107struct remote_lock *locks;108};109
110static struct repo *repo;111
112enum transfer_state {113NEED_FETCH,114RUN_FETCH_LOOSE,115RUN_FETCH_PACKED,116NEED_PUSH,117RUN_MKCOL,118RUN_PUT,119RUN_MOVE,120ABORTED,121COMPLETE
122};123
124struct transfer_request {125struct object *obj;126struct packed_git *target;127char *url;128char *dest;129struct remote_lock *lock;130struct curl_slist *headers;131struct buffer buffer;132enum transfer_state state;133CURLcode curl_result;134char errorstr[CURL_ERROR_SIZE];135long http_code;136void *userData;137struct active_request_slot *slot;138struct transfer_request *next;139};140
141static struct transfer_request *request_queue_head;142
143struct xml_ctx {144char *name;145int len;146char *cdata;147void (*userFunc)(struct xml_ctx *ctx, int tag_closed);148void *userData;149};150
151struct remote_lock {152char *url;153char *owner;154char *token;155char tmpfile_suffix[GIT_MAX_HEXSZ + 1];156time_t start_time;157long timeout;158int refreshing;159struct remote_lock *next;160};161
162/* Flags that control remote_ls processing */
163#define PROCESS_FILES (1u << 0)164#define PROCESS_DIRS (1u << 1)165#define RECURSIVE (1u << 2)166
167/* Flags that remote_ls passes to callback functions */
168#define IS_DIR (1u << 0)169
170struct remote_ls_ctx {171char *path;172void (*userFunc)(struct remote_ls_ctx *ls);173void *userData;174int flags;175char *dentry_name;176int dentry_flags;177struct remote_ls_ctx *parent;178};179
180/* get_dav_token_headers options */
181enum dav_header_flag {182DAV_HEADER_IF = (1u << 0),183DAV_HEADER_LOCK = (1u << 1),184DAV_HEADER_TIMEOUT = (1u << 2)185};186
187static char *xml_entities(const char *s)188{
189struct strbuf buf = STRBUF_INIT;190strbuf_addstr_xml_quoted(&buf, s);191return strbuf_detach(&buf, NULL);192}
193
194static void curl_setup_http_get(CURL *curl, const char *url,195const char *custom_req)196{
197curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);198curl_easy_setopt(curl, CURLOPT_URL, url);199curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req);200curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_null);201}
202
203static void curl_setup_http(CURL *curl, const char *url,204const char *custom_req, struct buffer *buffer,205curl_write_callback write_fn)206{
207curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);208curl_easy_setopt(curl, CURLOPT_URL, url);209curl_easy_setopt(curl, CURLOPT_INFILE, buffer);210curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->buf.len);211curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);212curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, seek_buffer);213curl_easy_setopt(curl, CURLOPT_SEEKDATA, buffer);214curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);215curl_easy_setopt(curl, CURLOPT_NOBODY, 0);216curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req);217curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);218}
219
220static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)221{
222struct strbuf buf = STRBUF_INIT;223struct curl_slist *dav_headers = http_copy_default_headers();224
225if (options & DAV_HEADER_IF) {226strbuf_addf(&buf, "If: (<%s>)", lock->token);227dav_headers = curl_slist_append(dav_headers, buf.buf);228strbuf_reset(&buf);229}230if (options & DAV_HEADER_LOCK) {231strbuf_addf(&buf, "Lock-Token: <%s>", lock->token);232dav_headers = curl_slist_append(dav_headers, buf.buf);233strbuf_reset(&buf);234}235if (options & DAV_HEADER_TIMEOUT) {236strbuf_addf(&buf, "Timeout: Second-%ld", lock->timeout);237dav_headers = curl_slist_append(dav_headers, buf.buf);238strbuf_reset(&buf);239}240strbuf_release(&buf);241
242return dav_headers;243}
244
245static void finish_request(struct transfer_request *request);246static void release_request(struct transfer_request *request);247
248static void process_response(void *callback_data)249{
250struct transfer_request *request =251(struct transfer_request *)callback_data;252
253finish_request(request);254}
255
256static void start_fetch_loose(struct transfer_request *request)257{
258struct active_request_slot *slot;259struct http_object_request *obj_req;260
261obj_req = new_http_object_request(repo->url, &request->obj->oid);262if (!obj_req) {263request->state = ABORTED;264return;265}266
267slot = obj_req->slot;268slot->callback_func = process_response;269slot->callback_data = request;270request->slot = slot;271request->userData = obj_req;272
273/* Try to get the request started, abort the request on error */274request->state = RUN_FETCH_LOOSE;275if (!start_active_slot(slot)) {276fprintf(stderr, "Unable to start GET request\n");277repo->can_update_info_refs = 0;278release_http_object_request(obj_req);279release_request(request);280}281}
282
283static void start_mkcol(struct transfer_request *request)284{
285char *hex = oid_to_hex(&request->obj->oid);286struct active_request_slot *slot;287
288request->url = get_remote_object_url(repo->url, hex, 1);289
290slot = get_active_slot();291slot->callback_func = process_response;292slot->callback_data = request;293curl_setup_http_get(slot->curl, request->url, DAV_MKCOL);294curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);295
296if (start_active_slot(slot)) {297request->slot = slot;298request->state = RUN_MKCOL;299} else {300request->state = ABORTED;301FREE_AND_NULL(request->url);302}303}
304
305static void start_fetch_packed(struct transfer_request *request)306{
307struct packed_git *target;308
309struct transfer_request *check_request = request_queue_head;310struct http_pack_request *preq;311
312target = find_sha1_pack(request->obj->oid.hash, repo->packs);313if (!target) {314fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", oid_to_hex(&request->obj->oid));315repo->can_update_info_refs = 0;316release_request(request);317return;318}319close_pack_index(target);320request->target = target;321
322fprintf(stderr, "Fetching pack %s\n",323hash_to_hex(target->hash));324fprintf(stderr, " which contains %s\n", oid_to_hex(&request->obj->oid));325
326preq = new_http_pack_request(target->hash, repo->url);327if (!preq) {328repo->can_update_info_refs = 0;329return;330}331
332/* Make sure there isn't another open request for this pack */333while (check_request) {334if (check_request->state == RUN_FETCH_PACKED &&335!strcmp(check_request->url, preq->url)) {336release_http_pack_request(preq);337release_request(request);338return;339}340check_request = check_request->next;341}342
343preq->slot->callback_func = process_response;344preq->slot->callback_data = request;345request->slot = preq->slot;346request->userData = preq;347
348/* Try to get the request started, abort the request on error */349request->state = RUN_FETCH_PACKED;350if (!start_active_slot(preq->slot)) {351fprintf(stderr, "Unable to start GET request\n");352release_http_pack_request(preq);353repo->can_update_info_refs = 0;354release_request(request);355}356}
357
358static void start_put(struct transfer_request *request)359{
360char *hex = oid_to_hex(&request->obj->oid);361struct active_request_slot *slot;362struct strbuf buf = STRBUF_INIT;363enum object_type type;364char hdr[50];365void *unpacked;366unsigned long len;367int hdrlen;368ssize_t size;369git_zstream stream;370
371unpacked = repo_read_object_file(the_repository, &request->obj->oid,372&type, &len);373hdrlen = format_object_header(hdr, sizeof(hdr), type, len);374
375/* Set it up */376git_deflate_init(&stream, zlib_compression_level);377size = git_deflate_bound(&stream, len + hdrlen);378strbuf_init(&request->buffer.buf, size);379request->buffer.posn = 0;380
381/* Compress it */382stream.next_out = (unsigned char *)request->buffer.buf.buf;383stream.avail_out = size;384
385/* First header.. */386stream.next_in = (void *)hdr;387stream.avail_in = hdrlen;388while (git_deflate(&stream, 0) == Z_OK)389; /* nothing */390
391/* Then the data itself.. */392stream.next_in = unpacked;393stream.avail_in = len;394while (git_deflate(&stream, Z_FINISH) == Z_OK)395; /* nothing */396git_deflate_end(&stream);397free(unpacked);398
399request->buffer.buf.len = stream.total_out;400
401strbuf_addstr(&buf, "Destination: ");402append_remote_object_url(&buf, repo->url, hex, 0);403request->dest = strbuf_detach(&buf, NULL);404
405append_remote_object_url(&buf, repo->url, hex, 0);406strbuf_add(&buf, request->lock->tmpfile_suffix, the_hash_algo->hexsz + 1);407request->url = strbuf_detach(&buf, NULL);408
409slot = get_active_slot();410slot->callback_func = process_response;411slot->callback_data = request;412curl_setup_http(slot->curl, request->url, DAV_PUT,413&request->buffer, fwrite_null);414
415if (start_active_slot(slot)) {416request->slot = slot;417request->state = RUN_PUT;418} else {419request->state = ABORTED;420FREE_AND_NULL(request->url);421}422}
423
424static void start_move(struct transfer_request *request)425{
426struct active_request_slot *slot;427struct curl_slist *dav_headers = http_copy_default_headers();428
429slot = get_active_slot();430slot->callback_func = process_response;431slot->callback_data = request;432curl_setup_http_get(slot->curl, request->url, DAV_MOVE);433dav_headers = curl_slist_append(dav_headers, request->dest);434dav_headers = curl_slist_append(dav_headers, "Overwrite: T");435curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);436
437if (start_active_slot(slot)) {438request->slot = slot;439request->state = RUN_MOVE;440} else {441request->state = ABORTED;442FREE_AND_NULL(request->url);443}444}
445
446static int refresh_lock(struct remote_lock *lock)447{
448struct active_request_slot *slot;449struct slot_results results;450struct curl_slist *dav_headers;451int rc = 0;452
453lock->refreshing = 1;454
455dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF | DAV_HEADER_TIMEOUT);456
457slot = get_active_slot();458slot->results = &results;459curl_setup_http_get(slot->curl, lock->url, DAV_LOCK);460curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);461
462if (start_active_slot(slot)) {463run_active_slot(slot);464if (results.curl_result != CURLE_OK) {465fprintf(stderr, "LOCK HTTP error %ld\n",466results.http_code);467} else {468lock->start_time = time(NULL);469rc = 1;470}471}472
473lock->refreshing = 0;474curl_slist_free_all(dav_headers);475
476return rc;477}
478
479static void check_locks(void)480{
481struct remote_lock *lock = repo->locks;482time_t current_time = time(NULL);483int time_remaining;484
485while (lock) {486time_remaining = lock->start_time + lock->timeout -487current_time;488if (!lock->refreshing && time_remaining < LOCK_REFRESH) {489if (!refresh_lock(lock)) {490fprintf(stderr,491"Unable to refresh lock for %s\n",492lock->url);493aborted = 1;494return;495}496}497lock = lock->next;498}499}
500
501static void release_request(struct transfer_request *request)502{
503struct transfer_request *entry = request_queue_head;504
505if (request == request_queue_head) {506request_queue_head = request->next;507} else {508while (entry && entry->next != request)509entry = entry->next;510if (entry)511entry->next = request->next;512}513
514free(request->url);515free(request);516}
517
518static void finish_request(struct transfer_request *request)519{
520struct http_pack_request *preq;521struct http_object_request *obj_req;522
523request->curl_result = request->slot->curl_result;524request->http_code = request->slot->http_code;525request->slot = NULL;526
527/* Keep locks active */528check_locks();529
530if (request->headers)531curl_slist_free_all(request->headers);532
533/* URL is reused for MOVE after PUT and used during FETCH */534if (request->state != RUN_PUT && request->state != RUN_FETCH_PACKED) {535FREE_AND_NULL(request->url);536}537
538if (request->state == RUN_MKCOL) {539if (request->curl_result == CURLE_OK ||540request->http_code == 405) {541remote_dir_exists[request->obj->oid.hash[0]] = 1;542start_put(request);543} else {544fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",545oid_to_hex(&request->obj->oid),546request->curl_result, request->http_code);547request->state = ABORTED;548aborted = 1;549}550} else if (request->state == RUN_PUT) {551if (request->curl_result == CURLE_OK) {552start_move(request);553} else {554fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n",555oid_to_hex(&request->obj->oid),556request->curl_result, request->http_code);557request->state = ABORTED;558aborted = 1;559}560} else if (request->state == RUN_MOVE) {561if (request->curl_result == CURLE_OK) {562if (push_verbosely)563fprintf(stderr, " sent %s\n",564oid_to_hex(&request->obj->oid));565request->obj->flags |= REMOTE;566release_request(request);567} else {568fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",569oid_to_hex(&request->obj->oid),570request->curl_result, request->http_code);571request->state = ABORTED;572aborted = 1;573}574} else if (request->state == RUN_FETCH_LOOSE) {575obj_req = (struct http_object_request *)request->userData;576
577if (finish_http_object_request(obj_req) == 0)578if (obj_req->rename == 0)579request->obj->flags |= (LOCAL | REMOTE);580
581/* Try fetching packed if necessary */582if (request->obj->flags & LOCAL) {583release_http_object_request(obj_req);584release_request(request);585} else586start_fetch_packed(request);587
588} else if (request->state == RUN_FETCH_PACKED) {589int fail = 1;590if (request->curl_result != CURLE_OK) {591fprintf(stderr, "Unable to get pack file %s\n%s",592request->url, curl_errorstr);593} else {594preq = (struct http_pack_request *)request->userData;595
596if (preq) {597if (finish_http_pack_request(preq) == 0)598fail = 0;599release_http_pack_request(preq);600}601}602if (fail)603repo->can_update_info_refs = 0;604else605http_install_packfile(request->target, &repo->packs);606release_request(request);607}608}
609
610static int is_running_queue;611static int fill_active_slot(void *data UNUSED)612{
613struct transfer_request *request;614
615if (aborted || !is_running_queue)616return 0;617
618for (request = request_queue_head; request; request = request->next) {619if (request->state == NEED_FETCH) {620start_fetch_loose(request);621return 1;622} else if (pushing && request->state == NEED_PUSH) {623if (remote_dir_exists[request->obj->oid.hash[0]] == 1) {624start_put(request);625} else {626start_mkcol(request);627}628return 1;629}630}631return 0;632}
633
634static void get_remote_object_list(unsigned char parent);635
636static void add_fetch_request(struct object *obj)637{
638struct transfer_request *request;639
640check_locks();641
642/*643* Don't fetch the object if it's known to exist locally
644* or is already in the request queue
645*/
646if (remote_dir_exists[obj->oid.hash[0]] == -1)647get_remote_object_list(obj->oid.hash[0]);648if (obj->flags & (LOCAL | FETCHING))649return;650
651obj->flags |= FETCHING;652request = xmalloc(sizeof(*request));653request->obj = obj;654request->url = NULL;655request->lock = NULL;656request->headers = NULL;657request->state = NEED_FETCH;658request->next = request_queue_head;659request_queue_head = request;660
661fill_active_slots();662step_active_slots();663}
664
665static int add_send_request(struct object *obj, struct remote_lock *lock)666{
667struct transfer_request *request;668struct packed_git *target;669
670/* Keep locks active */671check_locks();672
673/*674* Don't push the object if it's known to exist on the remote
675* or is already in the request queue
676*/
677if (remote_dir_exists[obj->oid.hash[0]] == -1)678get_remote_object_list(obj->oid.hash[0]);679if (obj->flags & (REMOTE | PUSHING))680return 0;681target = find_sha1_pack(obj->oid.hash, repo->packs);682if (target) {683obj->flags |= REMOTE;684return 0;685}686
687obj->flags |= PUSHING;688request = xmalloc(sizeof(*request));689request->obj = obj;690request->url = NULL;691request->lock = lock;692request->headers = NULL;693request->state = NEED_PUSH;694request->next = request_queue_head;695request_queue_head = request;696
697fill_active_slots();698step_active_slots();699
700return 1;701}
702
703static int fetch_indices(void)704{
705int ret;706
707if (push_verbosely)708fprintf(stderr, "Getting pack list\n");709
710switch (http_get_info_packs(repo->url, &repo->packs)) {711case HTTP_OK:712case HTTP_MISSING_TARGET:713ret = 0;714break;715default:716ret = -1;717}718
719return ret;720}
721
722static void one_remote_object(const struct object_id *oid)723{
724struct object *obj;725
726obj = lookup_object(the_repository, oid);727if (!obj)728obj = parse_object(the_repository, oid);729
730/* Ignore remote objects that don't exist locally */731if (!obj)732return;733
734obj->flags |= REMOTE;735if (!object_list_contains(objects, obj))736object_list_insert(obj, &objects);737}
738
739static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)740{
741int *lock_flags = (int *)ctx->userData;742
743if (tag_closed) {744if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) {745if ((*lock_flags & DAV_PROP_LOCKEX) &&746(*lock_flags & DAV_PROP_LOCKWR)) {747*lock_flags |= DAV_LOCK_OK;748}749*lock_flags &= DAV_LOCK_OK;750} else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) {751*lock_flags |= DAV_PROP_LOCKWR;752} else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) {753*lock_flags |= DAV_PROP_LOCKEX;754}755}756}
757
758static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)759{
760struct remote_lock *lock = (struct remote_lock *)ctx->userData;761git_hash_ctx hash_ctx;762unsigned char lock_token_hash[GIT_MAX_RAWSZ];763
764if (tag_closed && ctx->cdata) {765if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {766lock->owner = xstrdup(ctx->cdata);767} else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {768const char *arg;769if (skip_prefix(ctx->cdata, "Second-", &arg))770lock->timeout = strtol(arg, NULL, 10);771} else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {772lock->token = xstrdup(ctx->cdata);773
774the_hash_algo->init_fn(&hash_ctx);775the_hash_algo->update_fn(&hash_ctx, lock->token, strlen(lock->token));776the_hash_algo->final_fn(lock_token_hash, &hash_ctx);777
778lock->tmpfile_suffix[0] = '_';779memcpy(lock->tmpfile_suffix + 1, hash_to_hex(lock_token_hash), the_hash_algo->hexsz);780}781}782}
783
784static void one_remote_ref(const char *refname);785
786static void787xml_start_tag(void *userData, const char *name, const char **atts UNUSED)788{
789struct xml_ctx *ctx = (struct xml_ctx *)userData;790const char *c = strchr(name, ':');791int old_namelen, new_len;792
793if (!c)794c = name;795else796c++;797
798old_namelen = strlen(ctx->name);799new_len = old_namelen + strlen(c) + 2;800
801if (new_len > ctx->len) {802ctx->name = xrealloc(ctx->name, new_len);803ctx->len = new_len;804}805xsnprintf(ctx->name + old_namelen, ctx->len - old_namelen, ".%s", c);806
807FREE_AND_NULL(ctx->cdata);808
809ctx->userFunc(ctx, 0);810}
811
812static void813xml_end_tag(void *userData, const char *name)814{
815struct xml_ctx *ctx = (struct xml_ctx *)userData;816const char *c = strchr(name, ':');817char *ep;818
819ctx->userFunc(ctx, 1);820
821if (!c)822c = name;823else824c++;825
826ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;827*ep = 0;828}
829
830static void831xml_cdata(void *userData, const XML_Char *s, int len)832{
833struct xml_ctx *ctx = (struct xml_ctx *)userData;834free(ctx->cdata);835ctx->cdata = xmemdupz(s, len);836}
837
838static struct remote_lock *lock_remote(const char *path, long timeout)839{
840struct active_request_slot *slot;841struct slot_results results;842struct buffer out_buffer = { STRBUF_INIT, 0 };843struct strbuf in_buffer = STRBUF_INIT;844char *url;845char *ep;846char timeout_header[25];847struct remote_lock *lock = NULL;848struct curl_slist *dav_headers = http_copy_default_headers();849struct xml_ctx ctx;850char *escaped;851
852url = xstrfmt("%s%s", repo->url, path);853
854/* Make sure leading directories exist for the remote ref */855ep = strchr(url + strlen(repo->url) + 1, '/');856while (ep) {857char saved_character = ep[1];858ep[1] = '\0';859slot = get_active_slot();860slot->results = &results;861curl_setup_http_get(slot->curl, url, DAV_MKCOL);862if (start_active_slot(slot)) {863run_active_slot(slot);864if (results.curl_result != CURLE_OK &&865results.http_code != 405) {866fprintf(stderr,867"Unable to create branch path %s\n",868url);869free(url);870return NULL;871}872} else {873fprintf(stderr, "Unable to start MKCOL request\n");874free(url);875return NULL;876}877ep[1] = saved_character;878ep = strchr(ep + 1, '/');879}880
881escaped = xml_entities(ident_default_email());882strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);883free(escaped);884
885xsnprintf(timeout_header, sizeof(timeout_header), "Timeout: Second-%ld", timeout);886dav_headers = curl_slist_append(dav_headers, timeout_header);887dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");888
889slot = get_active_slot();890slot->results = &results;891curl_setup_http(slot->curl, url, DAV_LOCK, &out_buffer, fwrite_buffer);892curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);893curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, &in_buffer);894
895CALLOC_ARRAY(lock, 1);896lock->timeout = -1;897
898if (start_active_slot(slot)) {899run_active_slot(slot);900if (results.curl_result == CURLE_OK) {901XML_Parser parser = XML_ParserCreate(NULL);902enum XML_Status result;903ctx.name = xcalloc(10, 1);904ctx.len = 0;905ctx.cdata = NULL;906ctx.userFunc = handle_new_lock_ctx;907ctx.userData = lock;908XML_SetUserData(parser, &ctx);909XML_SetElementHandler(parser, xml_start_tag,910xml_end_tag);911XML_SetCharacterDataHandler(parser, xml_cdata);912result = XML_Parse(parser, in_buffer.buf,913in_buffer.len, 1);914free(ctx.name);915if (result != XML_STATUS_OK) {916fprintf(stderr, "XML error: %s\n",917XML_ErrorString(918XML_GetErrorCode(parser)));919lock->timeout = -1;920}921XML_ParserFree(parser);922} else {923fprintf(stderr,924"error: curl result=%d, HTTP code=%ld\n",925results.curl_result, results.http_code);926}927} else {928fprintf(stderr, "Unable to start LOCK request\n");929}930
931curl_slist_free_all(dav_headers);932strbuf_release(&out_buffer.buf);933strbuf_release(&in_buffer);934
935if (lock->token == NULL || lock->timeout <= 0) {936free(lock->token);937free(lock->owner);938free(url);939FREE_AND_NULL(lock);940} else {941lock->url = url;942lock->start_time = time(NULL);943lock->next = repo->locks;944repo->locks = lock;945}946
947return lock;948}
949
950static int unlock_remote(struct remote_lock *lock)951{
952struct active_request_slot *slot;953struct slot_results results;954struct remote_lock *prev = repo->locks;955struct curl_slist *dav_headers;956int rc = 0;957
958dav_headers = get_dav_token_headers(lock, DAV_HEADER_LOCK);959
960slot = get_active_slot();961slot->results = &results;962curl_setup_http_get(slot->curl, lock->url, DAV_UNLOCK);963curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);964
965if (start_active_slot(slot)) {966run_active_slot(slot);967if (results.curl_result == CURLE_OK)968rc = 1;969else970fprintf(stderr, "UNLOCK HTTP error %ld\n",971results.http_code);972} else {973fprintf(stderr, "Unable to start UNLOCK request\n");974}975
976curl_slist_free_all(dav_headers);977
978if (repo->locks == lock) {979repo->locks = lock->next;980} else {981while (prev && prev->next != lock)982prev = prev->next;983if (prev)984prev->next = lock->next;985}986
987free(lock->owner);988free(lock->url);989free(lock->token);990free(lock);991
992return rc;993}
994
995static void remove_locks(void)996{
997struct remote_lock *lock = repo->locks;998
999fprintf(stderr, "Removing remote locks...\n");1000while (lock) {1001struct remote_lock *next = lock->next;1002unlock_remote(lock);1003lock = next;1004}1005}
1006
1007static void remove_locks_on_signal(int signo)1008{
1009remove_locks();1010sigchain_pop(signo);1011raise(signo);1012}
1013
1014static void remote_ls(const char *path, int flags,1015void (*userFunc)(struct remote_ls_ctx *ls),1016void *userData);1017
1018/* extract hex from sharded "xx/x{38}" filename */
1019static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)1020{
1021memset(oid->hash, 0, GIT_MAX_RAWSZ);1022oid->algo = hash_algo_by_ptr(the_hash_algo);1023
1024if (strlen(path) != the_hash_algo->hexsz + 1)1025return -1;1026
1027if (hex_to_bytes(oid->hash, path, 1))1028return -1;1029path += 2;1030path++; /* skip '/' */1031
1032return hex_to_bytes(oid->hash + 1, path, the_hash_algo->rawsz - 1);1033}
1034
1035static void process_ls_object(struct remote_ls_ctx *ls)1036{
1037unsigned int *parent = (unsigned int *)ls->userData;1038const char *path = ls->dentry_name;1039struct object_id oid;1040
1041if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {1042remote_dir_exists[*parent] = 1;1043return;1044}1045
1046if (!skip_prefix(path, "objects/", &path) ||1047get_oid_hex_from_objpath(path, &oid))1048return;1049
1050one_remote_object(&oid);1051}
1052
1053static void process_ls_ref(struct remote_ls_ctx *ls)1054{
1055if (!strcmp(ls->path, ls->dentry_name) && (ls->dentry_flags & IS_DIR)) {1056fprintf(stderr, " %s\n", ls->dentry_name);1057return;1058}1059
1060if (!(ls->dentry_flags & IS_DIR))1061one_remote_ref(ls->dentry_name);1062}
1063
1064static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)1065{
1066struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;1067
1068if (tag_closed) {1069if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {1070if (ls->dentry_flags & IS_DIR) {1071
1072/* ensure collection names end with slash */1073str_end_url_with_slash(ls->dentry_name, &ls->dentry_name);1074
1075if (ls->flags & PROCESS_DIRS) {1076ls->userFunc(ls);1077}1078if (strcmp(ls->dentry_name, ls->path) &&1079ls->flags & RECURSIVE) {1080remote_ls(ls->dentry_name,1081ls->flags,1082ls->userFunc,1083ls->userData);1084}1085} else if (ls->flags & PROCESS_FILES) {1086ls->userFunc(ls);1087}1088} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {1089char *path = ctx->cdata;1090if (*ctx->cdata == 'h') {1091path = strstr(path, "//");1092if (path) {1093path = strchr(path+2, '/');1094}1095}1096if (path) {1097const char *url = repo->url;1098if (repo->path)1099url = repo->path;1100if (strncmp(path, url, repo->path_len))1101error("Parsed path '%s' does not match url: '%s'",1102path, url);1103else {1104path += repo->path_len;1105ls->dentry_name = xstrdup(path);1106}1107}1108} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {1109ls->dentry_flags |= IS_DIR;1110}1111} else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {1112FREE_AND_NULL(ls->dentry_name);1113ls->dentry_flags = 0;1114}1115}
1116
1117/*
1118* NEEDSWORK: remote_ls() ignores info/refs on the remote side. But it
1119* should _only_ heed the information from that file, instead of trying to
1120* determine the refs from the remote file system (badly: it does not even
1121* know about packed-refs).
1122*/
1123static void remote_ls(const char *path, int flags,1124void (*userFunc)(struct remote_ls_ctx *ls),1125void *userData)1126{
1127char *url = xstrfmt("%s%s", repo->url, path);1128struct active_request_slot *slot;1129struct slot_results results;1130struct strbuf in_buffer = STRBUF_INIT;1131struct buffer out_buffer = { STRBUF_INIT, 0 };1132struct curl_slist *dav_headers = http_copy_default_headers();1133struct xml_ctx ctx;1134struct remote_ls_ctx ls;1135
1136ls.flags = flags;1137ls.path = xstrdup(path);1138ls.dentry_name = NULL;1139ls.dentry_flags = 0;1140ls.userData = userData;1141ls.userFunc = userFunc;1142
1143strbuf_addstr(&out_buffer.buf, PROPFIND_ALL_REQUEST);1144
1145dav_headers = curl_slist_append(dav_headers, "Depth: 1");1146dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");1147
1148slot = get_active_slot();1149slot->results = &results;1150curl_setup_http(slot->curl, url, DAV_PROPFIND,1151&out_buffer, fwrite_buffer);1152curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);1153curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, &in_buffer);1154
1155if (start_active_slot(slot)) {1156run_active_slot(slot);1157if (results.curl_result == CURLE_OK) {1158XML_Parser parser = XML_ParserCreate(NULL);1159enum XML_Status result;1160ctx.name = xcalloc(10, 1);1161ctx.len = 0;1162ctx.cdata = NULL;1163ctx.userFunc = handle_remote_ls_ctx;1164ctx.userData = &ls;1165XML_SetUserData(parser, &ctx);1166XML_SetElementHandler(parser, xml_start_tag,1167xml_end_tag);1168XML_SetCharacterDataHandler(parser, xml_cdata);1169result = XML_Parse(parser, in_buffer.buf,1170in_buffer.len, 1);1171free(ctx.name);1172
1173if (result != XML_STATUS_OK) {1174fprintf(stderr, "XML error: %s\n",1175XML_ErrorString(1176XML_GetErrorCode(parser)));1177}1178XML_ParserFree(parser);1179}1180} else {1181fprintf(stderr, "Unable to start PROPFIND request\n");1182}1183
1184free(ls.path);1185free(url);1186strbuf_release(&out_buffer.buf);1187strbuf_release(&in_buffer);1188curl_slist_free_all(dav_headers);1189}
1190
1191static void get_remote_object_list(unsigned char parent)1192{
1193char path[] = "objects/XX/";1194static const char hex[] = "0123456789abcdef";1195unsigned int val = parent;1196
1197path[8] = hex[val >> 4];1198path[9] = hex[val & 0xf];1199remote_dir_exists[val] = 0;1200remote_ls(path, (PROCESS_FILES | PROCESS_DIRS),1201process_ls_object, &val);1202}
1203
1204static int locking_available(void)1205{
1206struct active_request_slot *slot;1207struct slot_results results;1208struct strbuf in_buffer = STRBUF_INIT;1209struct buffer out_buffer = { STRBUF_INIT, 0 };1210struct curl_slist *dav_headers = http_copy_default_headers();1211struct xml_ctx ctx;1212int lock_flags = 0;1213char *escaped;1214
1215escaped = xml_entities(repo->url);1216strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, escaped);1217free(escaped);1218
1219dav_headers = curl_slist_append(dav_headers, "Depth: 0");1220dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");1221
1222slot = get_active_slot();1223slot->results = &results;1224curl_setup_http(slot->curl, repo->url, DAV_PROPFIND,1225&out_buffer, fwrite_buffer);1226curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);1227curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, &in_buffer);1228
1229if (start_active_slot(slot)) {1230run_active_slot(slot);1231if (results.curl_result == CURLE_OK) {1232XML_Parser parser = XML_ParserCreate(NULL);1233enum XML_Status result;1234ctx.name = xcalloc(10, 1);1235ctx.len = 0;1236ctx.cdata = NULL;1237ctx.userFunc = handle_lockprop_ctx;1238ctx.userData = &lock_flags;1239XML_SetUserData(parser, &ctx);1240XML_SetElementHandler(parser, xml_start_tag,1241xml_end_tag);1242result = XML_Parse(parser, in_buffer.buf,1243in_buffer.len, 1);1244free(ctx.name);1245
1246if (result != XML_STATUS_OK) {1247fprintf(stderr, "XML error: %s\n",1248XML_ErrorString(1249XML_GetErrorCode(parser)));1250lock_flags = 0;1251}1252XML_ParserFree(parser);1253if (!lock_flags)1254error("no DAV locking support on %s",1255repo->url);1256
1257} else {1258error("Cannot access URL %s, return code %d",1259repo->url, results.curl_result);1260lock_flags = 0;1261}1262} else {1263error("Unable to start PROPFIND request on %s", repo->url);1264}1265
1266strbuf_release(&out_buffer.buf);1267strbuf_release(&in_buffer);1268curl_slist_free_all(dav_headers);1269
1270return lock_flags;1271}
1272
1273static struct object_list **add_one_object(struct object *obj, struct object_list **p)1274{
1275struct object_list *entry = xmalloc(sizeof(struct object_list));1276entry->item = obj;1277entry->next = *p;1278*p = entry;1279return &entry->next;1280}
1281
1282static struct object_list **process_blob(struct blob *blob,1283struct object_list **p)1284{
1285struct object *obj = &blob->object;1286
1287obj->flags |= LOCAL;1288
1289if (obj->flags & (UNINTERESTING | SEEN))1290return p;1291
1292obj->flags |= SEEN;1293return add_one_object(obj, p);1294}
1295
1296static struct object_list **process_tree(struct tree *tree,1297struct object_list **p)1298{
1299struct object *obj = &tree->object;1300struct tree_desc desc;1301struct name_entry entry;1302
1303obj->flags |= LOCAL;1304
1305if (obj->flags & (UNINTERESTING | SEEN))1306return p;1307if (parse_tree(tree) < 0)1308die("bad tree object %s", oid_to_hex(&obj->oid));1309
1310obj->flags |= SEEN;1311p = add_one_object(obj, p);1312
1313init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);1314
1315while (tree_entry(&desc, &entry))1316switch (object_type(entry.mode)) {1317case OBJ_TREE:1318p = process_tree(lookup_tree(the_repository, &entry.oid),1319p);1320break;1321case OBJ_BLOB:1322p = process_blob(lookup_blob(the_repository, &entry.oid),1323p);1324break;1325default:1326/* Subproject commit - not in this repository */1327break;1328}1329
1330free_tree_buffer(tree);1331return p;1332}
1333
1334static int get_delta(struct rev_info *revs, struct remote_lock *lock)1335{
1336int i;1337struct commit *commit;1338struct object_list **p = &objects;1339int count = 0;1340
1341while ((commit = get_revision(revs)) != NULL) {1342p = process_tree(repo_get_commit_tree(the_repository, commit),1343p);1344commit->object.flags |= LOCAL;1345if (!(commit->object.flags & UNINTERESTING))1346count += add_send_request(&commit->object, lock);1347}1348
1349for (i = 0; i < revs->pending.nr; i++) {1350struct object_array_entry *entry = revs->pending.objects + i;1351struct object *obj = entry->item;1352const char *name = entry->name;1353
1354if (obj->flags & (UNINTERESTING | SEEN))1355continue;1356if (obj->type == OBJ_TAG) {1357obj->flags |= SEEN;1358p = add_one_object(obj, p);1359continue;1360}1361if (obj->type == OBJ_TREE) {1362p = process_tree((struct tree *)obj, p);1363continue;1364}1365if (obj->type == OBJ_BLOB) {1366p = process_blob((struct blob *)obj, p);1367continue;1368}1369die("unknown pending object %s (%s)", oid_to_hex(&obj->oid), name);1370}1371
1372while (objects) {1373if (!(objects->item->flags & UNINTERESTING))1374count += add_send_request(objects->item, lock);1375objects = objects->next;1376}1377
1378return count;1379}
1380
1381static int update_remote(const struct object_id *oid, struct remote_lock *lock)1382{
1383struct active_request_slot *slot;1384struct slot_results results;1385struct buffer out_buffer = { STRBUF_INIT, 0 };1386struct curl_slist *dav_headers;1387
1388dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF);1389
1390strbuf_addf(&out_buffer.buf, "%s\n", oid_to_hex(oid));1391
1392slot = get_active_slot();1393slot->results = &results;1394curl_setup_http(slot->curl, lock->url, DAV_PUT,1395&out_buffer, fwrite_null);1396curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);1397
1398if (start_active_slot(slot)) {1399run_active_slot(slot);1400strbuf_release(&out_buffer.buf);1401if (results.curl_result != CURLE_OK) {1402fprintf(stderr,1403"PUT error: curl result=%d, HTTP code=%ld\n",1404results.curl_result, results.http_code);1405/* We should attempt recovery? */1406return 0;1407}1408} else {1409strbuf_release(&out_buffer.buf);1410fprintf(stderr, "Unable to start PUT request\n");1411return 0;1412}1413
1414return 1;1415}
1416
1417static struct ref *remote_refs;1418
1419static void one_remote_ref(const char *refname)1420{
1421struct ref *ref;1422struct object *obj;1423
1424ref = alloc_ref(refname);1425
1426if (http_fetch_ref(repo->url, ref) != 0) {1427fprintf(stderr,1428"Unable to fetch ref %s from %s\n",1429refname, repo->url);1430free(ref);1431return;1432}1433
1434/*1435* Fetch a copy of the object if it doesn't exist locally - it
1436* may be required for updating server info later.
1437*/
1438if (repo->can_update_info_refs && !repo_has_object_file(the_repository, &ref->old_oid)) {1439obj = lookup_unknown_object(the_repository, &ref->old_oid);1440fprintf(stderr, " fetch %s for %s\n",1441oid_to_hex(&ref->old_oid), refname);1442add_fetch_request(obj);1443}1444
1445ref->next = remote_refs;1446remote_refs = ref;1447}
1448
1449static void get_dav_remote_heads(void)1450{
1451remote_ls("refs/", (PROCESS_FILES | PROCESS_DIRS | RECURSIVE), process_ls_ref, NULL);1452}
1453
1454static void add_remote_info_ref(struct remote_ls_ctx *ls)1455{
1456struct strbuf *buf = (struct strbuf *)ls->userData;1457struct object *o;1458struct ref *ref;1459
1460ref = alloc_ref(ls->dentry_name);1461
1462if (http_fetch_ref(repo->url, ref) != 0) {1463fprintf(stderr,1464"Unable to fetch ref %s from %s\n",1465ls->dentry_name, repo->url);1466aborted = 1;1467free(ref);1468return;1469}1470
1471o = parse_object(the_repository, &ref->old_oid);1472if (!o) {1473fprintf(stderr,1474"Unable to parse object %s for remote ref %s\n",1475oid_to_hex(&ref->old_oid), ls->dentry_name);1476aborted = 1;1477free(ref);1478return;1479}1480
1481strbuf_addf(buf, "%s\t%s\n",1482oid_to_hex(&ref->old_oid), ls->dentry_name);1483
1484if (o->type == OBJ_TAG) {1485o = deref_tag(the_repository, o, ls->dentry_name, 0);1486if (o)1487strbuf_addf(buf, "%s\t%s^{}\n",1488oid_to_hex(&o->oid), ls->dentry_name);1489}1490free(ref);1491}
1492
1493static void update_remote_info_refs(struct remote_lock *lock)1494{
1495struct buffer buffer = { STRBUF_INIT, 0 };1496struct active_request_slot *slot;1497struct slot_results results;1498struct curl_slist *dav_headers;1499
1500remote_ls("refs/", (PROCESS_FILES | RECURSIVE),1501add_remote_info_ref, &buffer.buf);1502if (!aborted) {1503dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF);1504
1505slot = get_active_slot();1506slot->results = &results;1507curl_setup_http(slot->curl, lock->url, DAV_PUT,1508&buffer, fwrite_null);1509curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);1510
1511if (start_active_slot(slot)) {1512run_active_slot(slot);1513if (results.curl_result != CURLE_OK) {1514fprintf(stderr,1515"PUT error: curl result=%d, HTTP code=%ld\n",1516results.curl_result, results.http_code);1517}1518}1519}1520strbuf_release(&buffer.buf);1521}
1522
1523static int remote_exists(const char *path)1524{
1525char *url = xstrfmt("%s%s", repo->url, path);1526int ret;1527
1528
1529switch (http_get_strbuf(url, NULL, NULL)) {1530case HTTP_OK:1531ret = 1;1532break;1533case HTTP_MISSING_TARGET:1534ret = 0;1535break;1536case HTTP_ERROR:1537error("unable to access '%s': %s", url, curl_errorstr);1538/* fallthrough */1539default:1540ret = -1;1541}1542free(url);1543return ret;1544}
1545
1546static void fetch_symref(const char *path, char **symref, struct object_id *oid)1547{
1548char *url = xstrfmt("%s%s", repo->url, path);1549struct strbuf buffer = STRBUF_INIT;1550const char *name;1551
1552if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)1553die("Couldn't get %s for remote symref\n%s", url,1554curl_errorstr);1555free(url);1556
1557FREE_AND_NULL(*symref);1558oidclr(oid, the_repository->hash_algo);1559
1560if (buffer.len == 0)1561return;1562
1563/* Cut off trailing newline. */1564strbuf_rtrim(&buffer);1565
1566/* If it's a symref, set the refname; otherwise try for a sha1 */1567if (skip_prefix(buffer.buf, "ref: ", &name)) {1568*symref = xmemdupz(name, buffer.len - (name - buffer.buf));1569} else {1570get_oid_hex(buffer.buf, oid);1571}1572
1573strbuf_release(&buffer);1574}
1575
1576static int verify_merge_base(struct object_id *head_oid, struct ref *remote)1577{
1578struct commit *head = lookup_commit_or_die(head_oid, "HEAD");1579struct commit *branch = lookup_commit_or_die(&remote->old_oid,1580remote->name);1581int ret = repo_in_merge_bases(the_repository, branch, head);1582
1583if (ret < 0)1584exit(128);1585return ret;1586}
1587
1588static int delete_remote_branch(const char *pattern, int force)1589{
1590struct ref *refs = remote_refs;1591struct ref *remote_ref = NULL;1592struct object_id head_oid;1593char *symref = NULL;1594int match;1595int patlen = strlen(pattern);1596int i;1597struct active_request_slot *slot;1598struct slot_results results;1599char *url;1600
1601/* Find the remote branch(es) matching the specified branch name */1602for (match = 0; refs; refs = refs->next) {1603char *name = refs->name;1604int namelen = strlen(name);1605if (namelen < patlen ||1606memcmp(name + namelen - patlen, pattern, patlen))1607continue;1608if (namelen != patlen && name[namelen - patlen - 1] != '/')1609continue;1610match++;1611remote_ref = refs;1612}1613if (match == 0)1614return error("No remote branch matches %s", pattern);1615if (match != 1)1616return error("More than one remote branch matches %s",1617pattern);1618
1619/*1620* Remote HEAD must be a symref (not exactly foolproof; a remote
1621* symlink to a symref will look like a symref)
1622*/
1623fetch_symref("HEAD", &symref, &head_oid);1624if (!symref)1625return error("Remote HEAD is not a symref");1626
1627/* Remote branch must not be the remote HEAD */1628for (i = 0; symref && i < MAXDEPTH; i++) {1629if (!strcmp(remote_ref->name, symref))1630return error("Remote branch %s is the current HEAD",1631remote_ref->name);1632fetch_symref(symref, &symref, &head_oid);1633}1634
1635/* Run extra sanity checks if delete is not forced */1636if (!force) {1637/* Remote HEAD must resolve to a known object */1638if (symref)1639return error("Remote HEAD symrefs too deep");1640if (is_null_oid(&head_oid))1641return error("Unable to resolve remote HEAD");1642if (!repo_has_object_file(the_repository, &head_oid))1643return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid));1644
1645/* Remote branch must resolve to a known object */1646if (is_null_oid(&remote_ref->old_oid))1647return error("Unable to resolve remote branch %s",1648remote_ref->name);1649if (!repo_has_object_file(the_repository, &remote_ref->old_oid))1650return 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));1651
1652/* Remote branch must be an ancestor of remote HEAD */1653if (!verify_merge_base(&head_oid, remote_ref)) {1654return error("The branch '%s' is not an ancestor "1655"of your current HEAD.\n"1656"If you are sure you want to delete it,"1657" run:\n\t'git http-push -D %s %s'",1658remote_ref->name, repo->url, pattern);1659}1660}1661
1662/* Send delete request */1663fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);1664if (dry_run)1665return 0;1666url = xstrfmt("%s%s", repo->url, remote_ref->name);1667slot = get_active_slot();1668slot->results = &results;1669curl_setup_http_get(slot->curl, url, DAV_DELETE);1670if (start_active_slot(slot)) {1671run_active_slot(slot);1672free(url);1673if (results.curl_result != CURLE_OK)1674return error("DELETE request failed (%d/%ld)",1675results.curl_result, results.http_code);1676} else {1677free(url);1678return error("Unable to start DELETE request");1679}1680
1681return 0;1682}
1683
1684static void run_request_queue(void)1685{
1686is_running_queue = 1;1687fill_active_slots();1688add_fill_function(NULL, fill_active_slot);1689do {1690finish_all_active_slots();1691fill_active_slots();1692} while (request_queue_head && !aborted);1693
1694is_running_queue = 0;1695}
1696
1697int cmd_main(int argc, const char **argv)1698{
1699struct transfer_request *request;1700struct transfer_request *next_request;1701struct refspec rs = REFSPEC_INIT_PUSH;1702struct remote_lock *ref_lock = NULL;1703struct remote_lock *info_ref_lock = NULL;1704int delete_branch = 0;1705int force_delete = 0;1706int objects_to_send;1707int rc = 0;1708int i;1709int new_refs;1710struct ref *ref, *local_refs;1711
1712CALLOC_ARRAY(repo, 1);1713
1714argv++;1715for (i = 1; i < argc; i++, argv++) {1716const char *arg = *argv;1717
1718if (*arg == '-') {1719if (!strcmp(arg, "--all")) {1720push_all = MATCH_REFS_ALL;1721continue;1722}1723if (!strcmp(arg, "--force")) {1724force_all = 1;1725continue;1726}1727if (!strcmp(arg, "--dry-run")) {1728dry_run = 1;1729continue;1730}1731if (!strcmp(arg, "--helper-status")) {1732helper_status = 1;1733continue;1734}1735if (!strcmp(arg, "--verbose")) {1736push_verbosely = 1;1737http_is_verbose = 1;1738continue;1739}1740if (!strcmp(arg, "-d")) {1741delete_branch = 1;1742continue;1743}1744if (!strcmp(arg, "-D")) {1745delete_branch = 1;1746force_delete = 1;1747continue;1748}1749if (!strcmp(arg, "-h"))1750usage(http_push_usage);1751}1752if (!repo->url) {1753char *path = strstr(arg, "//");1754str_end_url_with_slash(arg, &repo->url);1755repo->path_len = strlen(repo->url);1756if (path) {1757repo->path = strchr(path+2, '/');1758if (repo->path)1759repo->path_len = strlen(repo->path);1760}1761continue;1762}1763refspec_appendn(&rs, argv, argc - i);1764break;1765}1766
1767if (!repo->url)1768usage(http_push_usage);1769
1770if (delete_branch && rs.nr != 1)1771die("You must specify only one branch name when deleting a remote branch");1772
1773setup_git_directory();1774
1775memset(remote_dir_exists, -1, 256);1776
1777http_init(NULL, repo->url, 1);1778
1779is_running_queue = 0;1780
1781/* Verify DAV compliance/lock support */1782if (!locking_available()) {1783rc = 1;1784goto cleanup;1785}1786
1787sigchain_push_common(remove_locks_on_signal);1788
1789/* Check whether the remote has server info files */1790repo->can_update_info_refs = 0;1791repo->has_info_refs = remote_exists("info/refs");1792repo->has_info_packs = remote_exists("objects/info/packs");1793if (repo->has_info_refs) {1794info_ref_lock = lock_remote("info/refs", LOCK_TIME);1795if (info_ref_lock)1796repo->can_update_info_refs = 1;1797else {1798error("cannot lock existing info/refs");1799rc = 1;1800goto cleanup;1801}1802}1803if (repo->has_info_packs)1804fetch_indices();1805
1806/* Get a list of all local and remote heads to validate refspecs */1807local_refs = get_local_heads();1808fprintf(stderr, "Fetching remote heads...\n");1809get_dav_remote_heads();1810run_request_queue();1811
1812/* Remove a remote branch if -d or -D was specified */1813if (delete_branch) {1814const char *branch = rs.items[i].src;1815if (delete_remote_branch(branch, force_delete) == -1) {1816fprintf(stderr, "Unable to delete remote branch %s\n",1817branch);1818if (helper_status)1819printf("error %s cannot remove\n", branch);1820}1821goto cleanup;1822}1823
1824/* match them up */1825if (match_push_refs(local_refs, &remote_refs, &rs, push_all)) {1826rc = -1;1827goto cleanup;1828}1829if (!remote_refs) {1830fprintf(stderr, "No refs in common and none specified; doing nothing.\n");1831if (helper_status)1832printf("error null no match\n");1833rc = 0;1834goto cleanup;1835}1836
1837new_refs = 0;1838for (ref = remote_refs; ref; ref = ref->next) {1839struct rev_info revs;1840struct strvec commit_argv = STRVEC_INIT;1841
1842if (!ref->peer_ref)1843continue;1844
1845if (is_null_oid(&ref->peer_ref->new_oid)) {1846if (delete_remote_branch(ref->name, 1) == -1) {1847error("Could not remove %s", ref->name);1848if (helper_status)1849printf("error %s cannot remove\n", ref->name);1850rc = -4;1851}1852else if (helper_status)1853printf("ok %s\n", ref->name);1854new_refs++;1855continue;1856}1857
1858if (oideq(&ref->old_oid, &ref->peer_ref->new_oid)) {1859if (push_verbosely)1860/* stable plumbing output; do not modify or localize */1861fprintf(stderr, "'%s': up-to-date\n", ref->name);1862if (helper_status)1863printf("ok %s up to date\n", ref->name);1864continue;1865}1866
1867if (!force_all &&1868!is_null_oid(&ref->old_oid) &&1869!ref->force) {1870if (!repo_has_object_file(the_repository, &ref->old_oid) ||1871!ref_newer(&ref->peer_ref->new_oid,1872&ref->old_oid)) {1873/*1874* We do not have the remote ref, or
1875* we know that the remote ref is not
1876* an ancestor of what we are trying to
1877* push. Either way this can be losing
1878* commits at the remote end and likely
1879* we were not up to date to begin with.
1880*/
1881/* stable plumbing output; do not modify or localize */1882error("remote '%s' is not an ancestor of\n"1883"local '%s'.\n"1884"Maybe you are not up-to-date and "1885"need to pull first?",1886ref->name,1887ref->peer_ref->name);1888if (helper_status)1889printf("error %s non-fast forward\n", ref->name);1890rc = -2;1891continue;1892}1893}1894oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);1895new_refs++;1896
1897fprintf(stderr, "updating '%s'", ref->name);1898if (strcmp(ref->name, ref->peer_ref->name))1899fprintf(stderr, " using '%s'", ref->peer_ref->name);1900fprintf(stderr, "\n from %s\n to %s\n",1901oid_to_hex(&ref->old_oid), oid_to_hex(&ref->new_oid));1902if (dry_run) {1903if (helper_status)1904printf("ok %s\n", ref->name);1905continue;1906}1907
1908/* Lock remote branch ref */1909ref_lock = lock_remote(ref->name, LOCK_TIME);1910if (!ref_lock) {1911fprintf(stderr, "Unable to lock remote branch %s\n",1912ref->name);1913if (helper_status)1914printf("error %s lock error\n", ref->name);1915rc = 1;1916continue;1917}1918
1919/* Set up revision info for this refspec */1920strvec_push(&commit_argv, ""); /* ignored */1921strvec_push(&commit_argv, "--objects");1922strvec_push(&commit_argv, oid_to_hex(&ref->new_oid));1923if (!push_all && !is_null_oid(&ref->old_oid))1924strvec_pushf(&commit_argv, "^%s",1925oid_to_hex(&ref->old_oid));1926repo_init_revisions(the_repository, &revs, setup_git_directory());1927setup_revisions(commit_argv.nr, commit_argv.v, &revs, NULL);1928revs.edge_hint = 0; /* just in case */1929
1930/* Generate a list of objects that need to be pushed */1931pushing = 0;1932if (prepare_revision_walk(&revs))1933die("revision walk setup failed");1934mark_edges_uninteresting(&revs, NULL, 0);1935objects_to_send = get_delta(&revs, ref_lock);1936finish_all_active_slots();1937
1938/* Push missing objects to remote, this would be a1939convenient time to pack them first if appropriate. */
1940pushing = 1;1941if (objects_to_send)1942fprintf(stderr, " sending %d objects\n",1943objects_to_send);1944
1945run_request_queue();1946
1947/* Update the remote branch if all went well */1948if (aborted || !update_remote(&ref->new_oid, ref_lock))1949rc = 1;1950
1951if (!rc)1952fprintf(stderr, " done\n");1953if (helper_status)1954printf("%s %s\n", !rc ? "ok" : "error", ref->name);1955unlock_remote(ref_lock);1956check_locks();1957strvec_clear(&commit_argv);1958release_revisions(&revs);1959}1960
1961/* Update remote server info if appropriate */1962if (repo->has_info_refs && new_refs) {1963if (info_ref_lock && repo->can_update_info_refs) {1964fprintf(stderr, "Updating remote server info\n");1965if (!dry_run)1966update_remote_info_refs(info_ref_lock);1967} else {1968fprintf(stderr, "Unable to update server info\n");1969}1970}1971
1972cleanup:1973if (info_ref_lock)1974unlock_remote(info_ref_lock);1975free(repo);1976
1977http_cleanup();1978
1979request = request_queue_head;1980while (request != NULL) {1981next_request = request->next;1982release_request(request);1983request = next_request;1984}1985
1986return rc;1987}
1988