receive-pack: allow using --strict mode for unpacking objects
[gitweb.git] / builtin-fetch-pack.c
index 3b217d96f074c1af1429ccdc90499338cfee04ea..29b38e4650cbd032ecfae02b334ce2b62ea1e622 100644 (file)
@@ -7,19 +7,18 @@
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
+#include "remote.h"
+#include "run-command.h"
 
-static int keep_pack;
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
 static int unpack_limit = 100;
-static int quiet;
-static int verbose;
-static int fetch_all;
-static int depth;
-static int no_progress;
+static struct fetch_pack_args args = {
+       /* .uploadpack = */ "git-upload-pack",
+};
+
 static const char fetch_pack_usage[] =
 "git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
-static const char *uploadpack = "git-upload-pack";
 
 #define COMPLETE       (1U << 0)
 #define COMMON         (1U << 1)
@@ -34,7 +33,7 @@ static const char *uploadpack = "git-upload-pack";
 #define MAX_IN_VAIN 256
 
 static struct commit_list *rev_list;
-static int non_common_revs, multi_ack, use_thin_pack, use_sideband;
+static int non_common_revs, multi_ack, use_sideband;
 
 static void rev_list_push(struct commit *commit, int mark)
 {
@@ -42,7 +41,8 @@ static void rev_list_push(struct commit *commit, int mark)
                commit->object.flags |= mark;
 
                if (!(commit->object.parsed))
-                       parse_commit(commit);
+                       if (parse_commit(commit))
+                               return;
 
                insert_by_date(commit, &rev_list);
 
@@ -84,7 +84,8 @@ static void mark_common(struct commit *commit,
                        if (!ancestors_only && !(o->flags & POPPED))
                                non_common_revs--;
                        if (!o->parsed && !dont_parse)
-                               parse_commit(commit);
+                               if (parse_commit(commit))
+                                       return;
 
                        for (parents = commit->parents;
                                        parents;
@@ -104,20 +105,20 @@ static const unsigned char* get_rev(void)
 
        while (commit == NULL) {
                unsigned int mark;
-               struct commit_list* parents;
+               struct commit_list *parents = NULL;
 
                if (rev_list == NULL || non_common_revs == 0)
                        return NULL;
 
                commit = rev_list->item;
                if (!(commit->object.parsed))
-                       parse_commit(commit);
+                       if (!parse_commit(commit))
+                               parents = commit->parents;
+
                commit->object.flags |= POPPED;
                if (!(commit->object.flags & COMMON))
                        non_common_revs--;
 
-               parents = commit->parents;
-
                if (commit->object.flags & COMMON) {
                        /* do not send "have", and ignore ancestors */
                        commit = NULL;
@@ -180,8 +181,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                                     (multi_ack ? " multi_ack" : ""),
                                     (use_sideband == 2 ? " side-band-64k" : ""),
                                     (use_sideband == 1 ? " side-band" : ""),
-                                    (use_thin_pack ? " thin-pack" : ""),
-                                    (no_progress ? " no-progress" : ""),
+                                    (args.use_thin_pack ? " thin-pack" : ""),
+                                    (args.no_progress ? " no-progress" : ""),
                                     " ofs-delta");
                else
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -189,13 +190,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        }
        if (is_repository_shallow())
                write_shallow_commits(fd[1], 1);
-       if (depth > 0)
-               packet_write(fd[1], "deepen %d", depth);
+       if (args.depth > 0)
+               packet_write(fd[1], "deepen %d", args.depth);
        packet_flush(fd[1]);
        if (!fetching)
                return 1;
 
-       if (depth > 0) {
+       if (args.depth > 0) {
                char line[1024];
                unsigned char sha1[20];
                int len;
@@ -213,7 +214,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                                if (!lookup_object(sha1))
                                        die("object not found: %s", line);
                                /* make sure that it is parsed as shallow */
-                               parse_object(sha1);
+                               if (!parse_object(sha1))
+                                       die("error in object: %s", line);
                                if (unregister_shallow(sha1))
                                        die("no shallow found: %s", line);
                                continue;
@@ -226,7 +228,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        retval = -1;
        while ((sha1 = get_rev())) {
                packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
-               if (verbose)
+               if (args.verbose)
                        fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
                in_vain++;
                if (!(31 & ++count)) {
@@ -244,7 +246,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 
                        do {
                                ack = get_ack(fd[0], result_sha1);
-                               if (verbose && ack)
+                               if (args.verbose && ack)
                                        fprintf(stderr, "got ack %d %s\n", ack,
                                                        sha1_to_hex(result_sha1));
                                if (ack == 1) {
@@ -263,7 +265,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        } while (ack);
                        flushes--;
                        if (got_continue && MAX_IN_VAIN < in_vain) {
-                               if (verbose)
+                               if (args.verbose)
                                        fprintf(stderr, "giving up\n");
                                break; /* give up */
                        }
@@ -271,7 +273,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        }
 done:
        packet_write(fd[1], "done\n");
-       if (verbose)
+       if (args.verbose)
                fprintf(stderr, "done\n");
        if (retval != 0) {
                multi_ack = 0;
@@ -280,7 +282,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
-                       if (verbose)
+                       if (args.verbose)
                                fprintf(stderr, "got ack (%d) %s\n", ack,
                                        sha1_to_hex(result_sha1));
                        if (ack == 1)
@@ -317,7 +319,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
 static void mark_recent_complete_commits(unsigned long cutoff)
 {
        while (complete && cutoff <= complete->item->date) {
-               if (verbose)
+               if (args.verbose)
                        fprintf(stderr, "Marking %s as complete\n",
                                sha1_to_hex(complete->item->object.sha1));
                pop_most_recent_commit(&complete, COMPLETE);
@@ -332,7 +334,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
        struct ref *ref, *next;
        struct ref *fastarray[32];
 
-       if (nr_match && !fetch_all) {
+       if (nr_match && !args.fetch_all) {
                if (ARRAY_SIZE(fastarray) < nr_match)
                        return_refs = xcalloc(nr_match, sizeof(struct ref *));
                else {
@@ -348,8 +350,8 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
                if (!memcmp(ref->name, "refs/", 5) &&
                    check_ref_format(ref->name + 5))
                        ; /* trash */
-               else if (fetch_all &&
-                        (!depth || prefixcmp(ref->name, "refs/tags/") )) {
+               else if (args.fetch_all &&
+                        (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
                        *newtail = ref;
                        ref->next = NULL;
                        newtail = &ref->next;
@@ -365,7 +367,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
                free(ref);
        }
 
-       if (!fetch_all) {
+       if (!args.fetch_all) {
                int i;
                for (i = 0; i < nr_match; i++) {
                        ref = return_refs[i];
@@ -387,7 +389,6 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
        int retval;
        unsigned long cutoff = 0;
 
-       track_object_refs = 0;
        save_commit_buffer = 0;
 
        for (ref = *refs; ref; ref = ref->next) {
@@ -408,7 +409,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
        }
 
-       if (!depth) {
+       if (!args.depth) {
                for_each_ref(mark_complete, NULL);
                if (cutoff)
                        mark_recent_complete_commits(cutoff);
@@ -442,7 +443,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                o = lookup_object(remote);
                if (!o || !(o->flags & COMPLETE)) {
                        retval = 0;
-                       if (!verbose)
+                       if (!args.verbose)
                                continue;
                        fprintf(stderr,
                                "want %s (%s)\n", sha1_to_hex(remote),
@@ -451,7 +452,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
 
                hashcpy(ref->new_sha1, local);
-               if (!verbose)
+               if (!args.verbose)
                        continue;
                fprintf(stderr,
                        "already have %s (%s)\n", sha1_to_hex(remote),
@@ -460,58 +461,46 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
        return retval;
 }
 
-static pid_t setup_sideband(int fd[2], int xd[2])
+static int sideband_demux(int fd, void *data)
 {
-       pid_t side_pid;
+       int *xd = data;
 
-       if (!use_sideband) {
-               fd[0] = xd[0];
-               fd[1] = xd[1];
-               return 0;
-       }
-       /* xd[] is talking with upload-pack; subprocess reads from
-        * xd[0], spits out band#2 to stderr, and feeds us band#1
-        * through our fd[0].
-        */
-       if (pipe(fd) < 0)
-               die("fetch-pack: unable to set up pipe");
-       side_pid = fork();
-       if (side_pid < 0)
-               die("fetch-pack: unable to fork off sideband demultiplexer");
-       if (!side_pid) {
-               /* subprocess */
-               close(fd[0]);
-               if (xd[0] != xd[1])
-                       close(xd[1]);
-               if (recv_sideband("fetch-pack", xd[0], fd[1], 2))
-                       exit(1);
-               exit(0);
-       }
-       close(xd[0]);
-       close(fd[1]);
-       fd[1] = xd[1];
-       return side_pid;
+       return recv_sideband("fetch-pack", xd[0], fd, 2);
 }
 
-static int get_pack(int xd[2])
+static int get_pack(int xd[2], char **pack_lockfile)
 {
-       int status;
-       pid_t pid, side_pid;
-       int fd[2];
+       struct async demux;
        const char *argv[20];
        char keep_arg[256];
        char hdr_arg[256];
        const char **av;
-       int do_keep = keep_pack;
-
-       side_pid = setup_sideband(fd, xd);
+       int do_keep = args.keep_pack;
+       struct child_process cmd;
+
+       memset(&demux, 0, sizeof(demux));
+       if (use_sideband) {
+               /* xd[] is talking with upload-pack; subprocess reads from
+                * xd[0], spits out band#2 to stderr, and feeds us band#1
+                * through demux->out.
+                */
+               demux.proc = sideband_demux;
+               demux.data = xd;
+               if (start_async(&demux))
+                       die("fetch-pack: unable to fork off sideband"
+                           " demultiplexer");
+       }
+       else
+               demux.out = xd[0];
 
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.argv = argv;
        av = argv;
        *hdr_arg = 0;
-       if (unpack_limit) {
+       if (!args.keep_pack && unpack_limit) {
                struct pack_header header;
 
-               if (read_pack_header(fd[0], &header))
+               if (read_pack_header(demux.out, &header))
                        die("protocol error: bad pack header");
                snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%u,%u",
                         ntohl(header.hdr_version), ntohl(header.hdr_entries));
@@ -522,13 +511,15 @@ static int get_pack(int xd[2])
        }
 
        if (do_keep) {
+               if (pack_lockfile)
+                       cmd.out = -1;
                *av++ = "index-pack";
                *av++ = "--stdin";
-               if (!quiet && !no_progress)
+               if (!args.quiet && !args.no_progress)
                        *av++ = "-v";
-               if (use_thin_pack)
+               if (args.use_thin_pack)
                        *av++ = "--fix-thin";
-               if (keep_pack > 1 || unpack_limit) {
+               if (args.lock_pack || unpack_limit) {
                        int s = sprintf(keep_arg,
                                        "--keep=fetch-pack %d on ", getpid());
                        if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
@@ -538,81 +529,67 @@ static int get_pack(int xd[2])
        }
        else {
                *av++ = "unpack-objects";
-               if (quiet)
+               if (args.quiet)
                        *av++ = "-q";
        }
        if (*hdr_arg)
                *av++ = hdr_arg;
        *av++ = NULL;
 
-       pid = fork();
-       if (pid < 0)
+       cmd.in = demux.out;
+       cmd.git_cmd = 1;
+       if (start_command(&cmd))
                die("fetch-pack: unable to fork off %s", argv[0]);
-       if (!pid) {
-               dup2(fd[0], 0);
-               close(fd[0]);
-               close(fd[1]);
-               execv_git_cmd(argv);
-               die("%s exec failed", argv[0]);
-       }
-       close(fd[0]);
-       close(fd[1]);
-       while (waitpid(pid, &status, 0) < 0) {
-               if (errno != EINTR)
-                       die("waiting for %s: %s", argv[0], strerror(errno));
-       }
-       if (WIFEXITED(status)) {
-               int code = WEXITSTATUS(status);
-               if (code)
-                       die("%s died with error code %d", argv[0], code);
-               return 0;
+       if (do_keep && pack_lockfile) {
+               *pack_lockfile = index_pack_lockfile(cmd.out);
+               close(cmd.out);
        }
-       if (WIFSIGNALED(status)) {
-               int sig = WTERMSIG(status);
-               die("%s died of signal %d", argv[0], sig);
-       }
-       die("%s died of unnatural causes %d", argv[0], status);
+
+       if (finish_command(&cmd))
+               die("%s failed", argv[0]);
+       if (use_sideband && finish_async(&demux))
+               die("error in sideband demultiplexer");
+       return 0;
 }
 
-static struct ref *do_fetch_pack(int fd[2], int nr_match, char **match)
+static struct ref *do_fetch_pack(int fd[2],
+               const struct ref *orig_ref,
+               int nr_match,
+               char **match,
+               char **pack_lockfile)
 {
-       struct ref *ref;
+       struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
 
-       get_remote_heads(fd[0], &ref, 0, NULL, 0);
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
        if (server_supports("multi_ack")) {
-               if (verbose)
+               if (args.verbose)
                        fprintf(stderr, "Server supports multi_ack\n");
                multi_ack = 1;
        }
        if (server_supports("side-band-64k")) {
-               if (verbose)
+               if (args.verbose)
                        fprintf(stderr, "Server supports side-band-64k\n");
                use_sideband = 2;
        }
        else if (server_supports("side-band")) {
-               if (verbose)
+               if (args.verbose)
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
-       if (!ref) {
-               packet_flush(fd[1]);
-               die("no matching remote head");
-       }
        if (everything_local(&ref, nr_match, match)) {
                packet_flush(fd[1]);
                goto all_done;
        }
        if (find_common(fd, sha1, ref) < 0)
-               if (keep_pack != 1)
+               if (!args.keep_pack)
                        /* When cloning, it is not unusual to have
                         * no common commit.
                         */
                        fprintf(stderr, "warning: no common commits\n");
 
-       if (get_pack(fd))
+       if (get_pack(fd, pack_lockfile))
                die("git-fetch-pack: fetch failed.");
 
  all_done:
@@ -638,7 +615,6 @@ static int remove_duplicates(int nr_heads, char **heads)
                        heads[dst] = heads[src];
                dst++;
        }
-       heads[dst] = 0;
        return dst;
 }
 
@@ -659,34 +635,26 @@ static int fetch_pack_config(const char *var, const char *value)
 
 static struct lock_file lock;
 
-void setup_fetch_pack(struct fetch_pack_args *args)
+static void fetch_pack_setup(void)
 {
-       uploadpack = args->uploadpack;
-       quiet = args->quiet;
-       keep_pack = args->keep_pack;
-       if (args->unpacklimit >= 0)
-               unpack_limit = args->unpacklimit;
-       if (args->keep_pack)
-               unpack_limit = 0;
-       use_thin_pack = args->use_thin_pack;
-       fetch_all = args->fetch_all;
-       verbose = args->verbose;
-       depth = args->depth;
-       no_progress = args->no_progress;
+       static int did_setup;
+       if (did_setup)
+               return;
+       git_config(fetch_pack_config);
+       if (0 <= transfer_unpack_limit)
+               unpack_limit = transfer_unpack_limit;
+       else if (0 <= fetch_unpack_limit)
+               unpack_limit = fetch_unpack_limit;
+       did_setup = 1;
 }
 
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 {
        int i, ret, nr_heads;
-       struct ref *ref;
+       struct ref *ref = NULL;
        char *dest = NULL, **heads;
-
-       git_config(fetch_pack_config);
-
-       if (0 <= transfer_unpack_limit)
-               unpack_limit = transfer_unpack_limit;
-       else if (0 <= fetch_unpack_limit)
-               unpack_limit = fetch_unpack_limit;
+       int fd[2];
+       struct child_process *conn;
 
        nr_heads = 0;
        heads = NULL;
@@ -695,40 +663,40 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 
                if (*arg == '-') {
                        if (!prefixcmp(arg, "--upload-pack=")) {
-                               uploadpack = arg + 14;
+                               args.uploadpack = arg + 14;
                                continue;
                        }
                        if (!prefixcmp(arg, "--exec=")) {
-                               uploadpack = arg + 7;
+                               args.uploadpack = arg + 7;
                                continue;
                        }
                        if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
-                               quiet = 1;
+                               args.quiet = 1;
                                continue;
                        }
                        if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
-                               keep_pack++;
-                               unpack_limit = 0;
+                               args.lock_pack = args.keep_pack;
+                               args.keep_pack = 1;
                                continue;
                        }
                        if (!strcmp("--thin", arg)) {
-                               use_thin_pack = 1;
+                               args.use_thin_pack = 1;
                                continue;
                        }
                        if (!strcmp("--all", arg)) {
-                               fetch_all = 1;
+                               args.fetch_all = 1;
                                continue;
                        }
                        if (!strcmp("-v", arg)) {
-                               verbose = 1;
+                               args.verbose = 1;
                                continue;
                        }
                        if (!prefixcmp(arg, "--depth=")) {
-                               depth = strtol(arg + 8, NULL, 0);
+                               args.depth = strtol(arg + 8, NULL, 0);
                                continue;
                        }
                        if (!strcmp("--no-progress", arg)) {
-                               no_progress = 1;
+                               args.no_progress = 1;
                                continue;
                        }
                        usage(fetch_pack_usage);
@@ -741,10 +709,33 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
-       ref = fetch_pack(dest, nr_heads, heads);
+       conn = git_connect(fd, (char *)dest, args.uploadpack,
+                          args.verbose ? CONNECT_VERBOSE : 0);
+       if (conn) {
+               get_remote_heads(fd[0], &ref, 0, NULL, 0);
 
+               ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
+               close(fd[0]);
+               close(fd[1]);
+               if (finish_connect(conn))
+                       ref = NULL;
+       } else {
+               ref = NULL;
+       }
        ret = !ref;
 
+       if (!ret && nr_heads) {
+               /* If the heads to pull were given, we should have
+                * consumed all of them by matching the remote.
+                * Otherwise, 'git-fetch remote no-such-ref' would
+                * silently succeed without issuing an error.
+                */
+               for (i = 0; i < nr_heads; i++)
+                       if (heads[i] && heads[i][0]) {
+                               error("no such remote ref %s", heads[i]);
+                               ret = 1;
+                       }
+       }
        while (ref) {
                printf("%s %s\n",
                       sha1_to_hex(ref->old_sha1), ref->name);
@@ -754,46 +745,33 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        return ret;
 }
 
-struct ref *fetch_pack(const char *dest, int nr_heads, char **heads)
+struct ref *fetch_pack(struct fetch_pack_args *my_args,
+                      int fd[], struct child_process *conn,
+                      const struct ref *ref,
+               const char *dest,
+               int nr_heads,
+               char **heads,
+               char **pack_lockfile)
 {
-       int i, ret;
-       int fd[2];
-       pid_t pid;
-       struct ref *ref;
        struct stat st;
+       struct ref *ref_cpy;
 
-       if (depth > 0) {
+       fetch_pack_setup();
+       memcpy(&args, my_args, sizeof(args));
+       if (args.depth > 0) {
                if (stat(git_path("shallow"), &st))
                        st.st_mtime = 0;
        }
 
-       printf("connect to %s\n", dest);
-
-       pid = git_connect(fd, (char *)dest, uploadpack,
-                          verbose ? CONNECT_VERBOSE : 0);
-       if (pid < 0)
-               return NULL;
        if (heads && nr_heads)
                nr_heads = remove_duplicates(nr_heads, heads);
-       ref = do_fetch_pack(fd, nr_heads, heads);
-       close(fd[0]);
-       close(fd[1]);
-       ret = finish_connect(pid);
-
-       if (!ret && nr_heads) {
-               /* If the heads to pull were given, we should have
-                * consumed all of them by matching the remote.
-                * Otherwise, 'git-fetch remote no-such-ref' would
-                * silently succeed without issuing an error.
-                */
-               for (i = 0; i < nr_heads; i++)
-                       if (heads[i] && heads[i][0]) {
-                               error("no such remote ref %s", heads[i]);
-                               ret = 1;
-                       }
+       if (!ref) {
+               packet_flush(fd[1]);
+               die("no matching remote head");
        }
+       ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
 
-       if (!ret && depth > 0) {
+       if (args.depth > 0) {
                struct cache_time mtime;
                char *shallow = git_path("shallow");
                int fd;
@@ -817,13 +795,9 @@ struct ref *fetch_pack(const char *dest, int nr_heads, char **heads)
                        unlink(shallow);
                        rollback_lock_file(&lock);
                } else {
-                       close(fd);
                        commit_lock_file(&lock);
                }
        }
 
-       if (ret)
-               ref = NULL;
-
-       return ref;
+       return ref_cpy;
 }