pull --rebase: exit early when the working directory is dirty
[gitweb.git] / upload-pack.c
index 67994680f2f48e573f78c592c853422e6d8995cf..b46dd365ea289c6d397dc6fc994b6ba4227886fc 100644 (file)
@@ -27,7 +27,8 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
 static unsigned long oldest_have;
 
 static int multi_ack, nr_our_refs;
-static int use_thin_pack, use_ofs_delta, no_progress;
+static int use_thin_pack, use_ofs_delta, use_include_tag;
+static int no_progress;
 static struct object_array have_obj;
 static struct object_array want_obj;
 static unsigned int timeout;
@@ -35,6 +36,7 @@ static unsigned int timeout;
  * otherwise maximum packet size (up to 65520 bytes).
  */
 static int use_sideband;
+static int debug_fd;
 
 static void reset_timeout(void)
 {
@@ -129,7 +131,8 @@ static int do_rev_list(int fd, void *create_full_pack)
                }
                setup_revisions(0, NULL, &revs, NULL);
        }
-       prepare_revision_walk(&revs);
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object);
        return 0;
@@ -144,6 +147,7 @@ static void create_pack_file(void)
        char abort_msg[] = "aborting due to possible repository "
                "corruption on the remote side.";
        int buffered = -1;
+       ssize_t sz;
        const char *argv[10];
        int arg = 0;
 
@@ -159,6 +163,8 @@ static void create_pack_file(void)
                argv[arg++] = "--progress";
        if (use_ofs_delta)
                argv[arg++] = "--delta-base-offset";
+       if (use_include_tag)
+               argv[arg++] = "--include-tag";
        argv[arg++] = NULL;
 
        memset(&pack_objects, 0, sizeof(pack_objects));
@@ -168,22 +174,15 @@ static void create_pack_file(void)
        pack_objects.git_cmd = 1;
        pack_objects.argv = argv;
 
-       if (start_command(&pack_objects)) {
-               /* daemon sets things up to ignore TERM */
-               kill(rev_list.pid, SIGKILL);
+       if (start_command(&pack_objects))
                die("git-upload-pack: unable to fork git-pack-objects");
-       }
 
        /* We read from pack_objects.err to capture stderr output for
         * progress bar, and pack_objects.out to capture the pack data.
         */
 
        while (1) {
-               const char *who;
                struct pollfd pfd[2];
-               pid_t pid;
-               int status;
-               ssize_t sz;
                int pe, pu, pollsize;
 
                reset_timeout();
@@ -204,123 +203,91 @@ static void create_pack_file(void)
                        pollsize++;
                }
 
-               if (pollsize) {
-                       if (poll(pfd, pollsize, -1) < 0) {
-                               if (errno != EINTR) {
-                                       error("poll failed, resuming: %s",
-                                             strerror(errno));
-                                       sleep(1);
-                               }
-                               continue;
-                       }
-                       if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
-                               /* Data ready; we keep the last byte
-                                * to ourselves in case we detect
-                                * broken rev-list, so that we can
-                                * leave the stream corrupted.  This
-                                * is unfortunate -- unpack-objects
-                                * would happily accept a valid pack
-                                * data with trailing garbage, so
-                                * appending garbage after we pass all
-                                * the pack data is not good enough to
-                                * signal breakage to downstream.
-                                */
-                               char *cp = data;
-                               ssize_t outsz = 0;
-                               if (0 <= buffered) {
-                                       *cp++ = buffered;
-                                       outsz++;
-                               }
-                               sz = xread(pack_objects.out, cp,
-                                         sizeof(data) - outsz);
-                               if (0 < sz)
-                                               ;
-                               else if (sz == 0) {
-                                       close(pack_objects.out);
-                                       pack_objects.out = -1;
-                               }
-                               else
-                                       goto fail;
-                               sz += outsz;
-                               if (1 < sz) {
-                                       buffered = data[sz-1] & 0xFF;
-                                       sz--;
-                               }
-                               else
-                                       buffered = -1;
-                               sz = send_client_data(1, data, sz);
-                               if (sz < 0)
-                                       goto fail;
-                       }
-                       if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
-                               /* Status ready; we ship that in the side-band
-                                * or dump to the standard error.
-                                */
-                               sz = xread(pack_objects.err, progress,
-                                         sizeof(progress));
-                               if (0 < sz)
-                                       send_client_data(2, progress, sz);
-                               else if (sz == 0) {
-                                       close(pack_objects.err);
-                                       pack_objects.err = -1;
-                               }
-                               else
-                                       goto fail;
+               if (!pollsize)
+                       break;
+
+               if (poll(pfd, pollsize, -1) < 0) {
+                       if (errno != EINTR) {
+                               error("poll failed, resuming: %s",
+                                     strerror(errno));
+                               sleep(1);
                        }
+                       continue;
                }
