]> git.ipfire.org Git - thirdparty/git.git/blobdiff - fetch-pack.c
The sixth batch
[thirdparty/git.git] / fetch-pack.c
index 7eaa19d7c17abeb4d2975d112a79762507a81009..acd55ba6e82a860eeb973d9d255c8d7efc13bddc 100644 (file)
@@ -38,6 +38,7 @@ static int server_supports_filtering;
 static struct shallow_lock shallow_lock;
 static const char *alternate_shallow_file;
 static struct strbuf fsck_msg_types = STRBUF_INIT;
+static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
 
 /* Remember to update object flag allocation in object.h */
 #define COMPLETE       (1U << 0)
@@ -794,7 +795,8 @@ static void write_promisor_file(const char *keep_name,
 }
 
 static int get_pack(struct fetch_pack_args *args,
-                   int xd[2], char **pack_lockfile,
+                   int xd[2], struct string_list *pack_lockfiles,
+                   int only_packfile,
                    struct ref **sought, int nr_sought)
 {
        struct async demux;
@@ -838,7 +840,7 @@ static int get_pack(struct fetch_pack_args *args,
        }
 
        if (do_keep || args->from_promisor) {
-               if (pack_lockfile)
+               if (pack_lockfiles)
                        cmd.out = -1;
                cmd_name = "index-pack";
                argv_array_push(&cmd.args, cmd_name);
@@ -855,15 +857,22 @@ static int get_pack(struct fetch_pack_args *args,
                                        "--keep=fetch-pack %"PRIuMAX " on %s",
                                        (uintmax_t)getpid(), hostname);
                }
-               if (args->check_self_contained_and_connected)
+               if (only_packfile && args->check_self_contained_and_connected)
                        argv_array_push(&cmd.args, "--check-self-contained-and-connected");
+               else
+                       /*
+                        * We cannot perform any connectivity checks because
+                        * not all packs have been downloaded; let the caller
+                        * have this responsibility.
+                        */
+                       args->check_self_contained_and_connected = 0;
                /*
                 * If we're obtaining the filename of a lockfile, we'll use
                 * that filename to write a .promisor file with more
                 * information below. If not, we need index-pack to do it for
                 * us.
                 */
-               if (!(do_keep && pack_lockfile) && args->from_promisor)
+               if (!(do_keep && pack_lockfiles) && args->from_promisor)
                        argv_array_push(&cmd.args, "--promisor");
        }
        else {
@@ -899,8 +908,9 @@ static int get_pack(struct fetch_pack_args *args,
        cmd.git_cmd = 1;
        if (start_command(&cmd))
                die(_("fetch-pack: unable to fork off %s"), cmd_name);
-       if (do_keep && pack_lockfile) {
-               *pack_lockfile = index_pack_lockfile(cmd.out);
+       if (do_keep && pack_lockfiles) {
+               string_list_append_nodup(pack_lockfiles,
+                                        index_pack_lockfile(cmd.out));
                close(cmd.out);
        }
 
@@ -922,8 +932,8 @@ static int get_pack(struct fetch_pack_args *args,
         * Now that index-pack has succeeded, write the promisor file using the
         * obtained .keep filename if necessary
         */
-       if (do_keep && pack_lockfile && args->from_promisor)
-               write_promisor_file(*pack_lockfile, sought, nr_sought);
+       if (do_keep && pack_lockfiles && pack_lockfiles->nr && args->from_promisor)
+               write_promisor_file(pack_lockfiles->items[0].string, sought, nr_sought);
 
        return 0;
 }
@@ -940,7 +950,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                                 const struct ref *orig_ref,
                                 struct ref **sought, int nr_sought,
                                 struct shallow_info *si,
-                                char **pack_lockfile)
+                                struct string_list *pack_lockfiles)
 {
        struct repository *r = the_repository;
        struct ref *ref = copy_ref_list(orig_ref);
@@ -1067,7 +1077,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                alternate_shallow_file = setup_temporary_shallow(si->shallow);
        else
                alternate_shallow_file = NULL;
-       if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
+       if (get_pack(args, fd, pack_lockfiles, 1, sought, nr_sought))
                die(_("git fetch-pack: fetch failed."));
 
  all_done:
@@ -1221,6 +1231,26 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
                warning("filtering not recognized by server, ignoring");
        }
 
+       if (server_supports_feature("fetch", "packfile-uris", 0)) {
+               int i;
+               struct strbuf to_send = STRBUF_INIT;
+
+               for (i = 0; i < uri_protocols.nr; i++) {
+                       const char *s = uri_protocols.items[i].string;
+
+                       if (!strcmp(s, "https") || !strcmp(s, "http")) {
+                               if (to_send.len)
+                                       strbuf_addch(&to_send, ',');
+                               strbuf_addstr(&to_send, s);
+                       }
+               }
+               if (to_send.len) {
+                       packet_buf_write(&req_buf, "packfile-uris %s",
+                                        to_send.buf);
+                       strbuf_release(&to_send);
+               }
+       }
+
        /* add wants */
        add_wants(args->no_dependents, wants, &req_buf);
 
@@ -1443,6 +1473,21 @@ static void receive_wanted_refs(struct packet_reader *reader,
                die(_("error processing wanted refs: %d"), reader->status);
 }
 
+static void receive_packfile_uris(struct packet_reader *reader,
+                                 struct string_list *uris)
+{
+       process_section_header(reader, "packfile-uris", 0);
+       while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+               if (reader->pktlen < the_hash_algo->hexsz ||
+                   reader->line[the_hash_algo->hexsz] != ' ')
+                       die("expected '<hash> <uri>', got: %s\n", reader->line);
+
+               string_list_append(uris, reader->line);
+       }
+       if (reader->status != PACKET_READ_DELIM)
+               die("expected DELIM");
+}
+
 enum fetch_state {
        FETCH_CHECK_LOCAL = 0,
        FETCH_SEND_REQUEST,
@@ -1451,13 +1496,20 @@ enum fetch_state {
        FETCH_DONE,
 };
 
+static void do_check_stateless_delimiter(const struct fetch_pack_args *args,
+                                        struct packet_reader *reader)
+{
+       check_stateless_delimiter(args->stateless_rpc, reader,
+                                 _("git fetch-pack: expected response end packet"));
+}
+
 static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                    int fd[2],
                                    const struct ref *orig_ref,
                                    struct ref **sought, int nr_sought,
                                    struct oid_array *shallows,
                                    struct shallow_info *si,
-                                   char **pack_lockfile)
+                                   struct string_list *pack_lockfiles)
 {
        struct repository *r = the_repository;
        struct ref *ref = copy_ref_list(orig_ref);
@@ -1469,6 +1521,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        struct fetch_negotiator negotiator_alloc;
        struct fetch_negotiator *negotiator;
        int seen_ack = 0;
+       struct string_list packfile_uris = STRING_LIST_INIT_DUP;
+       int i;
 
        if (args->no_dependents) {
                negotiator = NULL;
@@ -1535,6 +1589,10 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                        /* Process ACKs/NAKs */
                        switch (process_acks(negotiator, &reader, &common)) {
                        case READY:
+                               /*
+                                * Don't check for response delimiter; get_pack() will
+                                * read the rest of this response.
+                                */
                                state = FETCH_GET_PACK;
                                break;
                        case COMMON_FOUND:
@@ -1542,6 +1600,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                seen_ack = 1;
                                /* fallthrough */
                        case NO_COMMON_FOUND:
+                               do_check_stateless_delimiter(args, &reader);
                                state = FETCH_SEND_REQUEST;
                                break;
                        }
@@ -1557,10 +1616,14 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                        if (process_section_header(&reader, "wanted-refs", 1))
                                receive_wanted_refs(&reader, sought, nr_sought);
 
-                       /* get the pack */
+                       /* get the pack(s) */
+                       if (process_section_header(&reader, "packfile-uris", 1))
+                               receive_packfile_uris(&reader, &packfile_uris);
                        process_section_header(&reader, "packfile", 0);
-                       if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
+                       if (get_pack(args, fd, pack_lockfiles,
+                                    !packfile_uris.nr, sought, nr_sought))
                                die(_("git fetch-pack: fetch failed."));
+                       do_check_stateless_delimiter(args, &reader);
 
                        state = FETCH_DONE;
                        break;
@@ -1569,8 +1632,55 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                }
        }
 
