{"thread":{"id":"3626","subject":"[PATCH 5/6] http-push: support for updating remote info/refs","startedAt":"2006-03-11T04:18:18Z","lastAt":"2006-03-11T04:18:18Z","messageCount":1,"participants":["Nick Hengeveld"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"17447","messageId":"20060311041818.GG3997@reactrix.com","threadId":"3626","inReplyTo":null,"subject":"[PATCH 5/6] http-push: support for updating remote info/refs","fromName":"Nick Hengeveld","fromEmail":"nickh@reactrix.com","sentAt":"2006-03-11T04:18:18Z","receivedAt":"2006-03-11T04:18:18Z","isPatch":true,"sender":{"key":"nickh@reactrix.com","avatar":null},"body":"If info/refs exists on the remote, get a lock on info/refs, make sure that\nthere is a local copy of the object referenced in each remote ref (in case\nsomeone else added a tag we don't have locally), do all the refspec updates,\nand generate and send an updated info/refs file.\n\n---\n\n http-push.c |  547 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 536 insertions(+), 11 deletions(-)\n\nb29c6a3ec299241894a3effd8cea4d00ea117a8c\ndiff --git a/http-push.c b/http-push.c\nindex ece40da..65c6664 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -22,6 +22,7 @@ enum XML_Status {\n #define XML_STATUS_ERROR 0\n #endif\n \n+#define PREV_BUF_SIZE 4096\n #define RANGE_HEADER_SIZE 30\n \n /* DAV methods */\n@@ -58,9 +59,10 @@ enum XML_Status {\n \n /* bits #0-4 in revision.h */\n \n-#define LOCAL   (1u << 5)\n-#define REMOTE  (1u << 6)\n-#define PUSHING (1u << 7)\n+#define LOCAL    (1u << 5)\n+#define REMOTE   (1u << 6)\n+#define FETCHING (1u << 7)\n+#define PUSHING  (1u << 8)\n \n static int pushing = 0;\n static int aborted = 0;\n@@ -79,6 +81,9 @@ struct repo\n {\n \tchar *url;\n \tint path_len;\n+\tint has_info_refs;\n+\tint can_update_info_refs;\n+\tint has_info_packs;\n \tstruct packed_git *packs;\n \tstruct remote_lock *locks;\n };\n@@ -86,6 +91,9 @@ struct repo\n static struct repo *remote = NULL;\n \n enum transfer_state {\n+\tNEED_FETCH,\n+\tRUN_FETCH_LOOSE,\n+\tRUN_FETCH_PACKED,\n \tNEED_PUSH,\n \tRUN_MKCOL,\n \tRUN_PUT,\n@@ -104,6 +112,8 @@ struct transfer_request\n \tstruct buffer buffer;\n \tchar filename[PATH_MAX];\n \tchar tmpfile[PATH_MAX];\n+\tint local_fileno;\n+\tFILE *local_stream;\n \tenum transfer_state state;\n \tCURLcode curl_result;\n \tchar errorstr[CURL_ERROR_SIZE];\n@@ -113,6 +123,7 @@ struct transfer_request\n \tz_stream stream;\n \tint zret;\n \tint rename;\n+\tvoid *userData;\n \tstruct active_request_slot *slot;\n \tstruct transfer_request *next;\n };\n@@ -159,6 +170,7 @@ struct remote_ls_ctx\n };\n \n static void finish_request(struct transfer_request *request);\n+static void release_request(struct transfer_request *request);\n \n static void process_response(void *callback_data)\n {\n@@ -168,6 +180,258 @@ static void process_response(void *callb\n \tfinish_request(request);\n }\n \n+static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,\n+\t\t\t       void *data)\n+{\n+\tunsigned char expn[4096];\n+\tsize_t size = eltsize * nmemb;\n+\tint posn = 0;\n+\tstruct transfer_request *request = (struct transfer_request *)data;\n+\tdo {\n+\t\tssize_t retval = write(request->local_fileno,\n+\t\t\t\t       ptr + posn, size - posn);\n+\t\tif (retval < 0)\n+\t\t\treturn posn;\n+\t\tposn += retval;\n+\t} while (posn < size);\n+\n+\trequest->stream.avail_in = size;\n+\trequest->stream.next_in = ptr;\n+\tdo {\n+\t\trequest->stream.next_out = expn;\n+\t\trequest->stream.avail_out = sizeof(expn);\n+\t\trequest->zret = inflate(&request->stream, Z_SYNC_FLUSH);\n+\t\tSHA1_Update(&request->c, expn,\n+\t\t\t    sizeof(expn) - request->stream.avail_out);\n+\t} while (request->stream.avail_in && request->zret == Z_OK);\n+\tdata_received++;\n+\treturn size;\n+}\n+\n+static void start_fetch_loose(struct transfer_request *request)\n+{\n+\tchar *hex = sha1_to_hex(request->obj->sha1);\n+\tchar *filename;\n+\tchar prevfile[PATH_MAX];\n+\tchar *url;\n+\tchar *posn;\n+\tint prevlocal;\n+\tunsigned char prev_buf[PREV_BUF_SIZE];\n+\tssize_t prev_read = 0;\n+\tlong prev_posn = 0;\n+\tchar range[RANGE_HEADER_SIZE];\n+\tstruct curl_slist *range_header = NULL;\n+\tstruct active_request_slot *slot;\n+\n+\tfilename = sha1_file_name(request->obj->sha1);\n+\tsnprintf(request->filename, sizeof(request->filename), \"%s\", filename);\n+\tsnprintf(request->tmpfile, sizeof(request->tmpfile),\n+\t\t \"%s.temp\", filename);\n+\n+\tsnprintf(prevfile, sizeof(prevfile), \"%s.prev\", request->filename);\n+\tunlink(prevfile);\n+\trename(request->tmpfile, prevfile);\n+\tunlink(request->tmpfile);\n+\n+\tif (request->local_fileno != -1)\n+\t\terror(\"fd leakage in start: %d\", request->local_fileno);\n+\trequest->local_fileno = open(request->tmpfile,\n+\t\t\t\t     O_WRONLY | O_CREAT | O_EXCL, 0666);\n+\t/* This could have failed due to the \"lazy directory creation\";\n+\t * try to mkdir the last path component.\n+\t */\n+\tif (request->local_fileno < 0 && errno == ENOENT) {\n+\t\tchar *dir = strrchr(request->tmpfile, '/');\n+\t\tif (dir) {\n+\t\t\t*dir = 0;\n+\t\t\tmkdir(request->tmpfile, 0777);\n+\t\t\t*dir = '/';\n+\t\t}\n+\t\trequest->local_fileno = open(request->tmpfile,\n+\t\t\t\t\t     O_WRONLY | O_CREAT | O_EXCL, 0666);\n+\t}\n+\n+\tif (request->local_fileno < 0) {\n+\t\trequest->state = ABORTED;\n+\t\terror(\"Couldn't create temporary file %s for %s: %s\",\n+\t\t      request->tmpfile, request->filename, strerror(errno));\n+\t\treturn;\n+\t}\n+\n+\tmemset(&request->stream, 0, sizeof(request->stream));\n+\n+\tinflateInit(&request->stream);\n+\n+\tSHA1_Init(&request->c);\n+\n+\turl = xmalloc(strlen(remote->url) + 50);\n+\trequest->url = xmalloc(strlen(remote->url) + 50);\n+\tstrcpy(url, remote->url);\n+\tposn = 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+\tstrcpy(request->url, url);\n+\n+\t/* If a previous temp file is present, process what was already\n+\t   fetched. */\n+\tprevlocal = open(prevfile, O_RDONLY);\n+\tif (prevlocal != -1) {\n+\t\tdo {\n+\t\t\tprev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);\n+\t\t\tif (prev_read>0) {\n+\t\t\t\tif (fwrite_sha1_file(prev_buf,\n+\t\t\t\t\t\t     1,\n+\t\t\t\t\t\t     prev_read,\n+\t\t\t\t\t\t     request) == prev_read) {\n+\t\t\t\t\tprev_posn += prev_read;\n+\t\t\t\t} else {\n+\t\t\t\t\tprev_read = -1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} while (prev_read > 0);\n+\t\tclose(prevlocal);\n+\t}\n+\tunlink(prevfile);\n+\n+\t/* Reset inflate/SHA1 if there was an error reading the previous temp\n+\t   file; also rewind to the beginning of the local file. */\n+\tif (prev_read == -1) {\n+\t\tmemset(&request->stream, 0, sizeof(request->stream));\n+\t\tinflateInit(&request->stream);\n+\t\tSHA1_Init(&request->c);\n+\t\tif (prev_posn>0) {\n+\t\t\tprev_posn = 0;\n+\t\t\tlseek(request->local_fileno, SEEK_SET, 0);\n+\t\t\tftruncate(request->local_fileno, 0);\n+\t\t}\n+\t}\n+\n+\tslot = get_active_slot();\n+\tslot->callback_func = process_response;\n+\tslot->callback_data = request;\n+\trequest->slot = slot;\n+\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, request);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);\n+\n+\t/* If we have successfully processed data from a previous fetch\n+\t   attempt, only fetch the data we don't already have. */\n+\tif (prev_posn>0) {\n+\t\tif (push_verbosely)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Resuming fetch of object %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,\n+\t\t\t\t CURLOPT_HTTPHEADER, range_header);\n+\t}\n+\n+\t/* Try to get the request started, abort the request on error */\n+\trequest->state = RUN_FETCH_LOOSE;\n+\tif (!start_active_slot(slot)) {\n+\t\tfprintf(stderr, \"Unable to start GET request\\n\");\n+\t\tremote->can_update_info_refs = 0;\n+\t\trelease_request(request);\n+\t}\n+}\n+\n+static void start_fetch_packed(struct transfer_request *request)\n+{\n+\tchar *url;\n+\tstruct packed_git *target;\n+\tFILE *packfile;\n+\tchar *filename;\n+\tlong prev_posn = 0;\n+\tchar range[RANGE_HEADER_SIZE];\n+\tstruct curl_slist *range_header = NULL;\n+\n+\tstruct transfer_request *check_request = request_queue_head;\n+\tstruct active_request_slot *slot;\n+\n+\ttarget = find_sha1_pack(request->obj->sha1, remote->packs);\n+\tif (!target) {\n+\t\tfprintf(stderr, \"Unable to fetch %s, will not be able to update server info refs\\n\", sha1_to_hex(request->obj->sha1));\n+\t\tremote->can_update_info_refs = 0;\n+\t\trelease_request(request);\n+\t\treturn;\n+\t}\n+\n+\tfprintf(stderr,\t\"Fetching pack %s\\n\", sha1_to_hex(target->sha1));\n+\tfprintf(stderr, \" which contains %s\\n\", sha1_to_hex(request->obj->sha1));\n+\n+\tfilename = sha1_pack_name(target->sha1);\n+\tsnprintf(request->filename, sizeof(request->filename), \"%s\", filename);\n+\tsnprintf(request->tmpfile, sizeof(request->tmpfile),\n+\t\t \"%s.temp\", filename);\n+\n+\turl = xmalloc(strlen(remote->url) + 64);\n+\tsprintf(url, \"%sobjects/pack/pack-%s.pack\",\n+\t\tremote->url, sha1_to_hex(target->sha1));\n+\n+\t/* Make sure there isn't another open request for this pack */\n+\twhile (check_request) {\n+\t\tif (check_request->state == RUN_FETCH_PACKED &&\n+\t\t    !strcmp(check_request->url, url)) {\n+\t\t\tfree(url);\n+\t\t\trelease_request(request);\n+\t\t\treturn;\n+\t\t}\n+\t\tcheck_request = check_request->next;\n+\t}\n+\n+\tpackfile = fopen(request->tmpfile, \"a\");\n+\tif (!packfile) {\n+\t\tfprintf(stderr, \"Unable to open local file %s for pack\",\n+\t\t\tfilename);\n+\t\tremote->can_update_info_refs = 0;\n+\t\tfree(url);\n+\t\treturn;\n+\t}\n+\n+\tslot = get_active_slot();\n+\tslot->callback_func = process_response;\n+\tslot->callback_data = request;\n+\trequest->slot = slot;\n+\trequest->local_stream = packfile;\n+\trequest->userData = target;\n+\n+\trequest->url = url;\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);\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 = packfile;\n+\n+\t/* If there is data present from a previous transfer attempt,\n+\t   resume where it left off */\n+\tprev_posn = ftell(packfile);\n+\tif (prev_posn>0) {\n+\t\tif (push_verbosely)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Resuming fetch of pack %s at byte %ld\\n\",\n+\t\t\t\tsha1_to_hex(target->sha1), 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+\t/* Try to get the request started, abort the request on error */\n+\trequest->state = RUN_FETCH_PACKED;\n+\tif (!start_active_slot(slot)) {\n+\t\tfprintf(stderr, \"Unable to start GET request\\n\");\n+\t\tremote->can_update_info_refs = 0;\n+\t\trelease_request(request);\n+\t}\n+}\n+\n static void start_mkcol(struct transfer_request *request)\n {\n \tchar *hex = sha1_to_hex(request->obj->sha1);\n@@ -388,6 +652,10 @@ static void release_request(struct trans\n \t\t\tentry->next = entry->next->next;\n \t}\n \n+\tif (request->local_fileno != -1)\n+\t\tclose(request->local_fileno);\n+\tif (request->local_stream)\n+\t\tfclose(request->local_stream);\n \tif (request->url != NULL)\n \t\tfree(request->url);\n \tfree(request);\n@@ -395,7 +663,11 @@ static void release_request(struct trans\n \n static void finish_request(struct transfer_request *request)\n {\n-\trequest->curl_result =\trequest->slot->curl_result;\n+\tstruct stat st;\n+\tstruct packed_git *target;\n+\tstruct packed_git **lst;\n+\n+\trequest->curl_result = request->slot->curl_result;\n \trequest->http_code = request->slot->http_code;\n \trequest->slot = NULL;\n \n@@ -437,7 +709,6 @@ static void finish_request(struct transf\n \t\tif (request->curl_result == CURLE_OK) {\n \t\t\tfprintf(stderr, \"    sent %s\\n\",\n \t\t\t\tsha1_to_hex(request->obj->sha1));\n-\t\t\trequest->state = COMPLETE;\n \t\t\trequest->obj->flags |= REMOTE;\n \t\t\trelease_request(request);\n \t\t} else {\n@@ -447,12 +718,73 @@ static void finish_request(struct transf\n \t\t\trequest->state = ABORTED;\n \t\t\taborted = 1;\n \t\t}\n+\t} else if (request->state == RUN_FETCH_LOOSE) {\n+\t\tfchmod(request->local_fileno, 0444);\n+\t\tclose(request->local_fileno); request->local_fileno = -1;\n+\n+\t\tif (request->curl_result != CURLE_OK &&\n+\t\t    request->http_code != 416) {\n+\t\t\tif (stat(request->tmpfile, &st) == 0) {\n+\t\t\t\tif (st.st_size == 0)\n+\t\t\t\t\tunlink(request->tmpfile);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (request->http_code == 416)\n+\t\t\t\tfprintf(stderr, \"Warning: requested range invalid; we may already have all the data.\\n\");\n+\n+\t\t\tinflateEnd(&request->stream);\n+\t\t\tSHA1_Final(request->real_sha1, &request->c);\n+\t\t\tif (request->zret != Z_STREAM_END) {\n+\t\t\t\tunlink(request->tmpfile);\n+\t\t\t} else if (memcmp(request->obj->sha1, request->real_sha1, 20)) {\n+\t\t\t\tunlink(request->tmpfile);\n+\t\t\t} else {\n+\t\t\t\trequest->rename =\n+\t\t\t\t\tmove_temp_to_file(\n+\t\t\t\t\t\trequest->tmpfile,\n+\t\t\t\t\t\trequest->filename);\n+\t\t\t\tif (request->rename == 0) {\n+\t\t\t\t\trequest->obj->flags |= (LOCAL | REMOTE);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* Try fetching packed if necessary */\n+\t\tif (request->obj->flags & LOCAL)\n+\t\t\trelease_request(request);\n+\t\telse\n+\t\t\tstart_fetch_packed(request);\n+\n+\t} else if (request->state == RUN_FETCH_PACKED) {\n+\t\tif (request->curl_result != CURLE_OK) {\n+\t\t\tfprintf(stderr, \"Unable to get pack file %s\\n%s\",\n+\t\t\t\trequest->url, curl_errorstr);\n+\t\t\tremote->can_update_info_refs = 0;\n+\t\t} else {\n+\t\t\tfclose(request->local_stream);\n+\t\t\trequest->local_stream = NULL;\n+\t\t\tif (!move_temp_to_file(request->tmpfile,\n+\t\t\t\t\t       request->filename)) {\n+\t\t\t\ttarget = (struct packed_git *)request->userData;\n+\t\t\t\tlst = &remote->packs;\n+\t\t\t\twhile (*lst != target)\n+\t\t\t\t\tlst = &((*lst)->next);\n+\t\t\t\t*lst = (*lst)->next;\n+\n+\t\t\t\tif (!verify_pack(target, 0))\n+\t\t\t\t\tinstall_packed_git(target);\n+\t\t\t\telse\n+\t\t\t\t\tremote->can_update_info_refs = 0;\n+\t\t\t}\n+\t\t}\n+\t\trelease_request(request);\n \t}\n }\n \n void fill_active_slots(void)\n {\n \tstruct transfer_request *request = request_queue_head;\n+\tstruct transfer_request *next;\n \tstruct active_request_slot *slot = active_queue_head;\n \tint num_transfers;\n \n@@ -460,7 +792,10 @@ void fill_active_slots(void)\n \t\treturn;\n \n \twhile (active_requests < max_requests && request != NULL) {\n-\t\tif (pushing && request->state == NEED_PUSH) {\n+\t\tnext = request->next;\n+\t\tif (request->state == NEED_FETCH) {\n+\t\t\tstart_fetch_loose(request);\n+\t\t} else if (pushing && request->state == NEED_PUSH) {\n \t\t\tif (remote_dir_exists[request->obj->sha1[0]] == 1) {\n \t\t\t\tstart_put(request);\n \t\t\t} else {\n@@ -468,7 +803,7 @@ void fill_active_slots(void)\n \t\t\t}\n \t\t\tcurl_multi_perform(curlm, &num_transfers);\n \t\t}\n-\t\trequest = request->next;\n+\t\trequest = next;\n \t}\n \n \twhile (slot != NULL) {\n@@ -482,7 +817,38 @@ void fill_active_slots(void)\n \n static void get_remote_object_list(unsigned char parent);\n \n-static void add_request(struct object *obj, struct remote_lock *lock)\n+static void add_fetch_request(struct object *obj)\n+{\n+\tstruct transfer_request *request;\n+\n+\tcheck_locks();\n+\n+\t/*\n+\t * Don't fetch the object if it's known to exist locally\n+\t * or is already in the request queue\n+\t */\n+\tif (remote_dir_exists[obj->sha1[0]] == -1)\n+\t\tget_remote_object_list(obj->sha1[0]);\n+\tif (obj->flags & (LOCAL | FETCHING))\n+\t\treturn;\n+\n+\tobj->flags |= FETCHING;\n+\trequest = xmalloc(sizeof(*request));\n+\trequest->obj = obj;\n+\trequest->url = NULL;\n+\trequest->lock = NULL;\n+\trequest->headers = NULL;\n+\trequest->local_fileno = -1;\n+\trequest->local_stream = NULL;\n+\trequest->state = NEED_FETCH;\n+\trequest->next = request_queue_head;\n+\trequest_queue_head = request;\n+\n+\tfill_active_slots();\n+\tstep_active_slots();\n+}\n+\n+static void add_send_request(struct object *obj, struct remote_lock *lock)\n {\n \tstruct transfer_request *request = request_queue_head;\n \tstruct packed_git *target;\n@@ -510,6 +876,8 @@ static void add_request(struct object *o\n \trequest->url = NULL;\n \trequest->lock = lock;\n \trequest->headers = NULL;\n+\trequest->local_fileno = -1;\n+\trequest->local_stream = NULL;\n \trequest->state = NEED_PUSH;\n \trequest->next = request_queue_head;\n \trequest_queue_head = request;\n@@ -1375,7 +1743,7 @@ static void get_delta(struct rev_info *r\n \t\tp = process_tree(commit->tree, p, NULL, \"\");\n \t\tcommit->object.flags |= LOCAL;\n \t\tif (!(commit->object.flags & UNINTERESTING))\n-\t\t\tadd_request(&commit->object, lock);\n+\t\t\tadd_send_request(&commit->object, lock);\n \t}\n \n \tfor (pending = revs->pending_objects; pending; pending = pending->next) {\n@@ -1402,7 +1770,7 @@ static void get_delta(struct rev_info *r\n \n \twhile (objects) {\n \t\tif (!(objects->item->flags & UNINTERESTING))\n-\t\t\tadd_request(objects->item, lock);\n+\t\t\tadd_send_request(objects->item, lock);\n \t\tobjects = objects->next;\n \t}\n }\n@@ -1483,6 +1851,7 @@ static void one_remote_ref(char *refname\n {\n \tstruct ref *ref;\n \tunsigned char remote_sha1[20];\n+\tstruct object *obj;\n \n \tif (fetch_ref(refname, remote_sha1) != 0) {\n \t\tfprintf(stderr,\n@@ -1491,6 +1860,19 @@ static void one_remote_ref(char *refname\n \t\treturn;\n \t}\n \n+\t/*\n+\t * Fetch a copy of the object if it doesn't exist locally - it\n+\t * may be required for updating server info later.\n+\t */\n+\tif (remote->can_update_info_refs && !has_sha1_file(remote_sha1)) {\n+\t\tobj = lookup_unknown_object(remote_sha1);\n+\t\tif (obj) {\n+\t\t\tfprintf(stderr,\t\"  fetch %s for %s\\n\",\n+\t\t\t\tsha1_to_hex(remote_sha1), refname);\n+\t\t\tadd_fetch_request(obj);\n+\t\t}\n+\t}\n+\n \tint len = strlen(refname) + 1;\n \tref = xcalloc(1, sizeof(*ref) + len);\n \tmemcpy(ref->old_sha1, remote_sha1, 20);\n@@ -1596,6 +1978,125 @@ static void mark_edges_uninteresting(str\n \t}\n }\n \n+static void add_remote_info_ref(struct remote_ls_ctx *ls)\n+{\n+\tstruct buffer *buf = (struct buffer *)ls->userData;\n+\tunsigned char remote_sha1[20];\n+\tstruct object *o;\n+\tint len;\n+\tchar *ref_info;\n+\n+\tif (fetch_ref(ls->dentry_name, remote_sha1) != 0) {\n+\t\tfprintf(stderr,\n+\t\t\t\"Unable to fetch ref %s from %s\\n\",\n+\t\t\tls->dentry_name, remote->url);\n+\t\taborted = 1;\n+\t\treturn;\n+\t}\n+\n+\to = parse_object(remote_sha1);\n+\tif (!o) {\n+\t\tfprintf(stderr,\n+\t\t\t\"Unable to parse object %s for remote ref %s\\n\",\n+\t\t\tsha1_to_hex(remote_sha1), ls->dentry_name);\n+\t\taborted = 1;\n+\t\treturn;\n+\t}\n+\n+\tlen = strlen(ls->dentry_name) + 42;\n+\tref_info = xcalloc(len + 1, 1);\n+\tsprintf(ref_info, \"%s\t%s\\n\",\n+\t\tsha1_to_hex(remote_sha1), ls->dentry_name);\n+\tfwrite_buffer(ref_info, 1, len, buf);\n+\tfree(ref_info);\n+\n+\tif (o->type == tag_type) {\n+\t\to = deref_tag(o, ls->dentry_name, 0);\n+\t\tif (o) {\n+\t\t\tlen = strlen(ls->dentry_name) + 45;\n+\t\t\tref_info = xcalloc(len + 1, 1);\n+\t\t\tsprintf(ref_info, \"%s\t%s^{}\\n\",\n+\t\t\t\tsha1_to_hex(o->sha1), ls->dentry_name);\n+\t\t\tfwrite_buffer(ref_info, 1, len, buf);\n+\t\t\tfree(ref_info);\n+\t\t}\n+\t}\n+}\n+\n+static void update_remote_info_refs(struct remote_lock *lock)\n+{\n+\tstruct buffer buffer;\n+\tstruct active_request_slot *slot;\n+\tstruct slot_results results;\n+\tchar *if_header;\n+\tstruct curl_slist *dav_headers = NULL;\n+\n+\tbuffer.buffer = xmalloc(4096);\n+\tmemset(buffer.buffer, 0, 4096);\n+\tbuffer.size = 4096;\n+\tbuffer.posn = 0;\n+\tremote_ls(\"refs/\", (PROCESS_FILES | RECURSIVE),\n+\t\t  add_remote_info_ref, &buffer);\n+\tif (!aborted) {\n+\t\tif_header = xmalloc(strlen(lock->token) + 25);\n+\t\tsprintf(if_header, \"If: (<opaquelocktoken:%s>)\", lock->token);\n+\t\tdav_headers = curl_slist_append(dav_headers, if_header);\n+\n+\t\tslot = get_active_slot();\n+\t\tslot->results = &results;\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.posn);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_PUT, 1);\n+\t\tcurl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);\n+\t\t\t\t\n+\t\tbuffer.posn = 0;\n+\n+\t\tif (start_active_slot(slot)) {\n+\t\t\trun_active_slot(slot);\n+\t\t\tif (results.curl_result != CURLE_OK) {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"PUT error: curl result=%d, HTTP code=%ld\\n\",\n+\t\t\t\t\tresults.curl_result, results.http_code);\n+\t\t\t}\n+\t\t}\n+\t\tfree(if_header);\n+\t}\n+\tfree(buffer.buffer);\n+}\n+\n+static int remote_exists(const char *path)\n+{\n+\tchar *url = xmalloc(strlen(remote->url) + strlen(path) + 1);\n+\tstruct active_request_slot *slot;\n+\tstruct slot_results results;\n+\n+\tsprintf(url, \"%s%s\", remote->url, path);\n+\n+        slot = get_active_slot();\n+\tslot->results = &results;\n+        curl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);\n+\n+        if (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tif (results.http_code == 404)\n+\t\t\treturn 0;\n+\t\telse if (results.curl_result == CURLE_OK)\n+\t\t\treturn 1;\n+\t\telse\n+\t\t\tfprintf(stderr, \"HEAD HTTP error %ld\\n\", results.http_code);\n+\t} else {\n+\t\tfprintf(stderr, \"Unable to start HEAD request\\n\");\n+\t}\n+\n+\treturn -1;\n+}\n+\n int main(int argc, char **argv)\n {\n \tstruct transfer_request *request;\n@@ -1603,6 +2104,7 @@ int main(int argc, char **argv)\n \tint nr_refspec = 0;\n \tchar **refspec = NULL;\n \tstruct remote_lock *ref_lock = NULL;\n+\tstruct remote_lock *info_ref_lock = NULL;\n \tstruct rev_info revs;\n \tint rc = 0;\n \tint i;\n@@ -1667,6 +2169,18 @@ int main(int argc, char **argv)\n \t\tgoto cleanup;\n \t}\n \n+\t/* Check whether the remote has server info files */\n+\tremote->can_update_info_refs = 0;\n+\tremote->has_info_refs = remote_exists(\"info/refs\");\n+\tremote->has_info_packs = remote_exists(\"objects/info/packs\");\n+\tif (remote->has_info_refs) {\n+\t\tinfo_ref_lock = lock_remote(\"info/refs\", LOCK_TIME);\n+\t\tif (info_ref_lock)\n+\t\t\tremote->can_update_info_refs = 1;\n+\t}\n+\tif (remote->has_info_packs)\n+\t\tfetch_indices();\n+\n \t/* Get a list of all local and remote heads to validate refspecs */\n \tget_local_heads();\n \tfprintf(stderr, \"Fetching remote heads...\\n\");\n@@ -1769,7 +2283,6 @@ int main(int argc, char **argv)\n \t\tpushing = 0;\n \t\tprepare_revision_walk(&revs);\n \t\tmark_edges_uninteresting(revs.commits);\n-\t\tfetch_indices();\n \t\tget_delta(&revs, ref_lock);\n \t\tfinish_all_active_slots();\n \n@@ -1792,6 +2305,18 @@ int main(int argc, char **argv)\n \t\tcheck_locks();\n \t}\n \n+\t/* Update remote server info if appropriate */\n+\tif (remote->has_info_refs && new_refs) {\n+\t\tif (info_ref_lock && remote->can_update_info_refs) {\n+\t\t\tfprintf(stderr, \"Updating remote server info\\n\");\n+\t\t\tupdate_remote_info_refs(info_ref_lock);\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"Unable to update server info\\n\");\n+\t\t}\n+\t}\n+\tif (info_ref_lock)\n+\t\tunlock_remote(info_ref_lock);\n+\n  cleanup:\n \tfree(remote);\n \n-- \n1.2.4.g8e81-dirty\n"}]}