transport-helper.c: do not overwrite forced bit
[gitweb.git] / transport-helper.c
index fe1b7021d70b1014b9e7801c87175f7bf45875e6..705dce7e049af441f706ea56b8c5ca82bf2b5482 100644 (file)
@@ -11,6 +11,7 @@
 #include "thread-utils.h"
 #include "sigchain.h"
 #include "argv-array.h"
+#include "refs.h"
 
 static int debug;
 
@@ -27,7 +28,8 @@ struct helper_data {
                connect : 1,
                signed_tags : 1,
                check_connectivity : 1,
-               no_disconnect_req : 1;
+               no_disconnect_req : 1,
+               no_private_update : 1;
        char *export_marks;
        char *import_marks;
        /* These go from remote name (as in "list") to private name */
@@ -48,7 +50,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)
@@ -66,7 +68,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)
@@ -207,6 +209,8 @@ static struct child_process *get_helper(struct transport *transport)
                        strbuf_addstr(&arg, "--import-marks=");
                        strbuf_addstr(&arg, capname + strlen("import-marks "));
                        data->import_marks = strbuf_detach(&arg, NULL);
+               } else if (!prefixcmp(capname, "no-private-update")) {
+                       data->no_private_update = 1;
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
                            "helper probably needs newer version of Git.",
@@ -220,6 +224,8 @@ static struct child_process *get_helper(struct transport *transport)
                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)
@@ -483,7 +489,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
@@ -550,7 +556,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)
@@ -632,12 +638,12 @@ 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)
 {
        char *refname, *msg;
-       int status;
+       int status, forced = 0;
 
        if (!prefixcmp(buf->buf, "ok ")) {
                status = REF_STATUS_OK;
@@ -690,6 +696,16 @@ static void push_update_ref_status(struct strbuf *buf,
                        free(msg);
                        msg = NULL;
                }
+               else if (!strcmp(msg, "stale info")) {
+                       status = REF_STATUS_REJECT_STALE;
+                       free(msg);
+                       msg = NULL;
+               }
+               else if (!strcmp(msg, "forced update")) {
+                       forced = 1;
+                       free(msg);
+                       msg = NULL;
+               }
        }
 
        if (*ref)
@@ -698,7 +714,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) {
@@ -707,36 +723,54 @@ 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)->forced_update |= forced;
        (*ref)->remote_status = msg;
