http-push: improve remote lock management
[git.git] / http-push.c
index 226d719..553e1e1 100644 (file)
@@ -80,10 +80,10 @@ struct repo
        char *url;
        int path_len;
        struct packed_git *packs;
+       struct remote_lock *locks;
 };
 
 static struct repo *remote = NULL;
-static struct remote_lock *remote_locks = NULL;
 
 enum transfer_state {
        NEED_PUSH,
@@ -135,16 +135,27 @@ struct remote_lock
        char *token;
        time_t start_time;
        long timeout;
-       int active;
        int refreshing;
        struct remote_lock *next;
 };
 
-struct remote_dentry
+/* Flags that control remote_ls processing */
+#define PROCESS_FILES (1u << 0)
+#define PROCESS_DIRS  (1u << 1)
+#define RECURSIVE     (1u << 2)
+
+/* Flags that remote_ls passes to callback functions */
+#define IS_DIR (1u << 0)
+
+struct remote_ls_ctx
 {
-       char *base;
-       char *name;
-       int is_dir;
+       char *path;
+       void (*userFunc)(struct remote_ls_ctx *ls);
+       void *userData;
+       int flags;
+       char *dentry_name;
+       int dentry_flags;
+       struct remote_ls_ctx *parent;
 };
 
 static void finish_request(struct transfer_request *request);
@@ -299,62 +310,69 @@ static void start_move(struct transfer_request *request)
        }
 }
 