-
-               /* See if the children are still there */
-               if (rev_list.pid || pack_objects.pid) {
-                       pid = waitpid(-1, &status, WNOHANG);
-                       if (!pid)
-                               continue;
-                       who = ((pid == rev_list.pid) ? "git-rev-list" :
-                              (pid == pack_objects.pid) ? "git-pack-objects" :
-                              NULL);
-                       if (!who) {
-                               if (pid < 0) {
-                                       error("git-upload-pack: %s",
-                                             strerror(errno));
-                                       goto fail;
-                               }
-                               error("git-upload-pack: we weren't "
-                                     "waiting for %d", pid);
-                               continue;
+               if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
+                       /* Data ready; we keep the last byte to ourselves
+                        * in case we detect broken rev-list, so that we
+                        * can leave the stream corrupted.  This is
+                        * unfortunate -- unpack-objects would happily
+                        * accept a valid packdata with trailing garbage,
+                        * so appending garbage after we pass all the
+                        * pack data is not good enough to signal
+                        * breakage to downstream.
+                        */
+                       char *cp = data;
+                       ssize_t outsz = 0;
+                       if (0 <= buffered) {
+                               *cp++ = buffered;
+                               outsz++;
                        }
-                       if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
-                               error("git-upload-pack: %s died with error.",
-                                     who);
+                       sz = xread(pack_objects.out, cp,
+                                 sizeof(data) - outsz);
+                       if (0 < sz)
+                                       ;
+                       else if (sz == 0) {
+                               close(pack_objects.out);
+                               pack_objects.out = -1;
+                       }
+                       else
                                goto fail;
+                       sz += outsz;
+                       if (1 < sz) {
+                               buffered = data[sz-1] & 0xFF;
+                               sz--;
                        }
-                       if (pid == rev_list.pid)
-                               rev_list.pid = 0;
-                       if (pid == pack_objects.pid)
-                               pack_objects.pid = 0;
-                       if (rev_list.pid || pack_objects.pid)
-                               continue;
-               }
-
-               /* both died happily */
-               if (pollsize)
-                       continue;
-
-               /* flush the data */
-               if (0 <= buffered) {
-                       data[0] = buffered;
-                       sz = send_client_data(1, data, 1);
+                       else
+                               buffered = -1;
+                       sz = send_client_data(1, data, sz);
                        if (sz < 0)
                                goto fail;
-                       fprintf(stderr, "flushed.\n");
                }
-               if (use_sideband)
-                       packet_flush(1);
-               return;
+               if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
+                       /* Status ready; we ship that in the side-band
+                        * or dump to the standard error.
+                        */
+                       sz = xread(pack_objects.err, progress,
+                                 sizeof(progress));
+                       if (0 < sz)
+                               send_client_data(2, progress, sz);
+                       else if (sz == 0) {
+                               close(pack_objects.err);
+                               pack_objects.err = -1;
+                       }
+                       else
+                               goto fail;
+               }
        }
