Make git-branch a builtin
[gitweb.git] / http-fetch.c
index dc286b79f614a1e2c9ef2c21c2cc198bab973843..bc74f30f76fe0200f6c7ecc215ee1cd9211670f4 100644 (file)
@@ -36,21 +36,23 @@ enum XML_Status {
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
 
+static int commits_on_stdin;
+
 static int got_alternates = -1;
-static int corrupt_object_found = 0;
+static int corrupt_object_found;
 
 static struct curl_slist *no_pragma_header;
 
 struct alt_base
 {
-       char *base;
+       const char *base;
        int path_len;
        int got_indices;
        struct packed_git *packs;
        struct alt_base *next;
 };
 
-static struct alt_base *alt = NULL;
+static struct alt_base *alt;
 
 enum object_request_state {
        WAITING,
@@ -81,7 +83,7 @@ struct object_request
 };
 
 struct alternates_request {
-       char *base;
+       const char *base;
        char *url;
        struct buffer *buffer;
        struct active_request_slot *slot;
@@ -112,7 +114,7 @@ struct remote_ls_ctx
 };
 #endif
 
-static struct object_request *object_queue_head = NULL;
+static struct object_request *object_queue_head;
 
 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
                               void *data)
@@ -142,7 +144,20 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        return size;
 }
 
-static void fetch_alternates(char *base);
+static int missing__target(int code, int result)
+{
+       return  /* file:// URL -- do we ever use one??? */
+               (result == CURLE_FILE_COULDNT_READ_FILE) ||
+               /* http:// and https:// URL */
+               (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
+               /* ftp:// URL */
+               (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
+               ;
+}
+
+#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
+
+static void fetch_alternates(const char *base);
 
 static void process_object_response(void *callback_data);
 
@@ -299,7 +314,7 @@ static void finish_object_request(struct object_request *obj_req)
                unlink(obj_req->tmpfile);
                return;
        }
-       if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
+       if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
                unlink(obj_req->tmpfile);
                return;
        }
@@ -321,8 +336,7 @@ static void process_object_response(void *callback_data)
        obj_req->state = COMPLETE;
 
        /* Use alternates if necessary */