-static int refresh_lock(struct remote_lock *check_lock)
+static int refresh_lock(struct remote_lock *lock)
 {
        struct active_request_slot *slot;
+       struct slot_results results;
        char *if_header;
        char timeout_header[25];
        struct curl_slist *dav_headers = NULL;
-       struct remote_lock *lock;
-       int time_remaining;
-       time_t current_time;
+       int rc = 0;
 
-       /* Refresh all active locks if they're close to expiring */
-       for (lock = remote_locks; lock; lock = lock->next) {
-               if (!lock->active)
-                       continue;
+       lock->refreshing = 1;
 
-               current_time = time(NULL);
-               time_remaining = lock->start_time + lock->timeout
-                       - current_time;
-               if (time_remaining > LOCK_REFRESH)
-                       continue;
+       if_header = xmalloc(strlen(lock->token) + 25);
+       sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
+       sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
+       dav_headers = curl_slist_append(dav_headers, if_header);
+       dav_headers = curl_slist_append(dav_headers, timeout_header);
+
+       slot = get_active_slot();
+       slot->results = &results;
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
+       curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+       if (start_active_slot(slot)) {
+               run_active_slot(slot);
+               if (results.curl_result != CURLE_OK) {
+                       fprintf(stderr, "LOCK HTTP error %ld\n",
+                               results.http_code);
+               } else {
+                       lock->start_time = time(NULL);
+                       rc = 1;
+               }
+       }
 
-               lock->refreshing = 1;
+       lock->refreshing = 0;
+       curl_slist_free_all(dav_headers);
+       free(if_header);
 
-               if_header = xmalloc(strlen(lock->token) + 25);
-               sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
-               sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
-               dav_headers = curl_slist_append(dav_headers, if_header);
-               dav_headers = curl_slist_append(dav_headers, timeout_header);
+       return rc;
+}
 
-               slot = get_active_slot();
-               curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
-               curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-               curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
-               curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
-               curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+static void check_locks()
+{
+       struct remote_lock *lock = remote->locks;
+       time_t current_time = time(NULL);
+       int time_remaining;
 
-               if (start_active_slot(slot)) {
-                       run_active_slot(slot);
-                       if (slot->curl_result != CURLE_OK) {
-                               fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
-                               lock->active = 0;
-                       } else {
-                               lock->active = 1;
-                               lock->start_time = time(NULL);
+       while (lock) {
+               time_remaining = lock->start_time + lock->timeout -
+                       current_time;
+               if (!lock->refreshing && time_remaining < LOCK_REFRESH) {
+                       if (!refresh_lock(lock)) {
+                               fprintf(stderr,
+                                       "Unable to refresh lock for %s\n",
+                                       lock->url);
+                               aborted = 1;
+                               return;
                        }
                }
-
-               lock->refreshing = 0;
-               curl_slist_free_all(dav_headers);
-               free(if_header);
+               lock = lock->next;
        }
-
-       if (check_lock)
-               return check_lock->active;
-       else
-               return 0;
 }
 
 static void release_request(struct transfer_request *request)
@@ -382,7 +400,7 @@ static void finish_request(struct transfer_request *request)
        request->slot = NULL;
 
        /* Keep locks active */
-       refresh_lock(request->lock);
+       check_locks();
 
        if (request->headers != NULL)
                curl_slist_free_all(request->headers);
@@ -469,6 +487,9 @@ static void add_request(struct object *obj, struct remote_lock *lock)
        struct transfer_request *request = request_queue_head;
        struct packed_git *target;
 
+       /* Keep locks active */
+       check_locks();
+
        /*
         * Don't push the object if it's known to exist on the remote
         * or is already in the request queue
@@ -509,16 +530,18 @@ static int fetch_index(unsigned char *sha1)
 
        FILE *indexfile;
        struct active_request_slot *slot;
+       struct slot_results results;
 
        /* Don't use the index if the pack isn't there */
        url = xmalloc(strlen(remote->url) + 65);
        sprintf(url, "%s/objects/pack/pack-%s.pack", remote->url, hex);
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result != CURLE_OK) {
+               if (results.curl_result != CURLE_OK) {
                        free(url);
                        return error("Unable to verify pack %s is available",
                                     hex);
@@ -543,6 +566,7 @@ static int fetch_index(unsigned char *sha1)
                             filename);
 
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
@@ -566,7 +590,7 @@ static int fetch_index(unsigned char *sha1)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result != CURLE_OK) {
+               if (results.curl_result != CURLE_OK) {
                        free(url);
                        fclose(indexfile);
                        return error("Unable to get pack index %s\n%s", url,
@@ -606,6 +630,7 @@ static int fetch_indices(void)
        int i = 0;
 
        struct active_request_slot *slot;
+       struct slot_results results;
 
        data = xmalloc(4096);
        memset(data, 0, 4096);
@@ -620,16 +645,17 @@ static int fetch_indices(void)
        sprintf(url, "%s/objects/info/packs", remote->url);
 
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result != CURLE_OK) {
+               if (results.curl_result != CURLE_OK) {
                        free(buffer.buffer);
                        free(url);
-                       if (slot->http_code == 404)
+                       if (results.http_code == 404)
                                return 0;
                        else
                                return error("%s", curl_errorstr);
@@ -716,20 +742,22 @@ int fetch_ref(char *ref, unsigned char *sha1)
         struct buffer buffer;
        char *base = remote->url;
        struct active_request_slot *slot;
+       struct slot_results results;
         buffer.size = 41;
         buffer.posn = 0;
         buffer.buffer = hex;
         hex[41] = '\0';
-        
+
        url = quote_ref_url(base, ref);
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result != CURLE_OK)
+               if (results.curl_result != CURLE_OK)
                        return error("Couldn't get %s for %s\n%s",
                                     url, ref, curl_errorstr);
        } else {
@@ -803,55 +831,6 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 }
 
 static void one_remote_ref(char *refname);
-static void crawl_remote_refs(char *path);
-
-static void handle_crawl_ref_ctx(struct xml_ctx *ctx, int tag_closed)
-{
-       struct remote_dentry *dentry = (struct remote_dentry *)ctx->userData;
-
-
-       if (tag_closed) {
-               if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && dentry->name) {
-                       if (dentry->is_dir) {
-                               if (strcmp(dentry->name, dentry->base)) {
-                                       crawl_remote_refs(dentry->name);
-                               }
-                       } else {
-                               one_remote_ref(dentry->name);
-                       }
-               } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
-                       dentry->name = xmalloc(strlen(ctx->cdata) -
-                                              remote->path_len + 1);
-                       strcpy(dentry->name,
-                              ctx->cdata + remote->path_len);
-               } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
-                       dentry->is_dir = 1;
-               }
-       } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
-               dentry->name = NULL;
-               dentry->is_dir = 0;
-       }
-}
-
-static void handle_remote_object_list_ctx(struct xml_ctx *ctx, int tag_closed)
-{
-       char *path;
-       char *obj_hex;
-
-       if (tag_closed) {
-               if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
-                       path = ctx->cdata + remote->path_len;
-                       if (strlen(path) != 50)
-                               return;
-                       path += 9;
-                       obj_hex = xmalloc(strlen(path));
-                       strncpy(obj_hex, path, 2);
-                       strcpy(obj_hex + 2, path + 3);
-                       one_remote_object(obj_hex);
-                       free(obj_hex);
-               }
-       }
-}
 
 static void
 xml_start_tag(void *userData, const char *name, const char **atts)
