receive-pack: allow using --strict mode for unpacking objects
[gitweb.git] / builtin-fetch-pack.c
index 8f25d509a0b1bace78d0cb3f2a219b23e8b66d18..29b38e4650cbd032ecfae02b334ce2b62ea1e622 100644 (file)
@@ -7,15 +7,18 @@
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
+#include "remote.h"
+#include "run-command.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
 static int unpack_limit = 100;
-static struct fetch_pack_args args;
+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)
@@ -30,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)
 {
@@ -38,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);
 
@@ -80,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;
@@ -100,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;
@@ -176,7 +181,7 @@ 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" : ""),
+                                    (args.use_thin_pack ? " thin-pack" : ""),
                                     (args.no_progress ? " no-progress" : ""),
                                     " ofs-delta");
                else
@@ -209,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;
@@ -383,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) {
@@ -456,59 +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], 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 = args.keep_pack;
-       int keep_pipe[2];
+       struct child_process cmd;
 
-       side_pid = setup_sideband(fd, xd);
+       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 (!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));
@@ -519,8 +511,8 @@ static int get_pack(int xd[2], char **pack_lockfile)
        }
 
        if (do_keep) {
-               if (pack_lockfile && pipe(keep_pipe))
-                       die("fetch-pack: pipe setup failure: %s", strerror(errno));
+               if (pack_lockfile)
+                       cmd.out = -1;
                *av++ = "index-pack";
                *av++ = "--stdin";
                if (!args.quiet && !args.no_progress)
@@ -544,54 +536,31 @@ static int get_pack(int xd[2], char **pack_lockfile)
                *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);
-               if (do_keep && pack_lockfile) {
-                       dup2(keep_pipe[1], 1);
-                       close(keep_pipe[0]);
-                       close(keep_pipe[1]);
-               }
-               close(fd[0]);
-               close(fd[1]);
-               execv_git_cmd(argv);
-               die("%s exec failed", argv[0]);
-       }
-       close(fd[0]);
-       close(fd[1]);
        if (do_keep && pack_lockfile) {
-               close(keep_pipe[1]);
-               *pack_lockfile = index_pack_lockfile(keep_pipe[0]);
-               close(keep_pipe[0]);
-       }
-       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 (WIFSIGNALED(status)) {
-               int sig = WTERMSIG(status);
-               die("%s died of signal %d", argv[0], sig);
+               *pack_lockfile = index_pack_lockfile(cmd.out);
+               close(cmd.out);
        }
-       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],
+               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")) {
@@ -609,10 +578,6 @@ static struct ref *do_fetch_pack(int fd[2],
                        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;
@@ -686,8 +651,10 @@ static void fetch_pack_setup(void)
 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;
+       int fd[2];
+       struct child_process *conn;
 
        nr_heads = 0;
        heads = NULL;
@@ -742,9 +709,33 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
-       ref = fetch_pack(&args, dest, nr_heads, heads, NULL);
+       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);
@@ -755,16 +746,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 }
 
 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;
 
        fetch_pack_setup();
        memcpy(&args, my_args, sizeof(args));
@@ -773,31 +763,15 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        st.st_mtime = 0;
        }
 
-       pid = git_connect(fd, (char *)dest, uploadpack,
-                          args.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, pack_lockfile);
-       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 && args.depth > 0) {
+       if (args.depth > 0) {
                struct cache_time mtime;
                char *shallow = git_path("shallow");
                int fd;
@@ -821,13 +795,9 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        unlink(shallow);
                        rollback_lock_file(&lock);
                } else {
-                       close(fd);
                        commit_lock_file(&lock);
                }
        }
 
-       if (ret)
-               ref = NULL;
-
-       return ref;
+       return ref_cpy;
 }