{"thread":{"id":"2317","subject":"[PATCH 1/2] Add support for pushing to a remote repository using HTTP/DAV","startedAt":"2005-11-02T19:19:24Z","lastAt":"2005-11-02T19:43:04Z","messageCount":2,"participants":["Nick Hengeveld","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"11021","messageId":"20051102191924.GC3928@reactrix.com","threadId":"2317","inReplyTo":null,"subject":"[PATCH 1/2] Add support for pushing to a remote repository using HTTP/DAV","fromName":"Nick Hengeveld","fromEmail":"nickh@reactrix.com","sentAt":"2005-11-02T19:19:24Z","receivedAt":"2005-11-02T19:19:24Z","isPatch":true,"sender":{"key":"nickh@reactrix.com","avatar":null},"body":"Add support for pushing to a remote repository using HTTP/DAV\n\nSigned-off-by: Nick Hengeveld <nickh@reactrix.com>\n\n\n---\n\nThere is still some cleanup to be done here, but it is usable.\n\nTo enable support in Apache, load the dav and dav_fs modules, set\nDAVLockDB and add \"Dav on\" to the GIT directory.  The Apache user will\nneed write permissions in the GIT repository - at least to the refs and\nobjects directory - so it is probably a good idea to turn on mod_ssl and\nthe appropriate mod_auth_* module as well.\n\nBy default, the push will trust that if a remote has an object, it also\nhas that object's full history.  This behavior can be changed using the\n--complete arg, but it may be appropriate to either never trust the\nremote or to change the default behavior.\n\nThe push currently sends everything as loose objects, even if they are\nstored in a pack in the local repository.  It may make sense to pack all\nthe necessary objects prior to sending, although there doesn't seem to\nbe a way to resume a PUT so a failed transfer could prove much more\nannoying.\n\nThere is an advantage to keeping objects packed on the remote end, as the\nremote's pack indexes will be fetched and stored locally and objects stored\nin a remote pack will not require a separate HEAD request to check whether\nthey exist in the remote repository.\n\n\n Documentation/git-http-push.txt |   89 ++\n Makefile                        |   13 \n http-push.c                     | 1625 +++++++++++++++++++++++++++++++++++++++\n 3 files changed, 1725 insertions(+), 2 deletions(-)\n create mode 100644 Documentation/git-http-push.txt\n create mode 100644 http-push.c\n\napplies-to: b774cd64105bda561bba3c528a7fea19b87755a1\ncb4a5d4a797002739ece1ed67204520749324e2d\ndiff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt\nnew file mode 100644\nindex 0000000..c7066d6\n--- /dev/null\n+++ b/Documentation/git-http-push.txt\n@@ -0,0 +1,89 @@\n+git-http-push(1)\n+================\n+\n+NAME\n+----\n+git-http-push - Push missing objects using HTTP/DAV.\n+\n+\n+SYNOPSIS\n+--------\n+'git-http-push' [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n+\n+DESCRIPTION\n+-----------\n+Sends missing objects to remote repository, and updates the\n+remote branch.\n+\n+\n+OPTIONS\n+-------\n+--complete::\n+\tDo not assume that the remote repository is complete in its\n+\tcurrent state, and verify all objects in the entire local\n+\tref's history exist in the remote repository.\n+\n+--force::\n+\tUsually, the command refuses to update a remote ref that\n+\tis not an ancestor of the local ref used to overwrite it.\n+\tThis flag disables the check.  What this means is that\n+\tthe remote repository can lose commits; use it with\n+\tcare.\n+\n+--verbose::\n+\tReport the list of objects being walked locally and the\n+\tlist of objects successfully sent to the remote repository.\n+\n+<ref>...:\n+\tThe remote refs to update.\n+\n+\n+Specifying the Refs\n+-------------------\n+\n+A '<ref>' specification can be either a single pattern, or a pair\n+of such patterns separated by a colon \":\" (this means that a ref name\n+cannot have a colon in it).  A single pattern '<name>' is just a \n+shorthand for '<name>:<name>'.\n+\n+Each pattern pair consists of the source side (before the colon)\n+and the destination side (after the colon).  The ref to be\n+pushed is determined by finding a match that matches the source\n+side, and where it is pushed is determined by using the\n+destination side.\n+\n+ - It is an error if <src> does not match exactly one of the\n+   local refs.\n+\n+ - If <dst> does not match any remote ref, either\n+\n+   * it has to start with \"refs/\"; <dst> is used as the\n+     destination literally in this case.\n+\n+   * <src> == <dst> and the ref that matched the <src> must not\n+     exist in the set of remote refs; the ref matched <src>\n+     locally is used as the name of the destination.\n+\n+Without '--force', the <src> ref is stored at the remote only if\n+<dst> does not exist, or <dst> is a proper subset (i.e. an\n+ancestor) of <src>.  This check, known as \"fast forward check\",\n+is performed in order to avoid accidentally overwriting the\n+remote ref and lose other peoples' commits from there.\n+\n+With '--force', the fast forward check is disabled for all refs.\n+\n+Optionally, a <ref> parameter can be prefixed with a plus '+' sign\n+to disable the fast-forward check only on that ref.\n+\n+\n+Author\n+------\n+Written by Nick Hengeveld <nickh@reactrix.com>\n+\n+Documentation\n+--------------\n+Documentation by Nick Hengeveld\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 357cb3e..d441b9e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -6,12 +6,16 @@\n # Define NO_OPENSSL environment variable if you do not have OpenSSL. You will\n # miss out git-rev-list --merge-order. This also implies MOZILLA_SHA1.\n #\n-# Define NO_CURL if you do not have curl installed.  git-http-pull is not\n-# built, and you cannot use http:// and https:// transports.\n+# Define NO_CURL if you do not have curl installed.  git-http-pull and\n+# git-http-push are not built, and you cannot use http:// and https://\n+# transports.\n #\n # Define CURLDIR=/foo/bar if your curl header and library files are in\n # /foo/bar/include and /foo/bar/lib directories.\n #\n+# Define NO_EXPAT if you do not have expat installed.  git-http-push is\n+# not built, and you cannot push using http:// and https:// transports.\n+#\n # Define NO_STRCASESTR if you don't have strcasestr.\n #\n # Define PPC_SHA1 environment variable when running make to make use of\n@@ -223,6 +227,10 @@ ifndef NO_CURL\n \t\tCURL_LIBCURL = -lcurl\n \tendif\n \tPROGRAMS += git-http-fetch$X\n+\tifndef NO_EXPAT\n+\t\tEXPAT_LIBEXPAT = -lexpat\n+\t\tPROGRAMS += git-http-push$X\n+\tendif\n endif\n \n ifndef SHELL_PATH\n@@ -375,6 +383,7 @@ git-ssh-pull$X: rsh.o fetch.o\n git-ssh-push$X: rsh.o\n \n git-http-fetch$X: LIBS += $(CURL_LIBCURL)\n+git-http-push$X: LIBS += $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n git-rev-list$X: LIBS += $(OPENSSL_LIBSSL)\n \n init-db.o: init-db.c\ndiff --git a/http-push.c b/http-push.c\nnew file mode 100644\nindex 0000000..e85f1c1\n--- /dev/null\n+++ b/http-push.c\n@@ -0,0 +1,1625 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"pack.h\"\n+#include \"fetch.h\"\n+#include \"tag.h\"\n+#include \"blob.h\"\n+\n+#include <curl/curl.h>\n+#include <curl/easy.h>\n+#include \"expat.h\"\n+\n+static const char http_push_usage[] =\n+\"git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\\n\";\n+\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+#define USE_CURL_MULTI\n+#define DEFAULT_MAX_REQUESTS 5\n+#endif\n+\n+#if LIBCURL_VERSION_NUM < 0x070704\n+#define curl_global_cleanup() do { /* nothing */ } while(0)\n+#endif\n+#if LIBCURL_VERSION_NUM < 0x070800\n+#define curl_global_init(a) do { /* nothing */ } while(0)\n+#endif\n+\n+#if LIBCURL_VERSION_NUM < 0x070c04\n+#define NO_CURL_EASY_DUPHANDLE\n+#endif\n+\n+#define RANGE_HEADER_SIZE 30\n+\n+/* DAV method names and request body templates */\n+#define DAV_LOCK \"LOCK\"\n+#define DAV_MKCOL \"MKCOL\"\n+#define DAV_MOVE \"MOVE\"\n+#define DAV_PROPFIND \"PROPFIND\"\n+#define DAV_PUT \"PUT\"\n+#define DAV_UNLOCK \"UNLOCK\"\n+#define PROPFIND_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>\"\n+#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>\"\n+\n+static int active_requests = 0;\n+static int data_received;\n+static int pushing = 0;\n+static int aborted = 0;\n+\n+#ifdef USE_CURL_MULTI\n+static int max_requests = -1;\n+static CURLM *curlm;\n+#endif\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+static CURL *curl_default;\n+#endif\n+static struct curl_slist *no_pragma_header;\n+static struct curl_slist *default_headers;\n+static char curl_errorstr[CURL_ERROR_SIZE];\n+static char *lock_token = NULL;\n+\n+static int push_verbosely = 0;\n+static int push_all = 0;\n+static int force_all = 0;\n+\n+struct buffer\n+{\n+        size_t posn;\n+        size_t size;\n+        void *buffer;\n+};\n+\n+struct repo\n+{\n+\tchar *url;\n+\tstruct packed_git *packs;\n+};\n+\n+static struct repo *remote = NULL;\n+\n+enum transfer_state {\n+\tNEED_CHECK,\n+\tRUN_HEAD,\n+\tNEED_PUSH,\n+\tRUN_MKCOL,\n+\tRUN_PUT,\n+\tRUN_MOVE,\n+\tABORTED,\n+\tCOMPLETE,\n+};\n+\n+struct transfer_request\n+{\n+\tunsigned char sha1[20];\n+\tchar *url;\n+\tchar *dest;\n+\tchar *lock_token;\n+\tstruct curl_slist *headers;\n+\tstruct buffer buffer;\n+\tchar filename[PATH_MAX];\n+\tchar tmpfile[PATH_MAX];\n+\tenum transfer_state state;\n+\tCURLcode curl_result;\n+\tchar errorstr[CURL_ERROR_SIZE];\n+\tlong http_code;\n+\tunsigned char real_sha1[20];\n+\tSHA_CTX c;\n+\tz_stream stream;\n+\tint zret;\n+\tint rename;\n+\tstruct active_request_slot *slot;\n+\tstruct transfer_request *next;\n+};\n+\n+struct active_request_slot\n+{\n+\tCURL *curl;\n+\tFILE *local;\n+\tint in_use;\n+\tint done;\n+\tCURLcode curl_result;\n+\tlong http_code;\n+\tstruct active_request_slot *next;\n+};\n+\n+static struct transfer_request *request_queue_head = NULL;\n+static struct active_request_slot *active_queue_head = NULL;\n+\n+static int curl_ssl_verify = -1;\n+static char *ssl_cert = NULL;\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+static char *ssl_key = NULL;\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+static char *ssl_capath = NULL;\n+#endif\n+static char *ssl_cainfo = NULL;\n+static long curl_low_speed_limit = -1;\n+static long curl_low_speed_time = -1;\n+\n+struct lockprop\n+{\n+\tint supported_lock;\n+\tint lock_entry;\n+\tint lock_scope;\n+\tint lock_type;\n+\tint lock_exclusive;\n+\tint lock_exclusive_write;\n+};\n+\n+static int http_options(const char *var, const char *value)\n+{\n+\tif (!strcmp(\"http.sslverify\", var)) {\n+\t\tif (curl_ssl_verify == -1) {\n+\t\t\tcurl_ssl_verify = git_config_bool(var, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(\"http.sslcert\", var)) {\n+\t\tif (ssl_cert == NULL) {\n+\t\t\tssl_cert = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_cert, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+\tif (!strcmp(\"http.sslkey\", var)) {\n+\t\tif (ssl_key == NULL) {\n+\t\t\tssl_key = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_key, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tif (!strcmp(\"http.sslcapath\", var)) {\n+\t\tif (ssl_capath == NULL) {\n+\t\t\tssl_capath = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_capath, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+#endif\n+\tif (!strcmp(\"http.sslcainfo\", var)) {\n+\t\tif (ssl_cainfo == NULL) {\n+\t\t\tssl_cainfo = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_cainfo, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+#ifdef USE_CURL_MULTI\t\n+\tif (!strcmp(\"http.maxrequests\", var)) {\n+\t\tif (max_requests == -1)\n+\t\t\tmax_requests = git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+#endif\n+\n+\tif (!strcmp(\"http.lowspeedlimit\", var)) {\n+\t\tif (curl_low_speed_limit == -1)\n+\t\t\tcurl_low_speed_limit = (long)git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(\"http.lowspeedtime\", var)) {\n+\t\tif (curl_low_speed_time == -1)\n+\t\t\tcurl_low_speed_time = (long)git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Fall back on the default ones */\n+\treturn git_default_config(var, value);\n+}\n+\n+static size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,\n+\t\t\t   struct buffer *buffer)\n+{\n+\tsize_t size = eltsize * nmemb;\n+\tif (size > buffer->size - buffer->posn)\n+\t\tsize = buffer->size - buffer->posn;\n+\tmemcpy(ptr, buffer->buffer + buffer->posn, size);\n+\tbuffer->posn += size;\n+\treturn size;\n+}\n+\n+static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize,\n+\t\t\t\t    size_t nmemb, struct buffer *buffer)\n+{\n+\tsize_t size = eltsize * nmemb;\n+\tif (size > buffer->size - buffer->posn) {\n+\t\tbuffer->size = buffer->size * 3 / 2;\n+\t\tif (buffer->size < buffer->posn + size)\n+\t\t\tbuffer->size = buffer->posn + size;\n+\t\tbuffer->buffer = xrealloc(buffer->buffer, buffer->size);\n+\t}\n+\tmemcpy(buffer->buffer + buffer->posn, ptr, size);\n+\tbuffer->posn += size;\n+\tdata_received++;\n+\treturn size;\n+}\n+\n+static size_t fwrite_null(const void *ptr, size_t eltsize,\n+\t\t\t  size_t nmemb, struct buffer *buffer)\n+{\n+\tdata_received++;\n+\treturn eltsize * nmemb;\n+}\n+\n+#ifdef USE_CURL_MULTI\n+static void process_curl_messages(void);\n+static void process_request_queue(void);\n+#endif\n+\n+static CURL* get_curl_handle(void)\n+{\n+\tCURL* result = curl_easy_init();\n+\n+\tcurl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);\n+#if LIBCURL_VERSION_NUM >= 0x070907\n+\tcurl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);\n+#endif\n+\n+\tif (ssl_cert != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+\tif (ssl_key != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tif (ssl_capath != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);\n+#endif\n+\tif (ssl_cainfo != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);\n+\tcurl_easy_setopt(result, CURLOPT_FAILONERROR, 1);\n+\n+\tif (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {\n+\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,\n+\t\t\t\t curl_low_speed_limit);\n+\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,\n+\t\t\t\t curl_low_speed_time);\n+\t}\n+\n+\treturn result;\n+}\n+\n+static struct active_request_slot *get_active_slot(void)\n+{\n+\tstruct active_request_slot *slot = active_queue_head;\n+\tstruct active_request_slot *newslot;\n+\n+#ifdef USE_CURL_MULTI\n+\tint num_transfers;\n+\n+\t/* Wait for a slot to open up if the queue is full */\n+\twhile (active_requests >= max_requests) {\n+\t\tcurl_multi_perform(curlm, &num_transfers);\n+\t\tif (num_transfers < active_requests) {\n+\t\t\tprocess_curl_messages();\n+\t\t}\n+\t}\n+#endif\n+\n+\twhile (slot != NULL && slot->in_use) {\n+\t\tslot = slot->next;\n+\t}\n+\tif (slot == NULL) {\n+\t\tnewslot = xmalloc(sizeof(*newslot));\n+\t\tnewslot->curl = NULL;\n+\t\tnewslot->in_use = 0;\n+\t\tnewslot->next = NULL;\n+\n+\t\tslot = active_queue_head;\n+\t\tif (slot == NULL) {\n+\t\t\tactive_queue_head = newslot;\n+\t\t} else {\n+\t\t\twhile (slot->next != NULL) {\n+\t\t\t\tslot = slot->next;\n+\t\t\t}\n+\t\t\tslot->next = newslot;\n+\t\t}\n+\t\tslot = newslot;\n+\t}\n+\n+\tif (slot->curl == NULL) {\n+#ifdef NO_CURL_EASY_DUPHANDLE\n+\t\tslot->curl = get_curl_handle();\n+#else\n+\t\tslot->curl = curl_easy_duphandle(curl_default);\n+#endif\n+\t}\n+\n+\tactive_requests++;\n+\tslot->in_use = 1;\n+\tslot->done = 0;\n+\tslot->local = NULL;\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, default_headers);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);\n+\n+\treturn slot;\n+}\n+\n+static int start_active_slot(struct active_request_slot *slot)\n+{\n+#ifdef USE_CURL_MULTI\n+\tCURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);\n+\n+\tif (curlm_result != CURLM_OK &&\n+\t    curlm_result != CURLM_CALL_MULTI_PERFORM) {\n+\t\tactive_requests--;\n+\t\tslot->in_use = 0;\n+\t\treturn 0;\n+\t}\n+#endif\n+\treturn 1;\n+}\n+\n+static void run_active_slot(struct active_request_slot *slot)\n+{\n+#ifdef USE_CURL_MULTI\n+\tint num_transfers;\n+\tlong last_pos = 0;\n+\tlong current_pos;\n+\tfd_set readfds;\n+\tfd_set writefds;\n+\tfd_set excfds;\n+\tint max_fd;\n+\tstruct timeval select_timeout;\n+\tCURLMcode curlm_result;\n+\n+\twhile (!slot->done) {\n+\t\tdata_received = 0;\n+\t\tdo {\n+\t\t\tcurlm_result = curl_multi_perform(curlm,\n+\t\t\t\t\t\t\t  &num_transfers);\n+\t\t} while (curlm_result == CURLM_CALL_MULTI_PERFORM);\n+\t\tif (num_transfers < active_requests) {\n+\t\t\tprocess_curl_messages();\n+\t\t\tprocess_request_queue();\n+\t\t}\n+\n+\t\tif (!data_received && slot->local != NULL) {\n+\t\t\tcurrent_pos = ftell(slot->local);\n+\t\t\tif (current_pos > last_pos)\n+\t\t\t\tdata_received++;\n+\t\t\tlast_pos = current_pos;\n+\t\t}\n+\n+\t\tif (!slot->done && !data_received) {\n+\t\t\tmax_fd = 0;\n+\t\t\tFD_ZERO(&readfds);\n+\t\t\tFD_ZERO(&writefds);\n+\t\t\tFD_ZERO(&excfds);\n+\t\t\tselect_timeout.tv_sec = 0;\n+\t\t\tselect_timeout.tv_usec = 50000;\n+\t\t\tselect(max_fd, &readfds, &writefds,\n+\t\t\t       &excfds, &select_timeout);\n+\t\t}\n+\t}\n+#else\n+\tslot->curl_result = curl_easy_perform(slot->curl);\n+\tactive_requests--;\n+#endif\n+}\n+\n+static void start_check(struct transfer_request *request)\n+{\n+\tchar *hex = sha1_to_hex(request->sha1);\n+\tstruct active_request_slot *slot;\n+\tchar *posn;\n+\n+\trequest->url = xmalloc(strlen(remote->url) + 55);\n+\tstrcpy(request->url, remote->url);\n+\tposn = request->url + strlen(remote->url);\n+\tstrcpy(posn, \"objects/\");\n+\tposn += 8;\n+\tmemcpy(posn, hex, 2);\n+\tposn += 2;\n+\t*(posn++) = '/';\n+\tstrcpy(posn, hex + 2);\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, request->url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trequest->slot = slot;\n+\t\trequest->state = RUN_HEAD;\n+\t} else {\n+\t\trequest->state = ABORTED;\n+\t\tfree(request->url);\n+\t}\n+}\n+\n+static void start_mkcol(struct transfer_request *request)\n+{\n+\tchar *hex = sha1_to_hex(request->sha1);\n+\tstruct active_request_slot *slot;\n+\tchar *posn;\n+\n+\trequest->url = xmalloc(strlen(remote->url) + 13);\n+\tstrcpy(request->url, remote->url);\n+\tposn = request->url + strlen(remote->url);\n+\tstrcpy(posn, \"objects/\");\n+\tposn += 8;\n+\tmemcpy(posn, hex, 2);\n+\tposn += 2;\n+\tstrcpy(posn, \"/\");\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, request->url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trequest->slot = slot;\n+\t\trequest->state = RUN_MKCOL;\n+\t} else {\n+\t\trequest->state = ABORTED;\n+\t\tfree(request->url);\n+\t}\n+}\n+\n+static void start_put(struct transfer_request *request)\n+{\n+\tchar *hex = sha1_to_hex(request->sha1);\n+\tstruct active_request_slot *slot;\n+\tchar *posn;\n+\tchar type[20];\n+\tchar hdr[50];\n+\tvoid *unpacked;\n+\tunsigned long len;\n+\tint hdrlen;\n+\tssize_t size;\n+\tz_stream stream;\n+\n+\tunpacked = read_sha1_file(request->sha1, type, &len);\n+\thdrlen = sprintf(hdr, \"%s %lu\", type, len) + 1;\n+\n+\t/* Set it up */\n+\tmemset(&stream, 0, sizeof(stream));\n+\tdeflateInit(&stream, Z_BEST_COMPRESSION);\n+\tsize = deflateBound(&stream, len + hdrlen);\n+\trequest->buffer.buffer = xmalloc(size);\n+\n+\t/* Compress it */\n+\tstream.next_out = request->buffer.buffer;\n+\tstream.avail_out = size;\n+\n+\t/* First header.. */\n+\tstream.next_in = (void *)hdr;\n+\tstream.avail_in = hdrlen;\n+\twhile (deflate(&stream, 0) == Z_OK)\n+\t\t/* nothing */;\n+\n+\t/* Then the data itself.. */\n+\tstream.next_in = unpacked;\n+\tstream.avail_in = len;\n+\twhile (deflate(&stream, Z_FINISH) == Z_OK)\n+\t\t/* nothing */;\n+\tdeflateEnd(&stream);\n+\tfree(unpacked);\n+\n+\trequest->buffer.size = stream.total_out;\n+\trequest->buffer.posn = 0;\n+\n+\tif (request->url != NULL)\n+\t\tfree(request->url);\n+\trequest->url = xmalloc(strlen(remote->url) + \n+\t\t\t       strlen(request->lock_token) + 51);\n+\tstrcpy(request->url, remote->url);\n+\tposn = request->url + strlen(remote->url);\n+\tstrcpy(posn, \"objects/\");\n+\tposn += 8;\n+\tmemcpy(posn, hex, 2);\n+\tposn += 2;\n+\t*(posn++) = '/';\n+\tstrcpy(posn, hex + 2);\n+\trequest->dest = xmalloc(strlen(request->url) + 14);\n+\tsprintf(request->dest, \"Destination: %s\", request->url);\n+\tposn += 38;\n+\t*(posn++) = '.';\n+\tstrcpy(posn, request->lock_token);\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_PUT, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, request->url);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trequest->slot = slot;\n+\t\trequest->state = RUN_PUT;\n+\t} else {\n+\t\trequest->state = ABORTED;\n+\t\tfree(request->url);\n+\t}\n+}\n+\n+static void start_move(struct transfer_request *request)\n+{\n+\tstruct active_request_slot *slot;\n+\tstruct curl_slist *dav_headers = NULL;\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);\n+\tdav_headers = curl_slist_append(dav_headers, request->dest);\n+\tdav_headers = curl_slist_append(dav_headers, \"Overwrite: T\");\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, request->url);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trequest->slot = slot;\n+\t\trequest->state = RUN_MOVE;\n+\t} else {\n+\t\trequest->state = ABORTED;\n+\t\tfree(request->url);\n+\t}\n+}\n+\n+static void finish_request(struct transfer_request *request)\n+{\n+\trequest->curl_result =\trequest->slot->curl_result;\n+\trequest->http_code = request->slot->http_code;\n+\trequest->slot = NULL;\n+\tif (request->headers != NULL)\n+\t\tcurl_slist_free_all(request->headers);\n+\tif (request->state == RUN_HEAD) {\n+\t\tif (request->http_code == 404) {\n+\t\t\trequest->state = NEED_PUSH;\n+\t\t} else if (request->curl_result == CURLE_OK) {\n+\t\t\trequest->state = COMPLETE;\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"HEAD %s failed, aborting (%d/%ld)\\n\",\n+\t\t\t\tsha1_to_hex(request->sha1),\n+\t\t\t\trequest->curl_result, request->http_code);\n+\t\t\trequest->state = ABORTED;\n+\t\t\taborted = 1;\n+\t\t}\n+\t} else if (request->state == RUN_MKCOL) {\n+\t\tif (request->curl_result == CURLE_OK ||\n+\t\t    request->http_code == 405) {\n+\t\t\tstart_put(request);\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"MKCOL %s failed, aborting (%d/%ld)\\n\",\n+\t\t\t\tsha1_to_hex(request->sha1),\n+\t\t\t\trequest->curl_result, request->http_code);\n+\t\t\trequest->state = ABORTED;\n+\t\t\taborted = 1;\n+\t\t}\n+\t} else if (request->state == RUN_PUT) {\n+\t\tif (request->curl_result == CURLE_OK) {\n+\t\t\tstart_move(request);\n+\t\t} else {\n+\t\t\tfprintf(stderr,\t\"PUT %s failed, aborting (%d/%ld)\\n\",\n+\t\t\t\tsha1_to_hex(request->sha1),\n+\t\t\t\trequest->curl_result, request->http_code);\n+\t\t\trequest->state = ABORTED;\n+\t\t\taborted = 1;\n+\t\t}\n+\t} else if (request->state == RUN_MOVE) {\n+\t\tif (request->curl_result == CURLE_OK) {\n+\t\t\tif (push_verbosely)\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"sent %s\\n\",\n+\t\t\t\t\tsha1_to_hex(request->sha1));\n+\t\t\trequest->state = COMPLETE;\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"MOVE %s failed, aborting (%d/%ld)\\n\",\n+\t\t\t\tsha1_to_hex(request->sha1),\n+\t\t\t\trequest->curl_result, request->http_code);\n+\t\t\trequest->state = ABORTED;\n+\t\t\taborted = 1;\n+\t\t}\n+\t}\n+}\n+\n+static void release_request(struct transfer_request *request)\n+{\n+\tstruct transfer_request *entry = request_queue_head;\n+\n+\tif (request == request_queue_head) {\n+\t\trequest_queue_head = request->next;\n+\t} else {\n+\t\twhile (entry->next != NULL && entry->next != request)\n+\t\t\tentry = entry->next;\n+\t\tif (entry->next == request)\n+\t\t\tentry->next = entry->next->next;\n+\t}\n+\n+\tfree(request->url);\n+\tfree(request);\n+}\n+\n+#ifdef USE_CURL_MULTI\n+void process_curl_messages(void)\n+{\n+\tint num_messages;\n+\tstruct active_request_slot *slot;\n+\tstruct transfer_request *request = NULL;\n+\tCURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);\n+\n+\twhile (curl_message != NULL) {\n+\t\tif (curl_message->msg == CURLMSG_DONE) {\n+\t\t\tslot = active_queue_head;\n+\t\t\twhile (slot != NULL &&\n+\t\t\t       slot->curl != curl_message->easy_handle)\n+\t\t\t\tslot = slot->next;\n+\t\t\tif (slot != NULL) {\n+\t\t\t\tcurl_multi_remove_handle(curlm, slot->curl);\n+\t\t\t\tactive_requests--;\n+\t\t\t\tslot->done = 1;\n+\t\t\t\tslot->in_use = 0;\n+\t\t\t\tslot->curl_result = curl_message->data.result;\n+\t\t\t\tcurl_easy_getinfo(slot->curl,\n+\t\t\t\t\t\t  CURLINFO_HTTP_CODE,\n+\t\t\t\t\t\t  &slot->http_code);\n+\t\t\t\trequest = request_queue_head;\n+\t\t\t\twhile (request != NULL &&\n+\t\t\t\t       request->slot != slot)\n+\t\t\t\t\trequest = request->next;\n+\t\t\t\tif (request != NULL)\n+\t\t\t\t\tfinish_request(request);\n+\t\t\t} else {\n+\t\t\t\tfprintf(stderr, \"Received DONE message for unknown request!\\n\");\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"Unknown CURL message received: %d\\n\",\n+\t\t\t\t(int)curl_message->msg);\n+\t\t}\n+\t\tcurl_message = curl_multi_info_read(curlm, &num_messages);\n+\t}\n+}\n+\n+void process_request_queue(void)\n+{\n+\tstruct transfer_request *request = request_queue_head;\n+\tstruct active_request_slot *slot = active_queue_head;\n+\tint num_transfers;\n+\n+\tif (aborted)\n+\t\treturn;\n+\n+\twhile (active_requests < max_requests && request != NULL) {\n+\t\tif (!pushing && request->state == NEED_CHECK) {\n+\t\t\tstart_check(request);\n+\t\t\tcurl_multi_perform(curlm, &num_transfers);\n+\t\t} else if (pushing && request->state == NEED_PUSH) {\n+\t\t\tstart_mkcol(request);\n+\t\t\tcurl_multi_perform(curlm, &num_transfers);\n+\t\t}\n+\t\trequest = request->next;\n+\t}\n+\n+\twhile (slot != NULL) {\n+\t\tif (!slot->in_use && slot->curl != NULL) {\n+\t\t\tcurl_easy_cleanup(slot->curl);\n+\t\t\tslot->curl = NULL;\n+\t\t}\n+\t\tslot = slot->next;\n+\t}\t\t\t\t\n+}\n+#endif\n+\n+void process_waiting_requests(void)\n+{\n+\tstruct active_request_slot *slot = active_queue_head;\n+\n+\twhile (slot != NULL)\n+\t\tif (slot->in_use) {\n+\t\t\trun_active_slot(slot);\n+\t\t\tslot = active_queue_head;\n+\t\t} else {\n+\t\t\tslot = slot->next;\n+\t\t}\n+}\n+\n+void add_request(unsigned char *sha1, char *lock_token)\n+{\n+\tstruct transfer_request *request = request_queue_head;\n+\tstruct transfer_request *tail;\n+\tstruct packed_git *target;\n+\t\n+\twhile (request != NULL && memcmp(request->sha1, sha1, 20))\n+\t\trequest = request->next;\n+\tif (request != NULL)\n+\t\treturn;\n+\n+\ttarget = find_sha1_pack(sha1, remote->packs);\n+\tif (target)\n+\t\treturn;\n+\n+\trequest = xmalloc(sizeof(*request));\n+\tmemcpy(request->sha1, sha1, 20);\n+\trequest->url = NULL;\n+\trequest->lock_token = lock_token;\n+\trequest->headers = NULL;\n+\trequest->state = NEED_CHECK;\n+\trequest->next = NULL;\n+\n+\tif (request_queue_head == NULL) {\n+\t\trequest_queue_head = request;\n+\t} else {\n+\t\ttail = request_queue_head;\n+\t\twhile (tail->next != NULL) {\n+\t\t\ttail = tail->next;\n+\t\t}\n+\t\ttail->next = request;\n+\t}\n+#ifdef USE_CURL_MULTI\n+\tprocess_request_queue();\n+\tprocess_curl_messages();\n+#endif\n+}\n+\n+static int fetch_index(unsigned char *sha1)\n+{\n+\tchar *hex = sha1_to_hex(sha1);\n+\tchar *filename;\n+\tchar *url;\n+\tchar tmpfile[PATH_MAX];\n+\tlong prev_posn = 0;\n+\tchar range[RANGE_HEADER_SIZE];\n+\tstruct curl_slist *range_header = NULL;\n+\n+\tFILE *indexfile;\n+\tstruct active_request_slot *slot;\n+\n+\tif (has_pack_index(sha1))\n+\t\treturn 0;\n+\n+\tif (push_verbosely)\n+\t\tfprintf(stderr, \"Getting index for pack %s\\n\", hex);\n+\t\n+\turl = xmalloc(strlen(remote->url) + 64);\n+\tsprintf(url, \"%s/objects/pack/pack-%s.idx\", remote->url, hex);\n+\t\n+\tfilename = sha1_pack_index_name(sha1);\n+\tsnprintf(tmpfile, sizeof(tmpfile), \"%s.temp\", filename);\n+\tindexfile = fopen(tmpfile, \"a\");\n+\tif (!indexfile)\n+\t\treturn error(\"Unable to open local file %s for pack index\",\n+\t\t\t     filename);\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);\n+\tslot->local = indexfile;\n+\n+\t/* If there is data present from a previous transfer attempt,\n+\t   resume where it left off */\n+\tprev_posn = ftell(indexfile);\n+\tif (prev_posn>0) {\n+\t\tif (push_verbosely)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Resuming fetch of index for pack %s at byte %ld\\n\",\n+\t\t\t\thex, prev_posn);\n+\t\tsprintf(range, \"Range: bytes=%ld-\", prev_posn);\n+\t\trange_header = curl_slist_append(range_header, range);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);\n+\t}\n+\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tif (slot->curl_result != CURLE_OK) {\n+\t\t\tfree(url);\n+\t\t\tfclose(indexfile);\n+\t\t\treturn error(\"Unable to get pack index %s\\n%s\", url,\n+\t\t\t\t     curl_errorstr);\n+\t\t}\n+\t} else {\n+\t\tfree(url);\n+\t\treturn error(\"Unable to start request\");\n+\t}\n+\n+\tfree(url);\n+\tfclose(indexfile);\n+\n+\treturn move_temp_to_file(tmpfile, filename);\n+}\n+\n+static int setup_index(unsigned char *sha1)\n+{\n+\tstruct packed_git *new_pack;\n+\tif (has_pack_file(sha1))\n+\t\treturn 0; // don't list this as something we can get\n+\n+\tif (fetch_index(sha1))\n+\t\treturn -1;\n+\n+\tnew_pack = parse_pack_index(sha1);\n+\tnew_pack->next = remote->packs;\n+\tremote->packs = new_pack;\n+\treturn 0;\n+}\n+\n+static int fetch_indices()\n+{\n+\tunsigned char sha1[20];\n+\tchar *url;\n+\tstruct buffer buffer;\n+\tchar *data;\n+\tint i = 0;\n+\n+\tstruct active_request_slot *slot;\n+\n+\tdata = xmalloc(4096);\n+\tmemset(data, 0, 4096);\n+\tbuffer.size = 4096;\n+\tbuffer.posn = 0;\n+\tbuffer.buffer = data;\n+\n+\tif (push_verbosely)\n+\t\tfprintf(stderr, \"Getting pack list\\n\");\n+\t\n+\turl = xmalloc(strlen(remote->url) + 21);\n+\tsprintf(url, \"%s/objects/info/packs\", remote->url);\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n+\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tif (slot->curl_result != CURLE_OK) {\n+\t\t\tfree(buffer.buffer);\n+\t\t\tfree(url);\n+\t\t\tif (slot->http_code == 404)\n+\t\t\t\treturn 0;\n+\t\t\telse\n+\t\t\t\treturn error(\"%s\", curl_errorstr);\n+\t\t}\n+\t} else {\n+\t\tfree(buffer.buffer);\n+\t\tfree(url);\n+\t\treturn error(\"Unable to start request\");\n+\t}\n+\tfree(url);\n+\n+\tdata = buffer.buffer;\n+\twhile (i < buffer.posn) {\n+\t\tswitch (data[i]) {\n+\t\tcase 'P':\n+\t\t\ti++;\n+\t\t\tif (i + 52 < buffer.posn &&\n+\t\t\t    !strncmp(data + i, \" pack-\", 6) &&\n+\t\t\t    !strncmp(data + i + 46, \".pack\\n\", 6)) {\n+\t\t\t\tget_sha1_hex(data + i + 6, sha1);\n+\t\t\t\tsetup_index(sha1);\n+\t\t\t\ti += 51;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\tdefault:\n+\t\t\twhile (data[i] != '\\n')\n+\t\t\t\ti++;\n+\t\t}\n+\t\ti++;\n+\t}\n+\n+\tfree(buffer.buffer);\n+\treturn 0;\n+}\n+\n+static inline int needs_quote(int ch)\n+{\n+\tswitch (ch) {\n+\tcase '/': case '-': case '.':\n+\tcase 'A'...'Z':\tcase 'a'...'z':\tcase '0'...'9':\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn 1;\n+\t}\n+}\n+\n+static inline int hex(int v)\n+{\n+\tif (v < 10) return '0' + v;\n+\telse return 'A' + v - 10;\n+}\n+\n+static char *quote_ref_url(const char *base, const char *ref)\n+{\n+\tconst char *cp;\n+\tchar *dp, *qref;\n+\tint len, baselen, ch;\n+\n+\tbaselen = strlen(base);\n+\tlen = baselen + 12; /* \"refs/heads/\" + NUL */\n+\tfor (cp = ref; (ch = *cp) != 0; cp++, len++)\n+\t\tif (needs_quote(ch))\n+\t\t\tlen += 2; /* extra two hex plus replacement % */\n+\tqref = xmalloc(len);\n+\tmemcpy(qref, base, baselen);\n+\tmemcpy(qref + baselen, \"refs/heads/\", 11);\n+\tfor (cp = ref, dp = qref + baselen + 11; (ch = *cp) != 0; cp++) {\n+\t\tif (needs_quote(ch)) {\n+\t\t\t*dp++ = '%';\n+\t\t\t*dp++ = hex((ch >> 4) & 0xF);\n+\t\t\t*dp++ = hex(ch & 0xF);\n+\t\t}\n+\t\telse\n+\t\t\t*dp++ = ch;\n+\t}\n+\t*dp = 0;\n+\n+\treturn qref;\n+}\n+\n+int fetch_ref(char *ref, unsigned char *sha1)\n+{\n+        char *url;\n+        char hex[42];\n+        struct buffer buffer;\n+\tchar *base = remote->url;\n+\tstruct active_request_slot *slot;\n+        buffer.size = 41;\n+        buffer.posn = 0;\n+        buffer.buffer = hex;\n+        hex[41] = '\\0';\n+        \n+\turl = quote_ref_url(base, ref);\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n+\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tif (slot->curl_result != CURLE_OK)\n+\t\t\treturn error(\"Couldn't get %s for %s\\n%s\",\n+\t\t\t\t     url, ref, curl_errorstr);\n+\t} else {\n+\t\treturn error(\"Unable to start request\");\n+\t}\n+\n+        hex[40] = '\\0';\n+        get_sha1_hex(hex, sha1);\n+        return 0;\n+}\n+\n+static void\n+start_lockprop_element(void *userData, const char *name, const char **atts)\n+{\n+\tstruct lockprop *prop = (struct lockprop *)userData;\n+\n+\tif (prop->lock_type && !strcmp(name, \"D:write\")) {\n+\t\tif (prop->lock_exclusive) {\n+\t\t\tprop->lock_exclusive_write = 1;\n+\t\t}\n+\t} else if (prop->lock_scope && !strcmp(name, \"D:exclusive\")) {\n+\t\tprop->lock_exclusive = 1;\n+\t} else if (prop->lock_entry) {\n+\t\tif (!strcmp(name, \"D:lockscope\")) {\n+\t\t\tprop->lock_scope = 1;\n+\t\t} else if (!strcmp(name, \"D:locktype\")) {\n+\t\t\tprop->lock_type = 1;\n+\t\t}\n+\t} else if (prop->supported_lock) {\n+\t\tif (!strcmp(name, \"D:lockentry\")) {\n+\t\t\tprop->lock_entry = 1;\n+\t\t}\n+\t} else if (!strcmp(name, \"D:supportedlock\")) {\n+\t\tprop->supported_lock = 1;\n+\t}\n+}\n+\n+static void\n+end_lockprop_element(void *userData, const char *name)\n+{\n+\tstruct lockprop *prop = (struct lockprop *)userData;\n+\n+\tif (!strcmp(name, \"D:lockentry\")) {\n+\t\tprop->lock_entry = 0;\n+\t\tprop->lock_scope = 0;\n+\t\tprop->lock_type = 0;\n+\t\tprop->lock_exclusive = 0;\n+\t} else if (!strcmp(name, \"D:supportedlock\")) {\n+\t\tprop->supported_lock = 0;\n+\t}\n+}\n+\n+size_t process_lock_header( void *ptr, size_t size, size_t nmemb, void *stream)\n+{\n+\tsize_t header_size = size*nmemb;\n+\tchar *start;\n+\tchar *end;\n+\n+\tif (!strncmp(ptr, \"Lock-Token: <opaquelocktoken:\", 29)) {\n+\t\tstart = ptr + 29;\n+\t\tfor (end = ptr + header_size;\n+\t\t     *(end - 1) == '\\r' || *(end - 1) == '\\n' || *(end - 1) == '>';\n+\t\t     end--) {}\n+\t\tif (end > start) {\n+\t\t\tlock_token = xmalloc(end - start + 1);\n+\t\t\tmemcpy(lock_token, start, end - start);\n+\t\t\tlock_token[end - start] = 0;\n+\t\t}\n+\t}\n+\n+\treturn header_size;\n+}\n+\n+char *lock_remote(char *file, int timeout)\n+{\n+\tstruct active_request_slot *slot;\n+\tstruct buffer out_buffer;\n+\tchar *out_data;\n+\tchar *url;\n+\tchar timeout_header[25];\n+\tstruct curl_slist *dav_headers = NULL;\n+\n+\tif (lock_token != NULL)\n+\t\tfree(lock_token);\n+\n+\tout_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2;\n+\tout_data = xmalloc(out_buffer.size + 1);\n+\tsnprintf(out_data, out_buffer.size + 1, LOCK_REQUEST, git_default_email);\n+\tout_buffer.posn = 0;\n+\tout_buffer.buffer = out_data;\n+\n+\tsprintf(timeout_header, \"Timeout: Second-%d\", timeout);\n+\turl = xmalloc(strlen(remote->url) + strlen(file) + 1);\n+\tsprintf(url, \"%s%s\", remote->url, file);\n+\tdav_headers = curl_slist_append(dav_headers, timeout_header);\n+\tdav_headers = curl_slist_append(dav_headers, \"Content-Type: text/xml\");\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION,\n+\t\tprocess_lock_header);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tfree(out_data);\n+\t\tif (slot->curl_result != CURLE_OK) {\n+\t\t\tfprintf(stderr, \"Got HTTP error %ld\\n\", slot->http_code);\n+\t\t\treturn NULL;\n+\t\t}\n+\t} else {\n+\t\tfree(out_data);\n+\t\tfprintf(stderr, \"Unable to start request\\n\");\n+\t}\n+\n+\treturn strdup(lock_token);\n+}\n+\n+int unlock_remote(char *file, char *lock_token)\n+{\n+\tstruct active_request_slot *slot;\n+\tchar *url;\n+\tchar *lock_token_header;\n+\tstruct curl_slist *dav_headers = NULL;\n+\tint rc = 0;\n+\n+\tif (lock_token == NULL) {\n+\t\tfprintf(stderr, \"Unable to unlock, no lock token\");\n+\t\treturn 0;\n+\t}\n+\n+\tlock_token_header = xmalloc(strlen(lock_token) + 31);\n+\tsprintf(lock_token_header, \"Lock-Token: <opaquelocktoken:%s>\",\n+\t\tlock_token);\n+\turl = xmalloc(strlen(remote->url) + strlen(file) + 1);\n+\tsprintf(url, \"%s%s\", remote->url, file);\n+\tdav_headers = curl_slist_append(dav_headers, lock_token_header);\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tif (slot->curl_result == CURLE_OK)\n+\t\t\trc = 1;\n+\t\telse\n+\t\t\tfprintf(stderr, \"Got HTTP error %ld\\n\",\n+\t\t\t\tslot->http_code);\n+\t} else {\n+\t\tfprintf(stderr, \"Unable to start request\\n\");\n+\t}\n+\n+\tcurl_slist_free_all(dav_headers);\n+\tfree(lock_token_header);\n+\tfree(url);\n+\n+\treturn rc;\n+}\n+\n+int check_locking()\n+{\n+\tstruct active_request_slot *slot;\n+\tstruct buffer in_buffer;\n+\tstruct buffer out_buffer;\n+\tchar *in_data;\n+\tchar *out_data;\n+\tXML_Parser parser = XML_ParserCreate(NULL);\n+\tenum XML_Status result;\n+\tstruct lockprop supported_lock;\n+\tstruct curl_slist *dav_headers = NULL;\n+\n+\tout_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;\n+\tout_data = xmalloc(out_buffer.size + 1);\n+\tsnprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url);\n+\tout_buffer.posn = 0;\n+\tout_buffer.buffer = out_data;\n+\n+\tin_buffer.size = 4096;\n+\tin_data = xmalloc(in_buffer.size);\n+\tin_buffer.posn = 0;\n+\tin_buffer.buffer = in_data;\n+\n+\tdav_headers = curl_slist_append(dav_headers, \"Depth: 0\");\n+\tdav_headers = curl_slist_append(dav_headers, \"Content-Type: text/xml\");\n+\t\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n+\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tfree(out_data);\n+\t\tif (slot->curl_result != CURLE_OK) {\n+\t\t\tfree(in_buffer.buffer);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tXML_SetUserData(parser, &supported_lock);\n+\t\tXML_SetElementHandler(parser, start_lockprop_element,\n+\t\t\t\t      end_lockprop_element);\n+\t\tresult = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1);\n+\t\tfree(in_buffer.buffer);\n+\t\tif (result != XML_STATUS_OK)\n+\t\t\treturn error(\"%s\", XML_ErrorString(\n+\t\t\t\t\t     XML_GetErrorCode(parser)));\n+\t} else {\n+\t\tfree(out_data);\n+\t\tfree(in_buffer.buffer);\n+\t\treturn error(\"Unable to start request\");\n+\t}\n+\n+\tif (supported_lock.lock_exclusive_write)\n+\t\treturn 0;\n+\telse\n+\t\treturn 1;\n+}\n+\n+int is_ancestor(unsigned char *sha1, struct commit *commit)\n+{\n+\tstruct commit_list *parents;\n+\n+\tif (parse_commit(commit))\n+\t\treturn 0;\n+\tparents = commit->parents;\n+\tfor (; parents; parents = parents->next) {\n+\t\tif (!memcmp(sha1, parents->item->object.sha1, 20)) {\n+\t\t\treturn 1;\n+\t\t} else if (parents->item->object.type == commit_type) {\n+\t\t\tif (is_ancestor(\n+\t\t\t\t    sha1,\n+\t\t\t\t    (struct commit *)&parents->item->object\n+\t\t\t\t    ))\n+\t\t\t\treturn 1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+void get_delta(unsigned char *sha1, struct object *obj, char *lock_token)\n+{\n+\tstruct commit *commit;\n+\tstruct commit_list *parents;\n+\tstruct tree *tree;\n+\tstruct tree_entry_list *entry;\n+\n+\tif (sha1 && !memcmp(sha1, obj->sha1, 20))\n+\t\treturn;\n+\n+\tif (aborted)\n+\t\treturn;\n+\n+\tif (obj->type == commit_type) {\n+\t\tif (push_verbosely)\n+\t\t\tfprintf(stderr, \"walk %s\\n\", sha1_to_hex(obj->sha1));\n+\t\tadd_request(obj->sha1, lock_token);\n+\t\tcommit = (struct commit *)obj;\n+\t\tif (parse_commit(commit)) {\n+\t\t\tfprintf(stderr, \"Error parsing commit %s\\n\",\n+\t\t\t\tsha1_to_hex(obj->sha1));\n+\t\t\taborted = 1;\n+\t\t\treturn;\n+\t\t}\n+\t\tparents = commit->parents;\n+\t\tfor (; parents; parents = parents->next)\n+\t\t\tif (sha1 == NULL ||\n+\t\t\t    memcmp(sha1, parents->item->object.sha1, 20))\n+\t\t\t\tget_delta(sha1, &parents->item->object,\n+\t\t\t\t\t  lock_token);\n+\t\tget_delta(sha1, &commit->tree->object, lock_token);\n+\t} else if (obj->type == tree_type) {\n+\t\tif (push_verbosely)\n+\t\t\tfprintf(stderr, \"walk %s\\n\", sha1_to_hex(obj->sha1));\n+\t\tadd_request(obj->sha1, lock_token);\n+\t\ttree = (struct tree *)obj;\n+\t\tif (parse_tree(tree)) {\n+\t\t\tfprintf(stderr, \"Error parsing tree %s\\n\",\n+\t\t\t\tsha1_to_hex(obj->sha1));\n+\t\t\taborted = 1;\n+\t\t\treturn;\n+\t\t}\n+\t\tentry = tree->entries;\n+\t\ttree->entries = NULL;\n+\t\twhile (entry) {\n+\t\t\tstruct tree_entry_list *next = entry->next;\n+\t\t\tget_delta(sha1, entry->item.any, lock_token);\n+\t\t\tfree(entry->name);\n+\t\t\tfree(entry);\n+\t\t\tentry = next;\n+\t\t}\n+\t} else if (obj->type == blob_type || obj->type == tag_type) {\n+\t\tadd_request(obj->sha1, lock_token);\n+\t}\n+}\n+\n+int update_remote(char *remote_path, unsigned char *sha1, char *lock_token)\n+{\n+\tstruct active_request_slot *slot;\n+\tchar *url;\n+\tchar *out_data;\n+\tchar *if_header;\n+\tstruct buffer out_buffer;\n+\tstruct curl_slist *dav_headers = NULL;\n+\tint i;\n+\n+\turl = xmalloc(strlen(remote->url) + strlen(remote_path) + 1);\n+\tsprintf(url, \"%s%s\", remote->url, remote_path);\n+\n+\tif_header = xmalloc(strlen(lock_token) + 25);\n+\tsprintf(if_header, \"If: (<opaquelocktoken:%s>)\", lock_token);\n+\tdav_headers = curl_slist_append(dav_headers, if_header);\n+\n+\tout_buffer.size = 41;\n+\tout_data = xmalloc(out_buffer.size + 1);\n+\ti = snprintf(out_data, out_buffer.size + 1, \"%s\\n\", sha1_to_hex(sha1));\n+\tif (i != out_buffer.size) {\n+\t\tfprintf(stderr, \"Unable to initialize PUT request body\\n\");\n+\t\treturn 0;\n+\t}\n+\tout_buffer.posn = 0;\n+\tout_buffer.buffer = out_data;\n+\n+\tslot = get_active_slot();\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_PUT, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tfree(out_data);\n+\t\tfree(if_header);\n+\t\tfree(url);\n+\t\tif (slot->curl_result != CURLE_OK) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"PUT error: curl result=%d, HTTP code=%ld\\n\",\n+\t\t\t\tslot->curl_result, slot->http_code);\n+\t\t\t/* We should attempt recovery? */\n+\t\t\treturn 0;\n+\t\t}\n+\t} else {\n+\t\tfree(out_data);\n+\t\tfree(if_header);\n+\t\tfree(url);\n+\t\tfprintf(stderr, \"Unable to start PUT request\\n\");\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\tstruct active_request_slot *slot;\n+\tstruct active_request_slot *next_slot;\n+\tstruct transfer_request *request;\n+\tstruct transfer_request *next_request;\n+\tint nr_refspec = 0;\n+\tchar **refspec = NULL;\n+\tint do_remote_update;\n+\tint new_branch;\n+\tint force_this;\n+\tchar *local_ref;\n+\tunsigned char local_sha1[20];\n+\tstruct object *local_object = NULL;\n+\tchar *remote_ref = NULL;\n+\tunsigned char remote_sha1[20];\n+\tchar *remote_lock = NULL;\n+\tchar *remote_path = NULL;\n+\tchar *low_speed_limit;\n+\tchar *low_speed_time;\n+\tint rc = 0;\n+\tint i;\n+\n+\tsetup_ident();\n+\n+\tremote = xmalloc(sizeof(*remote));\n+\tremote->url = NULL;\n+\tremote->packs = NULL;\n+\n+\targv++;\n+\tfor (i = 1; i < argc; i++, argv++) {\n+\t\tchar *arg = *argv;\n+\n+\t\tif (*arg == '-') {\n+\t\t\tif (!strcmp(arg, \"--complete\")) {\n+\t\t\t\tpush_all = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(arg, \"--force\")) {\n+\t\t\t\tforce_all = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(arg, \"--verbose\")) {\n+\t\t\t\tpush_verbosely = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tusage(http_push_usage);\n+\t\t}\n+\t\tif (!remote->url) {\n+\t\t\tremote->url = arg;\n+\t\t\tcontinue;\n+\t\t}\n+\t\trefspec = argv;\n+\t\tnr_refspec = argc - i;\n+\t\tbreak;\n+\t}\n+\n+\tcurl_global_init(CURL_GLOBAL_ALL);\n+\n+#ifdef USE_CURL_MULTI\n+\t{\n+\t\tchar *http_max_requests = getenv(\"GIT_HTTP_MAX_REQUESTS\");\n+\t\tif (http_max_requests != NULL)\n+\t\t\tmax_requests = atoi(http_max_requests);\n+\t}\n+\n+\tcurlm = curl_multi_init();\n+\tif (curlm == NULL) {\n+\t\tfprintf(stderr, \"Error creating curl multi handle.\\n\");\n+\t\treturn 1;\n+\t}\n+#endif\n+\n+\tif (getenv(\"GIT_SSL_NO_VERIFY\"))\n+\t\tcurl_ssl_verify = 0;\n+\n+\tssl_cert = getenv(\"GIT_SSL_CERT\");\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+\tssl_key = getenv(\"GIT_SSL_KEY\");\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tssl_capath = getenv(\"GIT_SSL_CAPATH\");\n+#endif\n+\tssl_cainfo = getenv(\"GIT_SSL_CAINFO\");\n+\n+\tlow_speed_limit = getenv(\"GIT_HTTP_LOW_SPEED_LIMIT\");\n+\tif (low_speed_limit != NULL)\n+\t\tcurl_low_speed_limit = strtol(low_speed_limit, NULL, 10);\n+\tlow_speed_time = getenv(\"GIT_HTTP_LOW_SPEED_TIME\");\n+\tif (low_speed_time != NULL)\n+\t\tcurl_low_speed_time = strtol(low_speed_time, NULL, 10);\n+\n+\tgit_config(http_options);\n+\n+\tif (curl_ssl_verify == -1)\n+\t\tcurl_ssl_verify = 1;\n+\n+#ifdef USE_CURL_MULTI\n+\tif (max_requests < 1)\n+\t\tmax_requests = DEFAULT_MAX_REQUESTS;\n+#endif\n+\n+\tno_pragma_header = curl_slist_append(no_pragma_header, \"Pragma:\");\n+\tdefault_headers = curl_slist_append(default_headers, \"Range:\");\n+\tdefault_headers = curl_slist_append(default_headers, \"Destination:\");\n+\tdefault_headers = curl_slist_append(default_headers, \"If:\");\n+\tdefault_headers = curl_slist_append(default_headers,\n+\t\t\t\t\t    \"Pragma: no-cache\");\n+\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+\tcurl_default = get_curl_handle();\n+#endif\n+\n+\t/* Verify DAV compliance/lock support */\n+\tif (check_locking() != 0) {\n+\t\tfprintf(stderr, \"Error: no DAV locking support on remote repo %s\\n\", remote->url);\n+\t\trc = 1;\n+\t\tgoto cleanup;\n+\t}\n+\n+\t/* Process each refspec */\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tchar *ep;\n+\t\tforce_this = 0;\n+\t\tdo_remote_update = 0;\n+\t\tnew_branch = 0;\n+\t\tlocal_ref = refspec[i];\n+\t\tif (*local_ref == '+') {\n+\t\t\tforce_this = 1;\n+\t\t\tlocal_ref++;\n+\t\t}\n+\t\tep = strchr(local_ref, ':');\n+\t\tif (ep) {\n+\t\t\tremote_ref = ep + 1;\n+\t\t\t*ep = 0;\n+\t\t}\n+\t\telse\n+\t\t\tremote_ref = local_ref;\n+\n+\t\t/* Lock remote branch ref */\n+\t\tif (remote_path)\n+\t\t\tfree(remote_path);\n+\t\tremote_path = xmalloc(strlen(remote_ref) + 12);\n+\t\tsprintf(remote_path, \"refs/heads/%s\", remote_ref);\n+\t\tremote_lock = lock_remote(remote_path, 3600);\n+\t\tif (remote_lock == NULL) {\n+\t\t\tfprintf(stderr, \"Unable to lock remote branch %s\\n\",\n+\t\t\t\tremote_ref);\n+\t\t\trc = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Resolve local and remote refs */\n+\t\tif (fetch_ref(remote_ref, remote_sha1) != 0) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Remote branch %s does not exist on %s\\n\",\n+\t\t\t\tremote_ref, remote->url);\n+\t\t\tnew_branch = 1;\n+\t\t}\n+\t\tif (get_sha1(local_ref, local_sha1) != 0) {\n+\t\t\tfprintf(stderr, \"Error resolving local branch %s\\n\",\n+\t\t\t\tlocal_ref);\n+\t\t\trc = 1;\n+\t\t\tgoto unlock;\n+\t\t}\n+\t\n+\t\t/* Find relationship between local and remote */\n+\t\tlocal_object = parse_object(local_sha1);\n+\t\tif (!local_object) {\n+\t\t\tfprintf(stderr, \"Unable to parse local object %s\\n\",\n+\t\t\t\tsha1_to_hex(local_sha1));\n+\t\t\trc = 1;\n+\t\t\tgoto unlock;\n+\t\t} else if (new_branch) {\n+\t\t\tdo_remote_update = 1;\n+\t\t} else {\n+\t\t\tif (!memcmp(local_sha1, remote_sha1, 20)) {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"* %s: same as branch '%s' of %s\\n\",\n+\t\t\t\t\tlocal_ref, remote_ref, remote->url);\n+\t\t\t} else if (is_ancestor(remote_sha1,\n+\t\t\t\t\t       (struct commit *)local_object)) {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"Remote %s will fast-forward to local %s\\n\",\n+\t\t\t\t\tremote_ref, local_ref);\n+\t\t\t\tdo_remote_update = 1;\n+\t\t\t} else if (force_all || force_this) {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"* %s on %s does not fast forward to local branch '%s', overwriting\\n\",\n+\t\t\t\t\tremote_ref, remote->url, local_ref);\n+\t\t\t\tdo_remote_update = 1;\n+\t\t\t} else {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"* %s on %s does not fast forward to local branch '%s'\\n\",\n+\t\t\t\t\tremote_ref, remote->url, local_ref);\n+\t\t\t\trc = 1;\n+\t\t\t\tgoto unlock;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* Generate and check list of required objects */\n+\t\tpushing = 0;\n+\t\tif (do_remote_update || push_all)\n+\t\t\tfetch_indices();\n+\t\tget_delta(push_all ? NULL : remote_sha1,\n+\t\t\t  local_object, remote_lock);\n+\t\tprocess_waiting_requests();\n+\n+\t\t/* Push missing objects to remote, this would be a\n+\t\t   convenient time to pack them first if appropriate. */\n+\t\tpushing = 1;\n+\t\tprocess_request_queue();\n+\t\tprocess_waiting_requests();\n+\n+\t\t/* Update the remote branch if all went well */\n+\t\tif (do_remote_update) {\n+\t\t\tif (!aborted && update_remote(remote_path,\n+\t\t\t\t\t\t      local_sha1,\n+\t\t\t\t\t\t      remote_lock)) {\n+\t\t\t\tfprintf(stderr, \"%s remote branch %s\\n\",\n+\t\t\t\t\tnew_branch ? \"Created\" : \"Updated\",\n+\t\t\t\t\tremote_ref);\n+\t\t\t} else {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"Unable to %s remote branch %s\\n\",\n+\t\t\t\t\tnew_branch ? \"create\" : \"update\",\n+\t\t\t\t\tremote_ref);\n+\t\t\t\trc = 1;\n+\t\t\t\tgoto unlock;\n+\t\t\t}\n+\t\t}\n+\n+\tunlock:\n+\t\tunlock_remote(remote_path, remote_lock);\n+\t\tfree(remote_path);\n+\t\tfree(remote_lock);\n+\t}\n+\n+ cleanup:\n+\tfree(remote);\n+\n+\tcurl_slist_free_all(no_pragma_header);\n+\tcurl_slist_free_all(default_headers);\n+\n+\tslot = active_queue_head;\n+\twhile (slot != NULL) {\n+\t\tnext_slot = slot->next;\n+\t\tif (slot->curl != NULL)\n+\t\t\tcurl_easy_cleanup(slot->curl);\n+\t\tfree(slot);\n+\t\tslot = next_slot;\n+\t}\n+\n+\trequest = request_queue_head;\n+\twhile (request != NULL) {\n+\t\tnext_request = request->next;\n+\t\trelease_request(request);\n+\t\tfree(request);\n+\t\trequest = next_request;\n+\t}\n+\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+\tcurl_easy_cleanup(curl_default);\n+#endif\n+#ifdef USE_CURL_MULTI\n+\tcurl_multi_cleanup(curlm);\n+#endif\n+\tcurl_global_cleanup();\n+\treturn rc;\n+}\n---\n0.99.9.GIT\n"},{"id":"11023","messageId":"Pine.LNX.4.63.0511022040080.13353@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"2317","inReplyTo":"20051102191924.GC3928@reactrix.com","subject":"Re: [PATCH 1/2] Add support for pushing to a remote repository using HTTP/DAV","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2005-11-02T19:43:04Z","receivedAt":"2005-11-02T19:43:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 2 Nov 2005, Nick Hengeveld wrote:\n\n> Add support for pushing to a remote repository using HTTP/DAV\n\nGreat! I will try to test it in the next few days.\n\nCiao,\nDscho\n"}]}