-       if (obj_req->http_code == 404 ||
-           obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) {
+       if (missing_target(obj_req)) {
                fetch_alternates(alt->base);
                if (obj_req->repo->next != NULL) {
                        obj_req->repo =
@@ -391,7 +405,7 @@ void prefetch(unsigned char *sha1)
        char *filename = sha1_file_name(sha1);
 
        newreq = xmalloc(sizeof(*newreq));
-       memcpy(newreq->sha1, sha1, 20);
+       hashcpy(newreq->sha1, sha1);
        newreq->repo = alt;
        newreq->url = NULL;
        newreq->local = -1;
@@ -507,7 +521,7 @@ static void process_alternates_response(void *callback_data)
                (struct alternates_request *)callback_data;
        struct active_request_slot *slot = alt_req->slot;
        struct alt_base *tail = alt;
-       char *base = alt_req->base;
+       const char *base = alt_req->base;
        static const char null_byte = '\0';
        char *data;
        int i = 0;
@@ -535,8 +549,7 @@ static void process_alternates_response(void *callback_data)
                        return;
                }
        } else if (slot->curl_result != CURLE_OK) {
-               if (slot->http_code != 404 &&
-                   slot->curl_result != CURLE_FILE_COULDNT_READ_FILE) {
+               if (!missing_target(slot)) {
                        got_alternates = -1;
                        return;
                }
@@ -557,9 +570,36 @@ static void process_alternates_response(void *callback_data)
                        char *target = NULL;
                        char *path;
                        if (data[i] == '/') {
-                               serverlen = strchr(base + 8, '/') - base;
-                               okay = 1;
+                               /* This counts
+                                * http://git.host/pub/scm/linux.git/
+                                * -----------here^
+                                * so memcpy(dst, base, serverlen) will
+                                * copy up to "...git.host".
+                                */
+                               const char *colon_ss = strstr(base,"://");
+                               if (colon_ss) {
+                                       serverlen = (strchr(colon_ss + 3, '/')
+                                                    - base);
+                                       okay = 1;
+                               }
                        } else if (!memcmp(data + i, "../", 3)) {
+                               /* Relative URL; chop the corresponding
+                                * number of subpath from base (and ../
+                                * from data), and concatenate the result.
+                                *
+                                * The code first drops ../ from data, and
+                                * then drops one ../ from data and one path
+                                * from base.  IOW, one extra ../ is dropped
+                                * from data than path is dropped from base.
+                                *
+                                * This is not wrong.  The alternate in
+                                *     http://git.host/pub/scm/linux.git/
+                                * to borrow from
+                                *     http://git.host/pub/scm/linus.git/
+                                * is ../../linus.git/objects/.  You need
+                                * two ../../ to borrow from your direct
+                                * neighbour.
+                                */
                                i += 3;
                                serverlen = strlen(base);
                                while (i + 2 < posn &&
@@ -581,11 +621,13 @@ static void process_alternates_response(void *callback_data)
                                        okay = 1;
                                }
                        }
-                       /* skip 'objects' at end */
+                       /* skip "objects\n" at end */
                        if (okay) {
                                target = xmalloc(serverlen + posn - i - 6);
-                               strlcpy(target, base, serverlen);
-                               strlcpy(target + serverlen, data + i, posn - i - 6);
+                               memcpy(target, base, serverlen);
+                               memcpy(target + serverlen, data + i,
+                                      posn - i - 7);
+                               target[serverlen + posn - i - 7] = 0;
                                if (get_verbosely)
                                        fprintf(stderr,
                                                "Also look at %s\n", target);
@@ -612,7 +654,7 @@ static void process_alternates_response(void *callback_data)
        got_alternates = 1;
 }
 
-static void fetch_alternates(char *base)
+static void fetch_alternates(const char *base)
 {
        struct buffer buffer;
        char *url;
@@ -694,10 +736,8 @@ xml_start_tag(void *userData, const char *name, const char **atts)
        strcat(ctx->name, ".");
        strcat(ctx->name, c);
 
-       if (ctx->cdata) {
-               free(ctx->cdata);
-               ctx->cdata = NULL;
-       }
+       free(ctx->cdata);
+       ctx->cdata = NULL;
 
        ctx->userFunc(ctx, 0);
 }
@@ -724,8 +764,7 @@ static void
 xml_cdata(void *userData, const XML_Char *s, int len)
 {
        struct xml_ctx *ctx = (struct xml_ctx *)userData;
-       if (ctx->cdata)
-               free(ctx->cdata);
+       free(ctx->cdata);
        ctx->cdata = xmalloc(len + 1);
        strlcpy(ctx->cdata, s, len + 1);
 }
@@ -763,9 +802,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                        ls->dentry_flags |= IS_DIR;
                }
        } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
-               if (ls->dentry_name) {
-                       free(ls->dentry_name);
-               }
+               free(ls->dentry_name);
                ls->dentry_name = NULL;
                ls->dentry_flags = 0;
        }