+       for (i = 0; i < packfile_uris.nr; i++) {
+               struct child_process cmd = CHILD_PROCESS_INIT;
+               char packname[GIT_MAX_HEXSZ + 1];
+               const char *uri = packfile_uris.items[i].string +
+                       the_hash_algo->hexsz + 1;
+
+               argv_array_push(&cmd.args, "http-fetch");
+               argv_array_pushf(&cmd.args, "--packfile=%.*s",
+                                (int) the_hash_algo->hexsz,
+                                packfile_uris.items[i].string);
+               argv_array_push(&cmd.args, uri);
+               cmd.git_cmd = 1;
+               cmd.no_stdin = 1;
+               cmd.out = -1;
+               if (start_command(&cmd))
+                       die("fetch-pack: unable to spawn http-fetch");
+
+               if (read_in_full(cmd.out, packname, 5) < 0 ||
+                   memcmp(packname, "keep\t", 5))
+                       die("fetch-pack: expected keep then TAB at start of http-fetch output");
+
+               if (read_in_full(cmd.out, packname,
+                                the_hash_algo->hexsz + 1) < 0 ||
+                   packname[the_hash_algo->hexsz] != '\n')
+                       die("fetch-pack: expected hash then LF at end of http-fetch output");
+
+               packname[the_hash_algo->hexsz] = '\0';
+
+               close(cmd.out);
+
+               if (finish_command(&cmd))
+                       die("fetch-pack: unable to finish http-fetch");
+
+               if (memcmp(packfile_uris.items[i].string, packname,
+                          the_hash_algo->hexsz))
+                       die("fetch-pack: pack downloaded from %s does not match expected hash %.*s",
+                           uri, (int) the_hash_algo->hexsz,
+                           packfile_uris.items[i].string);
+
+               string_list_append_nodup(pack_lockfiles,
+                                        xstrfmt("%s/pack/pack-%s.keep",
+                                                get_object_directory(),
+                                                packname));
+       }
+       string_list_clear(&packfile_uris, 0);
+
        if (negotiator)
                negotiator->release(negotiator);
+
        oidset_clear(&common);
        return ref;
 }
@@ -1607,6 +1717,14 @@ static void fetch_pack_config(void)
        git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
        git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
        git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
+       if (!uri_protocols.nr) {
+               char *str;
+
+               if (!git_config_get_string("fetch.uriprotocols", &str) && str) {
+                       string_list_split(&uri_protocols, str, ',', -1);
+                       free(str);
+               }
+       }
 
        git_config(fetch_pack_config_cb, NULL);
 }
@@ -1759,7 +1877,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       const struct ref *ref,
                       struct ref **sought, int nr_sought,
                       struct oid_array *shallow,
-                      char **pack_lockfile,
+                      struct string_list *pack_lockfiles,
                       enum protocol_version version)
 {
        struct ref *ref_cpy;
@@ -1794,11 +1912,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                memset(&si, 0, sizeof(si));
                ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought,
                                           &shallows_scratch, &si,
-                                          pack_lockfile);
+                                          pack_lockfiles);
        } else {
                prepare_shallow_info(&si, shallow);
                ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
-                                       &si, pack_lockfile);
+                                       &si, pack_lockfiles);
        }
        reprepare_packed_git(the_repository);