@@ -913,6 +892,7 @@ xml_cdata(void *userData, const XML_Char *s, int len)
 static struct remote_lock *lock_remote(char *path, long timeout)
 {
        struct active_request_slot *slot;
+       struct slot_results results;
        struct buffer out_buffer;
        struct buffer in_buffer;
        char *out_data;
@@ -920,7 +900,7 @@ static struct remote_lock *lock_remote(char *path, long timeout)
        char *url;
        char *ep;
        char timeout_header[25];
-       struct remote_lock *lock = remote_locks;
+       struct remote_lock *lock = NULL;
        XML_Parser parser = XML_ParserCreate(NULL);
        enum XML_Status result;
        struct curl_slist *dav_headers = NULL;
@@ -929,31 +909,20 @@ static struct remote_lock *lock_remote(char *path, long timeout)
        url = xmalloc(strlen(remote->url) + strlen(path) + 1);
        sprintf(url, "%s%s", remote->url, path);
 
-       /* Make sure the url is not already locked */
-       while (lock && strcmp(lock->url, url)) {
-               lock = lock->next;
-       }
-       if (lock) {
-               free(url);
-               if (refresh_lock(lock))
-                       return lock;
-               else
-                       return NULL;
-       }
-
        /* Make sure leading directories exist for the remote ref */
        ep = strchr(url + strlen(remote->url) + 11, '/');
        while (ep) {
                *ep = 0;
                slot = get_active_slot();
+               slot->results = &results;
                curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
                curl_easy_setopt(slot->curl, CURLOPT_URL, url);
                curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
                curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
                if (start_active_slot(slot)) {
                        run_active_slot(slot);
-                       if (slot->curl_result != CURLE_OK &&
-                           slot->http_code != 405) {
+                       if (results.curl_result != CURLE_OK &&
+                           results.http_code != 405) {
                                fprintf(stderr,
                                        "Unable to create branch path %s\n",
                                        url);
@@ -985,6 +954,7 @@ static struct remote_lock *lock_remote(char *path, long timeout)
        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
 
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
@@ -996,14 +966,11 @@ static struct remote_lock *lock_remote(char *path, long timeout)
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
 
        lock = xcalloc(1, sizeof(*lock));
-       lock->owner = NULL;
-       lock->token = NULL;
        lock->timeout = -1;
-       lock->refreshing = 0;
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result == CURLE_OK) {
+               if (results.curl_result == CURLE_OK) {
                        ctx.name = xcalloc(10, 1);
                        ctx.len = 0;
                        ctx.cdata = NULL;
@@ -1041,10 +1008,9 @@ static struct remote_lock *lock_remote(char *path, long timeout)
                lock = NULL;
        } else {
                lock->url = url;
-               lock->active = 1;
                lock->start_time = time(NULL);
-               lock->next = remote_locks;
-               remote_locks = lock;
+               lock->next = remote->locks;
+               remote->locks = lock;
        }
 
        return lock;
@@ -1053,6 +1019,8 @@ static struct remote_lock *lock_remote(char *path, long timeout)
 static int unlock_remote(struct remote_lock *lock)
 {
        struct active_request_slot *slot;
+       struct slot_results results;
+       struct remote_lock *prev = remote->locks;
        char *lock_token_header;
        struct curl_slist *dav_headers = NULL;
        int rc = 0;
@@ -1063,6 +1031,7 @@ static int unlock_remote(struct remote_lock *lock)
        dav_headers = curl_slist_append(dav_headers, lock_token_header);
 
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
        curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);
@@ -1070,107 +1039,115 @@ static int unlock_remote(struct remote_lock *lock)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result == CURLE_OK)
+               if (results.curl_result == CURLE_OK)
                        rc = 1;
                else
-                       fprintf(stderr, "Got HTTP error %ld\n",
-                               slot->http_code);
+                       fprintf(stderr, "UNLOCK HTTP error %ld\n",
+                               results.http_code);
        } else {
-               fprintf(stderr, "Unable to start request\n");
+               fprintf(stderr, "Unable to start UNLOCK request\n");
        }
 
        curl_slist_free_all(dav_headers);
        free(lock_token_header);
 
-       lock->active = 0;
+       if (remote->locks == lock) {
+               remote->locks = lock->next;
+       } else {
+               while (prev && prev->next != lock)
+                       prev = prev->next;
+               if (prev)
+                       prev->next = prev->next->next;
+       }
+
+       if (lock->owner != NULL)
+               free(lock->owner);
+       free(lock->url);
+       free(lock->token);
+       free(lock);
 
        return rc;
 }
 
-static void crawl_remote_refs(char *path)
-{
-       char *url;
-       struct active_request_slot *slot;
-       struct buffer in_buffer;
-       struct buffer out_buffer;
-       char *in_data;
-       char *out_data;
-       XML_Parser parser = XML_ParserCreate(NULL);
-       enum XML_Status result;
-       struct curl_slist *dav_headers = NULL;
-       struct xml_ctx ctx;
-       struct remote_dentry dentry;
-
-       fprintf(stderr, "  %s\n", path);
-
-       dentry.base = path;
-       dentry.name = NULL;
-       dentry.is_dir = 0;
+static void remote_ls(const char *path, int flags,
+                     void (*userFunc)(struct remote_ls_ctx *ls),
+                     void *userData);
 
-       url = xmalloc(strlen(remote->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", remote->url, path);
+static void process_ls_object(struct remote_ls_ctx *ls)
+{
+       unsigned int *parent = (unsigned int *)ls->userData;
+       char *path = ls->dentry_name;
+       char *obj_hex;
 
-       out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
-       out_data = xmalloc(out_buffer.size + 1);
-       snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
-       out_buffer.posn = 0;
-       out_buffer.buffer = out_data;
+       if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {
+               remote_dir_exists[*parent] = 1;
+               return;
+       }
 
-       in_buffer.size = 4096;
-       in_data = xmalloc(in_buffer.size);
-       in_buffer.posn = 0;
-       in_buffer.buffer = in_data;
+       if (strlen(path) != 49)
+               return;
+       path += 8;
+       obj_hex = xmalloc(strlen(path));
+       strncpy(obj_hex, path, 2);
+       strcpy(obj_hex + 2, path + 3);
+       one_remote_object(obj_hex);
+       free(obj_hex);
+}
 
-       dav_headers = curl_slist_append(dav_headers, "Depth: 1");
-       dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+static void process_ls_ref(struct remote_ls_ctx *ls)
+{
+       if (!strcmp(ls->path, ls->dentry_name) && (ls->dentry_flags & IS_DIR)) {
+               fprintf(stderr, "  %s\n", ls->dentry_name);
+               return;
+       }
 
-       slot = get_active_slot();
-       curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
-       curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-       curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+       if (!(ls->dentry_flags & IS_DIR))
+               one_remote_ref(ls->dentry_name);
+}
 
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (slot->curl_result == CURLE_OK) {
-                       ctx.name = xcalloc(10, 1);
-                       ctx.len = 0;
-                       ctx.cdata = NULL;
-                       ctx.userFunc = handle_crawl_ref_ctx;
-                       ctx.userData = &dentry;
-                       XML_SetUserData(parser, &ctx);
-                       XML_SetElementHandler(parser, xml_start_tag,
-                                             xml_end_tag);
-                       XML_SetCharacterDataHandler(parser, xml_cdata);
-                       result = XML_Parse(parser, in_buffer.buffer,
-                                          in_buffer.posn, 1);
-                       free(ctx.name);
+static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
+{
+       struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
 
-                       if (result != XML_STATUS_OK) {
-                               fprintf(stderr, "XML error: %s\n",
-                                       XML_ErrorString(
-                                               XML_GetErrorCode(parser)));
+       if (tag_closed) {
+               if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
+                       if (ls->dentry_flags & IS_DIR) {
+                               if (ls->flags & PROCESS_DIRS) {
+                                       ls->userFunc(ls);
+                               }
+                               if (strcmp(ls->dentry_name, ls->path) &&
+                                   ls->flags & RECURSIVE) {
+                                       remote_ls(ls->dentry_name,
+                                                 ls->flags,
+                                                 ls->userFunc,
+                                                 ls->userData);
+                               }
+                       } else if (ls->flags & PROCESS_FILES) {
+                               ls->userFunc(ls);
                        }
+               } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
+                       ls->dentry_name = xmalloc(strlen(ctx->cdata) -
+                                                 remote->path_len + 1);
+                       strcpy(ls->dentry_name, ctx->cdata + remote->path_len);
+               } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
+                       ls->dentry_flags |= IS_DIR;
                }
-       } else {
-               fprintf(stderr, "Unable to start request\n");
+       } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
+               if (ls->dentry_name) {
+                       free(ls->dentry_name);
+               }
+               ls->dentry_name = NULL;
+               ls->dentry_flags = 0;
        }
-
-       free(url);
-       free(out_data);
-       free(in_buffer.buffer);
-       curl_slist_free_all(dav_headers);
 }
 
-static void get_remote_object_list(unsigned char parent)
+static void remote_ls(const char *path, int flags,
+                     void (*userFunc)(struct remote_ls_ctx *ls),
+                     void *userData)
 {
-       char *url;
+       char *url = xmalloc(strlen(remote->url) + strlen(path) + 1);
        struct active_request_slot *slot;
+       struct slot_results results;
        struct buffer in_buffer;
        struct buffer out_buffer;
        char *in_data;
@@ -1179,13 +1156,15 @@ static void get_remote_object_list(unsigned char parent)
        enum XML_Status result;
        struct curl_slist *dav_headers = NULL;
        struct xml_ctx ctx;
-       char path[] = "/objects/XX/";
-       static const char hex[] = "0123456789abcdef";
-       unsigned int val = parent;
+       struct remote_ls_ctx ls;
+
+       ls.flags = flags;
+       ls.path = strdup(path);
+       ls.dentry_name = NULL;
+       ls.dentry_flags = 0;
+       ls.userData = userData;
+       ls.userFunc = userFunc;
 
-       path[9] = hex[val >> 4];
-       path[10] = hex[val & 0xf];
-       url = xmalloc(strlen(remote->url) + strlen(path) + 1);
        sprintf(url, "%s%s", remote->url, path);
 
        out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
@@ -1203,6 +1182,7 @@ static void get_remote_object_list(unsigned char parent)
        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
 
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
@@ -1215,12 +1195,12 @@ static void get_remote_object_list(unsigned char parent)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result == CURLE_OK) {
-                       remote_dir_exists[parent] = 1;
+               if (results.curl_result == CURLE_OK) {
                        ctx.name = xcalloc(10, 1);
                        ctx.len = 0;
                        ctx.cdata = NULL;
-                       ctx.userFunc = handle_remote_object_list_ctx;
+                       ctx.userFunc = handle_remote_ls_ctx;
+                       ctx.userData = &ls;
                        XML_SetUserData(parser, &ctx);
                        XML_SetElementHandler(parser, xml_start_tag,
                                              xml_end_tag);
@@ -1234,22 +1214,35 @@ static void get_remote_object_list(unsigned char parent)
                                        XML_ErrorString(
                                                XML_GetErrorCode(parser)));
                        }
-               } else {
-                       remote_dir_exists[parent] = 0;
                }
        } else {
-               fprintf(stderr, "Unable to start request\n");
+               fprintf(stderr, "Unable to start PROPFIND request\n");
        }
 
+       free(ls.path);
        free(url);
        free(out_data);
        free(in_buffer.buffer);
        curl_slist_free_all(dav_headers);
 }
 