@@ -790,7 +827,7 @@ static int remote_ls(struct alt_base *repo, const char *path, int flags,
 
        ls.flags = flags;
        ls.repo = repo;
-       ls.path = strdup(path);
+       ls.path = xstrdup(path);
        ls.dentry_name = NULL;
        ls.dentry_flags = 0;
        ls.userData = userData;
@@ -868,7 +905,7 @@ static void process_ls_pack(struct remote_ls_ctx *ls)
 
        if (strlen(ls->dentry_name) == 63 &&
            !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
-           !strncmp(ls->dentry_name+58, ".pack", 5)) {
+           has_extension(ls->dentry_name, ".pack")) {
                get_sha1_hex(ls->dentry_name + 18, sha1);
                setup_index(ls->repo, sha1);
        }
@@ -915,8 +952,7 @@ static int fetch_indices(struct alt_base *repo)
        if (start_active_slot(slot)) {
                run_active_slot(slot);
                if (results.curl_result != CURLE_OK) {
-                       if (results.http_code == 404 ||
-                           results.curl_result == CURLE_FILE_COULDNT_READ_FILE) {
+                       if (missing_target(&results)) {
                                repo->got_indices = 1;
                                free(buffer.buffer);
                                return 0;
@@ -1068,7 +1104,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
        int ret = 0;
        struct object_request *obj_req = object_queue_head;
 
-       while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20))
+       while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
                obj_req = obj_req->next;
        if (obj_req == NULL)
                return error("Couldn't find request for %s in the queue", hex);
@@ -1097,8 +1133,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
                ret = error("Request for %s aborted", hex);
        } else if (obj_req->curl_result != CURLE_OK &&
                   obj_req->http_code != 416) {
-               if (obj_req->http_code == 404 ||
-                   obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE)
+               if (missing_target(obj_req))
                        ret = -1; /* Be silent, it is probably in a pack. */
                else
                        ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
@@ -1107,7 +1142,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
        } else if (obj_req->zret != Z_STREAM_END) {
                corrupt_object_found++;
                ret = error("File %s (%s) corrupt", hex, obj_req->url);
-       } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
+       } else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
                ret = error("File %s has bad hash", hex);
        } else if (obj_req->rename < 0) {
                ret = error("unable to write sha1 filename %s",
@@ -1185,7 +1220,7 @@ int fetch_ref(char *ref, unsigned char *sha1)
         char *url;
         char hex[42];
         struct buffer buffer;
-       char *base = alt->base;
+       const char *base = alt->base;
        struct active_request_slot *slot;
        struct slot_results results;
         buffer.size = 41;
@@ -1214,14 +1249,17 @@ int fetch_ref(char *ref, unsigned char *sha1)
         return 0;
 }
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-       char *commit_id;
-       char *url;
+       int commits;
+       const char **write_ref = NULL;
+       char **commit_id;
+       const char *url;
        char *path;
        int arg = 1;
        int rc = 0;
 
+       setup_ident();
        setup_git_directory();
        git_config(git_default_config);
 
@@ -1237,20 +1275,26 @@ int main(int argc, char **argv)
                } else if (argv[arg][1] == 'v') {
                        get_verbosely = 1;
                } else if (argv[arg][1] == 'w') {
-                       write_ref = argv[arg + 1];
+                       write_ref = &argv[arg + 1];
                        arg++;
                } else if (!strcmp(argv[arg], "--recover")) {
                        get_recover = 1;
+               } else if (!strcmp(argv[arg], "--stdin")) {
+                       commits_on_stdin = 1;
                }
                arg++;
        }
-       if (argc < arg + 2) {
-               usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] commit-id url");
+       if (argc < arg + 2 - commits_on_stdin) {
+               usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
                return 1;
        }
-       commit_id = argv[arg];
-       url = argv[arg + 1];
-       write_ref_log_details = url;
+       if (commits_on_stdin) {
+               commits = pull_targets_stdin(&commit_id, &write_ref);
+       } else {
+               commit_id = (char **) &argv[arg++];
+               commits = 1;
+       }
+       url = argv[arg];
 
        http_init();
 
@@ -1268,13 +1312,16 @@ int main(int argc, char **argv)
                        alt->path_len = strlen(path);
        }
 
-       if (pull(commit_id))
+       if (pull(commits, commit_id, write_ref, url))
                rc = 1;
 
        http_cleanup();
 
        curl_slist_free_all(no_pragma_header);
 
+       if (commits_on_stdin)
+               pull_targets_free(commits, commit_id, write_ref);
+
        if (corrupt_object_found) {
                fprintf(stderr,
 "Some loose object were found to be corrupt, but they might be just\n"