blame-options.txt: place each -L option variation on its own line
[gitweb.git] / transport-helper.c
index dcd8d97411dd15e01ba1480a44b2a998043b424a..2f5ac3fbeefd65c61c9e9c89f97896f9dfac89c5 100644 (file)
@@ -11,6 +11,7 @@
 #include "thread-utils.h"
 #include "sigchain.h"
 #include "argv-array.h"
+#include "refs.h"
 
 static int debug;
 
@@ -25,6 +26,7 @@ struct helper_data {
                option : 1,
                push : 1,
                connect : 1,
+               signed_tags : 1,
                no_disconnect_req : 1;
        char *export_marks;
        char *import_marks;
@@ -46,7 +48,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
                die_errno("Full write to remote helper failed");
 }
 
-static int recvline_fh(FILE *helper, struct strbuf *buffer)
+static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name)
 {
        strbuf_reset(buffer);
        if (debug)
@@ -54,7 +56,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer)
        if (strbuf_getline(buffer, helper, '\n') == EOF) {
                if (debug)
                        fprintf(stderr, "Debug: Remote helper quit.\n");
-               exit(128);
+               die("Reading from helper 'git-remote-%s' failed", name);
        }
 
        if (debug)
@@ -64,7 +66,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer)
 
 static int recvline(struct helper_data *helper, struct strbuf *buffer)
 {
-       return recvline_fh(helper->out, buffer);
+       return recvline_fh(helper->out, buffer, helper->name);
 }
 
 static void xchgline(struct helper_data *helper, struct strbuf *buffer)
@@ -191,6 +193,8 @@ static struct child_process *get_helper(struct transport *transport)
                        refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
                } else if (!strcmp(capname, "connect")) {
                        data->connect = 1;
+               } else if (!strcmp(capname, "signed-tags")) {
+                       data->signed_tags = 1;
                } else if (!prefixcmp(capname, "export-marks ")) {
                        struct strbuf arg = STRBUF_INIT;
                        strbuf_addstr(&arg, "--export-marks=");
@@ -211,10 +215,11 @@ static struct child_process *get_helper(struct transport *transport)
                int i;
                data->refspec_nr = refspec_nr;
                data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
-               for (i = 0; i < refspec_nr; i++) {
+               for (i = 0; i < refspec_nr; i++)
                        free((char *)refspecs[i]);
-               }
                free(refspecs);
+       } else if (data->import || data->bidi_import || data->export) {
+               warning("This remote helper should implement refspec capability.");
        }
        strbuf_release(&buf);
        if (debug)
@@ -410,9 +415,11 @@ static int get_exporter(struct transport *transport,
        /* we need to duplicate helper->in because we want to use it after
         * fastexport is done with it. */
        fastexport->out = dup(helper->in);
-       fastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));
+       fastexport->argv = xcalloc(6 + revlist_args->nr, sizeof(*fastexport->argv));
        fastexport->argv[argc++] = "fast-export";
        fastexport->argv[argc++] = "--use-done-feature";
+       fastexport->argv[argc++] = data->signed_tags ?
+               "--signed-tags=verbatim" : "--signed-tags=warn-strip";
        if (data->export_marks)
                fastexport->argv[argc++] = data->export_marks;
        if (data->import_marks)
@@ -469,7 +476,7 @@ static int fetch_with_import(struct transport *transport,
         * were fetching.
         *
         * (If no "refspec" capability was specified, for historical
-        * reasons we default to *:*.)
+        * reasons we default to the equivalent of *:*.)
         *
         * Store the result in to_fetch[i].old_sha1.  Callers such
         * as "git fetch" can use the value to write feedback to the
@@ -536,7 +543,7 @@ static int process_connect_service(struct transport *transport,
                goto exit;
 
        sendline(data, &cmdbuf);
-       recvline_fh(input, &cmdbuf);
+       recvline_fh(input, &cmdbuf, name);
        if (!strcmp(cmdbuf.buf, "")) {
                data->no_disconnect_req = 1;
                if (debug)
@@ -618,7 +625,7 @@ static int fetch(struct transport *transport,
        return -1;
 }
 
-static void push_update_ref_status(struct strbuf *buf,
+static int push_update_ref_status(struct strbuf *buf,
                                   struct ref **ref,
                                   struct ref *remote_refs)
 {
@@ -684,7 +691,7 @@ static void push_update_ref_status(struct strbuf *buf,
                *ref = find_ref_by_name(remote_refs, refname);
        if (!*ref) {
                warning("helper reported unexpected status of %s", refname);
-               return;
+               return 1;
        }
 
        if ((*ref)->status != REF_STATUS_NONE) {
@@ -693,11 +700,12 @@ static void push_update_ref_status(struct strbuf *buf,
                 * status reported by the remote helper if the latter is 'no match'.
                 */
                if (status == REF_STATUS_NONE)
-                       return;
+                       return 1;
        }
 
        (*ref)->status = status;
        (*ref)->remote_status = msg;
+       return !(status == REF_STATUS_OK);
 }
 
 static void push_update_refs_status(struct helper_data *data,
@@ -706,11 +714,24 @@ static void push_update_refs_status(struct helper_data *data,
        struct strbuf buf = STRBUF_INIT;
        struct ref *ref = remote_refs;
        for (;;) {
+               char *private;
+
                recvline(data, &buf);
                if (!buf.len)
                        break;
 
-               push_update_ref_status(&buf, &ref, remote_refs);
+               if (push_update_ref_status(&buf, &ref, remote_refs))
+                       continue;
+
+               if (!data->refspecs)
+                       continue;
+
+               /* propagate back the update to the remote namespace */
+               private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
+               if (!private)
+                       continue;
+               update_ref("update by helper", private, ref->new_sha1, NULL, 0, 0);
+               free(private);
        }
        strbuf_release(&buf);
 }
@@ -785,6 +806,9 @@ static int push_refs_with_export(struct transport *transport,
        struct string_list revlist_args = STRING_LIST_INIT_NODUP;
        struct strbuf buf = STRBUF_INIT;
 
+       if (!data->refspecs)
+               die("remote-helper doesn't support push; refspec needed");
+
        helper = get_helper(transport);
 
        write_constant(helper->in, "export\n");
@@ -795,22 +819,19 @@ static int push_refs_with_export(struct transport *transport,
                char *private;
                unsigned char sha1[20];
 
-               if (!data->refspecs)
-                       continue;
+               if (ref->deletion)
+                       die("remote-helpers do not support ref deletion");
+
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
                if (private && !get_sha1(private, sha1)) {
                        strbuf_addf(&buf, "^%s", private);
                        string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
+                       hashcpy(ref->old_sha1, sha1);
                }
                free(private);
 
-               if (ref->deletion) {
-                       die("remote-helpers do not support ref deletion");
-               }
-
                if (ref->peer_ref)
                        string_list_append(&revlist_args, ref->peer_ref->name);
-
        }
 
        if (get_exporter(transport, &exporter, &revlist_args))