]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/push.c
Merge branch 'en/ort-perf-batch-9'
[thirdparty/git.git] / builtin / push.c
index bc94078e72f8e57aaed7d6a4d2ec579b2499a1d4..194967ed79dc99fe37720ffff012bf40275ff29d 100644 (file)
@@ -61,26 +61,27 @@ static struct refspec rs = REFSPEC_INIT_PUSH;
 
 static struct string_list push_options_config = STRING_LIST_INIT_DUP;
 
-static const char *map_refspec(const char *ref,
-                              struct remote *remote, struct ref *local_refs)
+static void refspec_append_mapped(struct refspec *refspec, const char *ref,
+                                 struct remote *remote, struct ref *local_refs)
 {
        const char *branch_name;
        struct ref *matched = NULL;
 
        /* Does "ref" uniquely name our ref? */
-       if (count_refspec_match(ref, local_refs, &matched) != 1)
-               return ref;
+       if (count_refspec_match(ref, local_refs, &matched) != 1) {
+               refspec_append(refspec, ref);
+               return;
+       }
 
        if (remote->push.nr) {
                struct refspec_item query;
                memset(&query, 0, sizeof(struct refspec_item));
                query.src = matched->name;
                if (!query_refspecs(&remote->push, &query) && query.dst) {
-                       struct strbuf buf = STRBUF_INIT;
-                       strbuf_addf(&buf, "%s%s:%s",
-                                   query.force ? "+" : "",
-                                   query.src, query.dst);
-                       return strbuf_detach(&buf, NULL);
+                       refspec_appendf(refspec, "%s%s:%s",
+                                       query.force ? "+" : "",
+                                       query.src, query.dst);
+                       return;
                }
        }
 
@@ -88,14 +89,13 @@ static const char *map_refspec(const char *ref,
            skip_prefix(matched->name, "refs/heads/", &branch_name)) {
                struct branch *branch = branch_get(branch_name);
                if (branch->merge_nr == 1 && branch->merge[0]->src) {
-                       struct strbuf buf = STRBUF_INIT;
-                       strbuf_addf(&buf, "%s:%s",
-                                   ref, branch->merge[0]->src);
-                       return strbuf_detach(&buf, NULL);
+                       refspec_appendf(refspec, "%s:%s",
+                                       ref, branch->merge[0]->src);
+                       return;
                }
        }
 
-       return ref;
+       refspec_append(refspec, ref);
 }
 
 static void set_refspecs(const char **refs, int nr, const char *repo)
@@ -107,30 +107,26 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
        for (i = 0; i < nr; i++) {
                const char *ref = refs[i];
                if (!strcmp("tag", ref)) {
-                       struct strbuf tagref = STRBUF_INIT;
                        if (nr <= ++i)
                                die(_("tag shorthand without <tag>"));
                        ref = refs[i];
                        if (deleterefs)
-                               strbuf_addf(&tagref, ":refs/tags/%s", ref);
+                               refspec_appendf(&rs, ":refs/tags/%s", ref);
                        else
-                               strbuf_addf(&tagref, "refs/tags/%s", ref);
-                       ref = strbuf_detach(&tagref, NULL);
+                               refspec_appendf(&rs, "refs/tags/%s", ref);
                } else if (deleterefs) {
-                       struct strbuf delref = STRBUF_INIT;
-                       if (strchr(ref, ':'))
+                       if (strchr(ref, ':') || !*ref)
                                die(_("--delete only accepts plain target ref names"));
-                       strbuf_addf(&delref, ":%s", ref);
-                       ref = strbuf_detach(&delref, NULL);
+                       refspec_appendf(&rs, ":%s", ref);
                } else if (!strchr(ref, ':')) {
                        if (!remote) {
                                /* lazily grab remote and local_refs */
                                remote = remote_get(repo);
                                local_refs = get_local_heads();
                        }
-                       ref = map_refspec(ref, remote, local_refs);
-               }
-               refspec_append(&rs, ref);
+                       refspec_append_mapped(&rs, ref, remote, local_refs);
+               } else
+                       refspec_append(&rs, ref);
        }
 }
 