+static void get_remote_object_list(unsigned char parent)
+{
+       char path[] = "objects/XX/";
+       static const char hex[] = "0123456789abcdef";
+       unsigned int val = parent;
+
+       path[8] = hex[val >> 4];
+       path[9] = hex[val & 0xf];
+       remote_dir_exists[val] = 0;
+       remote_ls(path, (PROCESS_FILES | PROCESS_DIRS),
+                 process_ls_object, &val);
+}
+
 static int locking_available(void)
 {
        struct active_request_slot *slot;
+       struct slot_results results;
        struct buffer in_buffer;
        struct buffer out_buffer;
        char *in_data;
@@ -1276,8 +1269,9 @@ static int locking_available(void)
 
        dav_headers = curl_slist_append(dav_headers, "Depth: 0");
        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
-       
+
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
@@ -1290,7 +1284,7 @@ static int locking_available(void)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (slot->curl_result == CURLE_OK) {
+               if (results.curl_result == CURLE_OK) {
                        ctx.name = xcalloc(10, 1);
                        ctx.len = 0;
                        ctx.cdata = NULL;
@@ -1416,6 +1410,7 @@ static void get_delta(struct rev_info *revs, struct remote_lock *lock)
 static int update_remote(unsigned char *sha1, struct remote_lock *lock)
 {
        struct active_request_slot *slot;
+       struct slot_results results;
        char *out_data;
        char *if_header;
        struct buffer out_buffer;
@@ -1437,6 +1432,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
        out_buffer.buffer = out_data;
 
        slot = get_active_slot();
+       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
@@ -1451,10 +1447,10 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
                run_active_slot(slot);
                free(out_data);
                free(if_header);
-               if (slot->curl_result != CURLE_OK) {
+               if (results.curl_result != CURLE_OK) {
                        fprintf(stderr,
                                "PUT error: curl result=%d, HTTP code=%ld\n",
-                               slot->curl_result, slot->http_code);
+                               results.curl_result, results.http_code);
                        /* We should attempt recovery? */
                        return 0;
                }
@@ -1512,7 +1508,7 @@ static void get_local_heads(void)
 static void get_dav_remote_heads(void)
 {
        remote_tail = &remote_refs;
-       crawl_remote_refs("refs/");
+       remote_ls("refs/", (PROCESS_FILES | PROCESS_DIRS | RECURSIVE), process_ls_ref, NULL);
 }
 
 static int is_zero_sha1(const unsigned char *sha1)
@@ -1606,7 +1602,7 @@ int main(int argc, char **argv)
        struct transfer_request *next_request;
        int nr_refspec = 0;
        char **refspec = NULL;
-       struct remote_lock *ref_lock;
+       struct remote_lock *ref_lock = NULL;
        struct rev_info revs;
        int rc = 0;
        int i;
@@ -1614,10 +1610,7 @@ int main(int argc, char **argv)
        setup_git_directory();
        setup_ident();
 
-       remote = xmalloc(sizeof(*remote));
-       remote->url = NULL;
-       remote->path_len = 0;
-       remote->packs = NULL;
+       remote = xcalloc(sizeof(*remote), 1);
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
@@ -1752,23 +1745,20 @@ int main(int argc, char **argv)
                }
 
                /* Set up revision info for this refspec */
-               const char *commit_argv[3];
-               int commit_argc = 2;
+               const char *commit_argv[4];
+               int commit_argc = 3;
                char *new_sha1_hex = strdup(sha1_to_hex(ref->new_sha1));
                char *old_sha1_hex = NULL;
-               commit_argv[1] = new_sha1_hex;
+               commit_argv[1] = "--objects";
+               commit_argv[2] = new_sha1_hex;
                if (!push_all && !is_zero_sha1(ref->old_sha1)) {
                        old_sha1_hex = xmalloc(42);
                        sprintf(old_sha1_hex, "^%s",
                                sha1_to_hex(ref->old_sha1));
-                       commit_argv[2] = old_sha1_hex;
+                       commit_argv[3] = old_sha1_hex;
                        commit_argc++;
                }
-               revs.commits = NULL;
                setup_revisions(commit_argc, commit_argv, &revs, NULL);
-               revs.tag_objects = 1;
-               revs.tree_objects = 1;
-               revs.blob_objects = 1;
                free(new_sha1_hex);
                if (old_sha1_hex) {
                        free(old_sha1_hex);
@@ -1799,6 +1789,7 @@ int main(int argc, char **argv)
                if (!rc)
                        fprintf(stderr, "    done\n");
                unlock_remote(ref_lock);
+               check_locks();
        }
 
  cleanup: