Merge branch 'maint-2.11' into maint
[gitweb.git] / send-pack.c
index 37ee04ea3bf41b3bc4b0b920d52449c2f6d681d7..d2d2a49a0231293e98ab8546945f8d3ef9fa205d 100644 (file)
@@ -36,18 +36,15 @@ int option_parse_push_signed(const struct option *opt,
        die("bad %s argument: %s", opt->long_name, arg);
 }
 
-static int feed_object(const unsigned char *sha1, int fd, int negative)
+static void feed_object(const unsigned char *sha1, FILE *fh, int negative)
 {
-       char buf[42];
-
        if (negative && !has_sha1_file(sha1))
-               return 1;
+               return;
 
-       memcpy(buf + negative, sha1_to_hex(sha1), 40);
        if (negative)
-               buf[0] = '^';
-       buf[40 + negative] = '\n';
-       return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
+               putc('^', fh);
+       fputs(sha1_to_hex(sha1), fh);
+       putc('\n', fh);
 }
 
 /*
@@ -73,7 +70,9 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
                NULL,
        };
        struct child_process po = CHILD_PROCESS_INIT;
+       FILE *po_in;
        int i;
+       int rc;
 
        i = 4;
        if (args->use_thin_pack)
@@ -97,21 +96,22 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
         * We feed the pack-objects we just spawned with revision
         * parameters by writing to the pipe.
         */
+       po_in = xfdopen(po.in, "w");
        for (i = 0; i < extra->nr; i++)
-               if (!feed_object(extra->sha1[i], po.in, 1))
-                       break;
+               feed_object(extra->sha1[i], po_in, 1);
 
        while (refs) {
-               if (!is_null_oid(&refs->old_oid) &&
-                   !feed_object(refs->old_oid.hash, po.in, 1))
-                       break;
-               if (!is_null_oid(&refs->new_oid) &&
-                   !feed_object(refs->new_oid.hash, po.in, 0))
-                       break;
+               if (!is_null_oid(&refs->old_oid))
+                       feed_object(refs->old_oid.hash, po_in, 1);
+               if (!is_null_oid(&refs->new_oid))
+                       feed_object(refs->new_oid.hash, po_in, 0);
                refs = refs->next;
        }
 
-       close(po.in);
+       fflush(po_in);
+       if (ferror(po_in))
+               die_errno("error writing to pack-objects");
+       fclose(po_in);
 
        if (args->stateless_rpc) {
                char *buf = xmalloc(LARGE_PACKET_MAX);
@@ -126,27 +126,44 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
                po.out = -1;
        }
 
-       if (finish_command(&po))
+       rc = finish_command(&po);
+       if (rc) {
+               /*
+                * For a normal non-zero exit, we assume pack-objects wrote
+                * something useful to stderr. For death by signal, though,
+                * we should mention it to the user. The exception is SIGPIPE
+                * (141), because that's a normal occurence if the remote end
+                * hangs up (and we'll report that by trying to read the unpack
+                * status).
+                */
+               if (rc > 128 && rc != 141)
+                       error("pack-objects died of signal %d", rc - 128);
                return -1;
+       }
+       return 0;
+}
+
+static int receive_unpack_status(int in)
+{
+       const char *line = packet_read_line(in, NULL);
+       if (!skip_prefix(line, "unpack ", &line))
+               return error(_("unable to parse remote unpack status: %s"), line);
+       if (strcmp(line, "ok"))
+               return error(_("remote unpack failed: %s"), line);
        return 0;
 }
 
 static int receive_status(int in, struct ref *refs)
 {
        struct ref *hint;
-       int ret = 0;
-       char *line = packet_read_line(in, NULL);
-       if (!starts_with(line, "unpack "))
-               return error("did not receive remote status");
-       if (strcmp(line, "unpack ok")) {
-               error("unpack failed: %s", line + 7);
-               ret = -1;
-       }
+       int ret;
+
        hint = NULL;
+       ret = receive_unpack_status(in);
        while (1) {
                char *refname;
                char *msg;
-               line = packet_read_line(in, NULL);
+               char *line = packet_read_line(in, NULL);
                if (!line)
                        break;
                if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
@@ -182,8 +199,7 @@ static int receive_status(int in, struct ref *refs)
                        hint->status = REF_STATUS_REMOTE_REJECT;
                        ret = -1;
                }
-               if (msg)
-                       hint->remote_status = xstrdup(msg);
+               hint->remote_status = xstrdup_or_null(msg);
                /* start our next search from the next ref */
                hint = hint->next;
        }
@@ -261,12 +277,13 @@ static int generate_push_cert(struct strbuf *req_buf,
                              const char *push_cert_nonce)
 {
        const struct ref *ref;
+       struct string_list_item *item;
        char *signing_key = xstrdup(get_signing_key());
        const char *cp, *np;
        struct strbuf cert = STRBUF_INIT;
        int update_seen = 0;
 
-       strbuf_addf(&cert, "certificate version 0.1\n");
+       strbuf_addstr(&cert, "certificate version 0.1\n");
        strbuf_addf(&cert, "pusher %s ", signing_key);
        datestamp(&cert);
        strbuf_addch(&cert, '\n');
@@ -277,6 +294,9 @@ static int generate_push_cert(struct strbuf *req_buf,
        }
        if (push_cert_nonce[0])
                strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
+       if (args->push_options)
+               for_each_string_list_item(item, args->push_options)
+                       strbuf_addf(&cert, "push-option %s\n", item->string);
        strbuf_addstr(&cert, "\n");
 
        for (ref = remote_refs; ref; ref = ref->next) {
@@ -371,6 +391,8 @@ int send_pack(struct send_pack_args *args,
        int agent_supported = 0;
        int use_atomic = 0;
        int atomic_supported = 0;
+       int use_push_options = 0;
+       int push_options_supported = 0;
        unsigned cmds_sent = 0;
        int ret;
        struct async demux;
@@ -393,6 +415,8 @@ int send_pack(struct send_pack_args *args,
                args->use_thin_pack = 0;
        if (server_supports("atomic"))
                atomic_supported = 1;
+       if (server_supports("push-options"))
+               push_options_supported = 1;
 
        if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
                int len;
@@ -419,6 +443,11 @@ int send_pack(struct send_pack_args *args,
 
        use_atomic = atomic_supported && args->atomic;
 
+       if (args->push_options && !push_options_supported)
+               die(_("the receiving end does not support push options"));
+
+       use_push_options = push_options_supported && args->push_options;
+
        if (status_report)
                strbuf_addstr(&cap_buf, " report-status");
        if (use_sideband)
@@ -427,6 +456,8 @@ int send_pack(struct send_pack_args *args,
                strbuf_addstr(&cap_buf, " quiet");
        if (use_atomic)
                strbuf_addstr(&cap_buf, " atomic");
+       if (use_push_options)
+               strbuf_addstr(&cap_buf, " push-options");
        if (agent_supported)
                strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
 
@@ -513,6 +544,18 @@ int send_pack(struct send_pack_args *args,
        strbuf_release(&req_buf);
        strbuf_release(&cap_buf);
 
+       if (use_push_options) {
+               struct string_list_item *item;
+               struct strbuf sb = STRBUF_INIT;
+
+               for_each_string_list_item(item, args->push_options)
+                       packet_buf_write(&sb, "%s", item->string);
+
+               write_or_die(out, sb.buf, sb.len);
+               packet_flush(out);
+               strbuf_release(&sb);
+       }
+
        if (use_sideband && cmds_sent) {
                memset(&demux, 0, sizeof(demux));
                demux.proc = sideband_demux;
@@ -532,6 +575,14 @@ int send_pack(struct send_pack_args *args,
                                close(out);
                        if (git_connection_is_socket(conn))
                                shutdown(fd[0], SHUT_WR);
+
+                       /*
+                        * Do not even bother with the return value; we know we
+                        * are failing, and just want the error() side effects.
+                        */
+                       if (status_report)
+                               receive_unpack_status(in);
+
                        if (use_sideband) {
                                close(demux.out);
                                finish_async(&demux);