@@ -192,8 +188,6 @@ static const char message_detached_head_die[] =
 static void setup_push_upstream(struct remote *remote, struct branch *branch,
                                int triangular, int simple)
 {
-       struct strbuf refspec = STRBUF_INIT;
-
        if (!branch)
                die(_(message_detached_head_die), remote->name);
        if (!branch->merge_nr || !branch->merge || !branch->remote_name)
@@ -219,18 +213,14 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
                        die_push_simple(branch, remote);
        }
 
-       strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src);
-       refspec_append(&rs, refspec.buf);
+       refspec_appendf(&rs, "%s:%s", branch->refname, branch->merge[0]->src);
 }
 
 static void setup_push_current(struct remote *remote, struct branch *branch)
 {
-       struct strbuf refspec = STRBUF_INIT;
-
        if (!branch)
                die(_(message_detached_head_die), remote->name);
-       strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname);
-       refspec_append(&rs, refspec.buf);
+       refspec_appendf(&rs, "%s:%s", branch->refname, branch->refname);
 }
 
 static int is_workflow_triangular(struct remote *remote)
@@ -300,6 +290,12 @@ static const char message_advice_ref_needs_force[] =
           "or update a remote ref to make it point at a non-commit object,\n"
           "without using the '--force' option.\n");
 
+static const char message_advice_ref_needs_update[] =
+       N_("Updates were rejected because the tip of the remote-tracking\n"
+          "branch has been updated since the last checkout. You may want\n"
+          "to integrate those changes locally (e.g., 'git pull ...')\n"
+          "before forcing an update.\n");
+
 static void advise_pull_before_push(void)
 {
        if (!advice_push_non_ff_current || !advice_push_update_rejected)
@@ -335,6 +331,13 @@ static void advise_ref_needs_force(void)
        advise(_(message_advice_ref_needs_force));
 }
 
+static void advise_ref_needs_update(void)
+{
+       if (!advice_push_ref_needs_update || !advice_push_update_rejected)
+               return;
+       advise(_(message_advice_ref_needs_update));
+}
+
 static int push_with_options(struct transport *transport, struct refspec *rs,
                             int flags)
 {
@@ -384,12 +387,14 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
                advise_ref_fetch_first();
        } else if (reject_reasons & REJECT_NEEDS_FORCE) {
                advise_ref_needs_force();
+       } else if (reject_reasons & REJECT_REF_NEEDS_UPDATE) {
+               advise_ref_needs_update();
        }
 
        return 1;
 }
 
-static int do_push(const char *repo, int flags,
+static int do_push(int flags,
                   const struct string_list *push_options,
                   struct remote *remote)
 {
@@ -520,6 +525,12 @@ static int git_push_config(const char *k, const char *v, void *cb)
                if (!v)
                        return config_error_nonbool(k);
                return color_parse(v, push_colors[slot]);
+       } else if (!strcmp(k, "push.useforceifincludes")) {
+               if (git_config_bool(k, v))
+                       *flags |= TRANSPORT_PUSH_FORCE_IF_INCLUDES;
+               else
+                       *flags &= ~TRANSPORT_PUSH_FORCE_IF_INCLUDES;
+               return 0;
        }
 
        return git_default_config(k, v, NULL);
@@ -551,6 +562,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
                               N_("require old value of ref to be at this value"),
                               PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option),
+               OPT_BIT(0, TRANS_OPT_FORCE_IF_INCLUDES, &flags,
+                       N_("require remote updates to be integrated locally"),
+                       TRANSPORT_PUSH_FORCE_IF_INCLUDES),
                OPT_CALLBACK(0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)",
                             N_("control recursive pushing of submodules"), option_parse_recurse_submodules),
                OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE),
@@ -635,11 +649,14 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
                die(_("--all and --mirror are incompatible"));
 
+       if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
+               cas.use_force_if_includes = 1;
+
        for_each_string_list_item(item, push_options)
                if (strchr(item->string, '\n'))
                        die(_("push options must not have new line characters"));
 
-       rc = do_push(repo, flags, push_options, remote);
+       rc = do_push(flags, push_options, remote);
        string_list_clear(&push_options_cmdline, 0);
        string_list_clear(&push_options_config, 0);
        if (rc == -1)