+       return !(status == REF_STATUS_OK);
 }
 
 static void push_update_refs_status(struct helper_data *data,
-                                   struct ref *remote_refs)
+                                   struct ref *remote_refs,
+                                   int flags)
 {
        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 (flags & TRANSPORT_PUSH_DRY_RUN || !data->refspecs || data->no_private_update)
+                       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);
 }
 
 static int push_refs_with_push(struct transport *transport,
-               struct ref *remote_refs, int flags)
+                              struct ref *remote_refs, int flags)
 {
        int force_all = flags & TRANSPORT_PUSH_FORCE;
        int mirror = flags & TRANSPORT_PUSH_MIRROR;
        struct helper_data *data = transport->data;
        struct strbuf buf = STRBUF_INIT;
        struct ref *ref;
+       struct string_list cas_options = STRING_LIST_INIT_DUP;
+       struct string_list_item *cas_option;
 
        get_helper(transport);
        if (!data->push)
@@ -749,6 +783,7 @@ static int push_refs_with_push(struct transport *transport,
                /* Check for statuses set by set_ref_status_for_push() */
                switch (ref->status) {
                case REF_STATUS_REJECT_NONFASTFORWARD:
+               case REF_STATUS_REJECT_STALE:
                case REF_STATUS_REJECT_ALREADY_EXISTS:
                case REF_STATUS_UPTODATE:
                        continue;
@@ -771,11 +806,29 @@ static int push_refs_with_push(struct transport *transport,
                strbuf_addch(&buf, ':');
                strbuf_addstr(&buf, ref->name);
                strbuf_addch(&buf, '\n');
+
+               /*
+                * The "--force-with-lease" options without explicit
+                * values to expect have already been expanded into
+                * the ref->old_sha1_expect[] field; we can ignore
+                * transport->smart_options->cas altogether and instead
+                * can enumerate them from the refs.
+                */
+               if (ref->expect_old_sha1) {
+                       struct strbuf cas = STRBUF_INIT;
+                       strbuf_addf(&cas, "%s:%s",
+                                   ref->name, sha1_to_hex(ref->old_sha1_expect));
+                       string_list_append(&cas_options, strbuf_detach(&cas, NULL));
+               }
        }
-       if (buf.len == 0)
+       if (buf.len == 0) {
+               string_list_clear(&cas_options, 0);
                return 0;
+       }
 
        standard_options(transport);
+       for_each_string_list_item(cas_option, &cas_options)
+               set_helper_option(transport, "cas", cas_option->string);
 
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
                if (set_helper_option(transport, "dry-run", "true") != 0)
@@ -786,7 +839,7 @@ static int push_refs_with_push(struct transport *transport,
        sendline(data, &buf);
        strbuf_release(&buf);
 
-       push_update_refs_status(data, remote_refs);
+       push_update_refs_status(data, remote_refs, flags);
        return 0;
 }
 
@@ -799,6 +852,19 @@ 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");
+
+       if (flags & TRANSPORT_PUSH_DRY_RUN) {
+               if (set_helper_option(transport, "dry-run", "true") != 0)
+                       die("helper %s does not support dry-run", data->name);
+       }
+
+       if (flags & TRANSPORT_PUSH_FORCE) {
+               if (set_helper_option(transport, "force", "true") != 0)
+                       warning("helper %s does not support 'force'", data->name);
+       }
+
        helper = get_helper(transport);
 
        write_constant(helper->in, "export\n");
@@ -809,8 +875,9 @@ 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);
@@ -819,13 +886,11 @@ static int push_refs_with_export(struct transport *transport,
                }
                free(private);
 
-               if (ref->deletion) {
-                       die("remote-helpers do not support ref deletion");
-               }
-
-               if (ref->peer_ref)
+               if (ref->peer_ref) {
+                       if (strcmp(ref->peer_ref->name, ref->name))
+                               die("remote-helpers do not support old:new syntax");
                        string_list_append(&revlist_args, ref->peer_ref->name);
-
+               }
        }
 
        if (get_exporter(transport, &exporter, &revlist_args))
@@ -833,7 +898,7 @@ static int push_refs_with_export(struct transport *transport,
 
        if (finish_command(&exporter))
                die("Error while running fast-export");
-       push_update_refs_status(data, remote_refs);
+       push_update_refs_status(data, remote_refs, flags);
        return 0;
 }
 
@@ -965,6 +1030,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 #define PBUFFERSIZE 8192
 
 /* Print bidirectional transfer loop debug message. */
+__attribute__((format (printf, 1, 2)))
 static void transfer_debug(const char *fmt, ...)
 {
        va_list args;
@@ -1050,7 +1116,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
                return -1;
        } else if (bytes == 0) {
                transfer_debug("%s EOF (with %i bytes in buffer)",
-                       t->src_name, t->bufuse);
+                       t->src_name, (int)t->bufuse);
                t->state = SSTATE_FLUSHING;
        } else if (bytes > 0) {
                t->bufuse += bytes;
@@ -1114,7 +1180,7 @@ static void *udt_copy_task_routine(void *udt)
 #ifndef NO_PTHREADS
 
 /*
- * Join thread, with apporiate errors on failure. Name is name for the
+ * Join thread, with appropriate errors on failure. Name is name for the
  * thread (for error messages). Returns 0 on success, 1 on failure.
  */
 static int tloop_join(pthread_t thread, const char *name)
@@ -1180,7 +1246,7 @@ static void udt_kill_transfer(struct unidirectional_transfer *t)
 }
 
 /*
- * Join process, with apporiate errors on failure. Name is name for the
+ * Join process, with appropriate errors on failure. Name is name for the
  * process (for error messages). Returns 0 on success, 1 on failure.
  */
 static int tloop_join(pid_t pid, const char *name)