+
+       if (finish_command(&pack_objects)) {
+               error("git-upload-pack: git-pack-objects died with error.");
+               goto fail;
+       }
+       if (finish_async(&rev_list))
+               goto fail;      /* error was already reported */
+
+       /* flush the data */
+       if (0 <= buffered) {
+               data[0] = buffered;
+               sz = send_client_data(1, data, 1);
+               if (sz < 0)
+                       goto fail;
+               fprintf(stderr, "flushed.\n");
+       }
+       if (use_sideband)
+               packet_flush(1);
+       return;
+
  fail:
-       if (pack_objects.pid)
-               kill(pack_objects.pid, SIGKILL);
-       if (rev_list.pid)
-               kill(rev_list.pid, SIGKILL);
        send_client_data(3, abort_msg, sizeof(abort_msg));
        die("git-upload-pack: %s", abort_msg);
 }
@@ -430,7 +397,6 @@ static int get_common_commits(void)
        char hex[41], last_hex[41];
        int len;
 
-       track_object_refs = 0;
        save_commit_buffer = 0;
 
        for(;;) {
@@ -482,6 +448,8 @@ static void receive_needs(void)
        static char line[1000];
        int len, depth = 0;
 
+       if (debug_fd)
+               write_in_full(debug_fd, "#S\n", 3);
        for (;;) {
                struct object *o;
                unsigned char sha1_buf[20];
@@ -489,6 +457,8 @@ static void receive_needs(void)
                reset_timeout();
                if (!len)
                        break;
+               if (debug_fd)
+                       write_in_full(debug_fd, line, len);
 
                if (!prefixcmp(line, "shallow ")) {
                        unsigned char sha1[20];
@@ -527,6 +497,8 @@ static void receive_needs(void)
                        use_sideband = DEFAULT_PACKET_MAX;
                if (strstr(line+45, "no-progress"))
                        no_progress = 1;
+               if (strstr(line+45, "include-tag"))
+                       use_include_tag = 1;
 
                /* We have sent all our refs already, and the other end
                 * should have chosen out of them; otherwise they are
@@ -544,6 +516,8 @@ static void receive_needs(void)
                        add_object_array(o, NULL, &want_obj);
                }
        }
+       if (debug_fd)
+               write_in_full(debug_fd, "#E\n", 3);
        if (depth == 0 && shallows.nr == 0)
                return;
        if (depth > 0) {
@@ -571,7 +545,8 @@ static void receive_needs(void)
                                /* make sure the real parents are parsed */
                                unregister_shallow(object->sha1);
                                object->parsed = 0;
-                               parse_commit((struct commit *)object);
+                               if (parse_commit((struct commit *)object))
+                                       die("invalid commit");
                                parents = ((struct commit *)object)->parents;
                                while (parents) {
                                        add_object_array(&parents->item->object,
@@ -595,7 +570,8 @@ static void receive_needs(void)
 static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        static const char *capabilities = "multi_ack thin-pack side-band"
-               " side-band-64k ofs-delta shallow no-progress";
+               " side-band-64k ofs-delta shallow no-progress"
+               " include-tag";
        struct object *o = parse_object(sha1);
 
        if (!o)
@@ -613,7 +589,8 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
        }
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, refname, 0);
-               packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+               if (o)
+                       packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
        }
        return 0;
 }
@@ -658,12 +635,17 @@ int main(int argc, char **argv)
 
        if (i != argc-1)
                usage(upload_pack_usage);
+
+       setup_path(NULL);
+
        dir = argv[i];
 
        if (!enter_repo(dir, strict))
                die("'%s': unable to chdir or not a git archive", dir);
        if (is_repository_shallow())
                die("attempt to fetch/clone from a shallow repository");
+       if (getenv("GIT_DEBUG_SEND_PACK"))
+               debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
        upload_pack();
        return 0;
 }