]> git.ipfire.org Git - thirdparty/git.git/blobdiff - transport-helper.c
Merge branch 'pb/ref-filter-with-crlf'
[thirdparty/git.git] / transport-helper.c
index c52c99d829d739047b867d2f431ab9eda48cb9dc..5f6e0b3bd874dba714c107dffebd423ef25d10b4 100644 (file)
@@ -723,13 +723,61 @@ static int fetch(struct transport *transport,
        return -1;
 }
 
+struct push_update_ref_state {
+       struct ref *hint;
+       struct ref_push_report *report;
+       int new_report;
+};
+
 static int push_update_ref_status(struct strbuf *buf,
-                                  struct ref **ref,
+                                  struct push_update_ref_state *state,
                                   struct ref *remote_refs)
 {
        char *refname, *msg;
        int status, forced = 0;
 
+       if (starts_with(buf->buf, "option ")) {
+               struct object_id old_oid, new_oid;
+               const char *key, *val;
+               char *p;
+
+               if (!state->hint || !(state->report || state->new_report))
+                       die(_("'option' without a matching 'ok/error' directive"));
+               if (state->new_report) {
+                       if (!state->hint->report) {
+                               state->hint->report = xcalloc(1, sizeof(struct ref_push_report));
+                               state->report = state->hint->report;
+                       } else {
+                               state->report = state->hint->report;
+                               while (state->report->next)
+                                       state->report = state->report->next;
+                               state->report->next = xcalloc(1, sizeof(struct ref_push_report));
+                               state->report = state->report->next;
+                       }
+                       state->new_report = 0;
+               }
+               key = buf->buf + 7;
+               p = strchr(key, ' ');
+               if (p)
+                       *p++ = '\0';
+               val = p;
+               if (!strcmp(key, "refname"))
+                       state->report->ref_name = xstrdup_or_null(val);
+               else if (!strcmp(key, "old-oid") && val &&
+                        !parse_oid_hex(val, &old_oid, &val))
+                       state->report->old_oid = oiddup(&old_oid);
+               else if (!strcmp(key, "new-oid") && val &&
+                        !parse_oid_hex(val, &new_oid, &val))
+                       state->report->new_oid = oiddup(&new_oid);
+               else if (!strcmp(key, "forced-update"))
+                       state->report->forced_update = 1;
+               /* Not update remote namespace again. */
+               return 1;
+       }
+
+       state->report = NULL;
+       state->new_report = 0;
+
        if (starts_with(buf->buf, "ok ")) {
                status = REF_STATUS_OK;
                refname = buf->buf + 3;
@@ -779,22 +827,26 @@ static int push_update_ref_status(struct strbuf *buf,
                        status = REF_STATUS_REJECT_STALE;
                        FREE_AND_NULL(msg);
                }
+               else if (!strcmp(msg, "remote ref updated since checkout")) {
+                       status = REF_STATUS_REJECT_REMOTE_UPDATED;
+                       FREE_AND_NULL(msg);
+               }
                else if (!strcmp(msg, "forced update")) {
                        forced = 1;
                        FREE_AND_NULL(msg);
                }
        }
 
-       if (*ref)
-               *ref = find_ref_by_name(*ref, refname);
-       if (!*ref)
-               *ref = find_ref_by_name(remote_refs, refname);
-       if (!*ref) {
+       if (state->hint)
+               state->hint = find_ref_by_name(state->hint, refname);
+       if (!state->hint)
+               state->hint = find_ref_by_name(remote_refs, refname);
+       if (!state->hint) {
                warning(_("helper reported unexpected status of %s"), refname);
                return 1;
        }
 
-       if ((*ref)->status != REF_STATUS_NONE) {
+       if (state->hint->status != REF_STATUS_NONE) {
                /*
                 * Earlier, the ref was marked not to be pushed, so ignore the ref
                 * status reported by the remote helper if the latter is 'no match'.
@@ -803,9 +855,11 @@ static int push_update_ref_status(struct strbuf *buf,
                        return 1;
        }
 
-       (*ref)->status = status;
-       (*ref)->forced_update |= forced;
-       (*ref)->remote_status = msg;
+       if (status == REF_STATUS_OK)
+               state->new_report = 1;
+       state->hint->status = status;
+       state->hint->forced_update |= forced;
+       state->hint->remote_status = msg;
        return !(status == REF_STATUS_OK);
 }
 
@@ -813,37 +867,57 @@ static int push_update_refs_status(struct helper_data *data,
                                    struct ref *remote_refs,
                                    int flags)
 {
+       struct ref *ref;
+       struct ref_push_report *report;
        struct strbuf buf = STRBUF_INIT;
-       struct ref *ref = remote_refs;
-       int ret = 0;
+       struct push_update_ref_state state = { remote_refs, NULL, 0 };
 
        for (;;) {
-               char *private;
-
                if (recvline(data, &buf)) {
-                       ret = 1;
-                       break;
+                       strbuf_release(&buf);
+                       return 1;
                }
-
                if (!buf.len)
                        break;
+               push_update_ref_status(&buf, &state, remote_refs);
+       }
+       strbuf_release(&buf);
 
-               if (push_update_ref_status(&buf, &ref, remote_refs))
-                       continue;
+       if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
+               return 0;
 
-               if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
-                       continue;
+       /* propagate back the update to the remote namespace */
+       for (ref = remote_refs; ref; ref = ref->next) {
+               char *private;
 
-               /* propagate back the update to the remote namespace */
-               private = apply_refspecs(&data->rs, ref->name);
-               if (!private)
+               if (ref->status != REF_STATUS_OK)
                        continue;
-               update_ref("update by helper", private, &ref->new_oid, NULL,
-                          0, 0);
-               free(private);
+
+               if (!ref->report) {
+                       private = apply_refspecs(&data->rs, ref->name);
+                       if (!private)
+                               continue;
+                       update_ref("update by helper", private, &(ref->new_oid),
+                                  NULL, 0, 0);
+                       free(private);
+               } else {
+                       for (report = ref->report; report; report = report->next) {
+                               private = apply_refspecs(&data->rs,
+                                                        report->ref_name
+                                                        ? report->ref_name
+                                                        : ref->name);
+                               if (!private)
+                                       continue;
+                               update_ref("update by helper", private,
+                                          report->new_oid
+                                          ? report->new_oid
+                                          : &(ref->new_oid),
+                                          NULL, 0, 0);
+                               free(private);
+                       }
+               }
        }
-       strbuf_release(&buf);
-       return ret;
+       return 0;
 }
 
 static void set_common_push_options(struct transport *transport,
@@ -864,6 +938,11 @@ static void set_common_push_options(struct transport *transport,
                if (set_helper_option(transport, TRANS_OPT_ATOMIC, "true") != 0)
                        die(_("helper %s does not support --atomic"), name);
 
+       if (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES)
+               if (set_helper_option(transport, TRANS_OPT_FORCE_IF_INCLUDES, "true") != 0)
+                       die(_("helper %s does not support --%s"),
+                           name, TRANS_OPT_FORCE_IF_INCLUDES);
+
        if (flags & TRANSPORT_PUSH_OPTIONS) {
                struct string_list_item *item;
                for_each_string_list_item(item, transport->push_options)
@@ -897,6 +976,7 @@ static int push_refs_with_push(struct transport *transport,
                case REF_STATUS_REJECT_NONFASTFORWARD:
                case REF_STATUS_REJECT_STALE:
                case REF_STATUS_REJECT_ALREADY_EXISTS:
+               case REF_STATUS_REJECT_REMOTE_UPDATED:
                        if (atomic) {
                                reject_atomic_push(remote_refs, mirror);
                                string_list_clear(&cas_options, 0);