Merge branch 'jk/t5562-perl-path-fix'
[gitweb.git] / builtin / push.c
index 2ac81042292ef1852ec9a31dea34ec91a1b796e3..d09a42062c36d930b8147d6b0a4bcfedf34a2321 100644 (file)
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "config.h"
 #include "refs.h"
+#include "refspec.h"
 #include "run-command.h"
 #include "builtin.h"
 #include "remote.h"
 #include "submodule.h"
 #include "submodule-config.h"
 #include "send-pack.h"
+#include "color.h"
 
 static const char * const push_usage[] = {
        N_("git push [<options>] [<repository> [<refspec>...]]"),
        NULL,
 };
 
+static int push_use_color = -1;
+static char push_colors[][COLOR_MAXLEN] = {
+       GIT_COLOR_RESET,
+       GIT_COLOR_RED,  /* ERROR */
+};
+
+enum color_push {
+       PUSH_COLOR_RESET = 0,
+       PUSH_COLOR_ERROR = 1
+};
+
+static int parse_push_color_slot(const char *slot)
+{
+       if (!strcasecmp(slot, "reset"))
+               return PUSH_COLOR_RESET;
+       if (!strcasecmp(slot, "error"))
+               return PUSH_COLOR_ERROR;
+       return -1;
+}
+
+static const char *push_get_color(enum color_push ix)
+{
+       if (want_color_stderr(push_use_color))
+               return push_colors[ix];
+       return "";
+}
+
 static int thin = 1;
 static int deleterefs;
 static const char *receivepack;
@@ -28,16 +57,9 @@ static enum transport_family family;
 
 static struct push_cas_option cas;
 
-static const char **refspec;
-static int refspec_nr;
-static int refspec_alloc;
+static struct refspec rs = REFSPEC_INIT_PUSH;
 
-static void add_refspec(const char *ref)
-{
-       refspec_nr++;
-       ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
-       refspec[refspec_nr-1] = ref;
-}
+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)
@@ -48,12 +70,11 @@ static const char *map_refspec(const char *ref,
        if (count_refspec_match(ref, local_refs, &matched) != 1)
                return ref;
 
-       if (remote->push) {
-               struct refspec query;
-               memset(&query, 0, sizeof(struct refspec));
+       if (remote->push.nr) {
+               struct refspec_item query;
+               memset(&query, 0, sizeof(struct refspec_item));
                query.src = matched->name;
-               if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
-                   query.dst) {
+               if (!query_refspecs(&remote->push, &query) && query.dst) {
                        struct strbuf buf = STRBUF_INIT;
                        strbuf_addf(&buf, "%s%s:%s",
                                    query.force ? "+" : "",
@@ -108,7 +129,7 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
                        }
                        ref = map_refspec(ref, remote, local_refs);
                }
-               add_refspec(ref);
+               refspec_append(&rs, ref);
        }
 }
 
@@ -196,7 +217,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
        }
 
        strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src);
-       add_refspec(refspec.buf);
+       refspec_append(&rs, refspec.buf);
 }
 
 static void setup_push_current(struct remote *remote, struct branch *branch)
@@ -206,7 +227,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch)
        if (!branch)
                die(_(message_detached_head_die), remote->name);
        strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname);
-       add_refspec(refspec.buf);
+       refspec_append(&rs, refspec.buf);
 }
 
 static int is_workflow_triangular(struct remote *remote)
@@ -223,7 +244,7 @@ static void setup_default_push_refspecs(struct remote *remote)
        switch (push_default) {
        default:
        case PUSH_DEFAULT_MATCHING:
-               add_refspec(":");
+               refspec_append(&rs, ":");
                break;
 
        case PUSH_DEFAULT_UNSPECIFIED:
@@ -311,7 +332,8 @@ static void advise_ref_needs_force(void)
        advise(_(message_advice_ref_needs_force));
 }
 
-static int push_with_options(struct transport *transport, int flags)
+static int push_with_options(struct transport *transport, struct refspec *rs,
+                            int flags)
 {
        int err;
        unsigned int reject_reasons;
@@ -333,10 +355,12 @@ static int push_with_options(struct transport *transport, int flags)
 
        if (verbosity > 0)
                fprintf(stderr, _("Pushing to %s\n"), transport->url);
-       err = transport_push(transport, refspec_nr, refspec, flags,
-                            &reject_reasons);
-       if (err != 0)
+       err = transport_push(transport, rs, flags, &reject_reasons);
+       if (err != 0) {
+               fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
                error(_("failed to push some refs to '%s'"), transport->url);
+               fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET));
+       }
 
        err |= transport_disconnect(transport);
        if (!err)
@@ -364,6 +388,7 @@ static int do_push(const char *repo, int flags,
        struct remote *remote = pushremote_get(repo);
        const char **url;
        int url_nr;
+       struct refspec *push_refspec = &rs;
 
        if (!remote) {
                if (repo)
@@ -384,27 +409,9 @@ static int do_push(const char *repo, int flags,
        if (push_options->nr)
                flags |= TRANSPORT_PUSH_OPTIONS;
 
-       if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
-               if (!strcmp(*refspec, "refs/tags/*"))
-                       return error(_("--all and --tags are incompatible"));
-               return error(_("--all can't be combined with refspecs"));
-       }
-
-       if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
-               if (!strcmp(*refspec, "refs/tags/*"))
-                       return error(_("--mirror and --tags are incompatible"));
-               return error(_("--mirror can't be combined with refspecs"));
-       }
-
-       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
-                               (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
-               return error(_("--all and --mirror are incompatible"));
-       }
-
-       if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
-               if (remote->push_refspec_nr) {
-                       refspec = remote->push_refspec;
-                       refspec_nr = remote->push_refspec_nr;
+       if (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) {
+               if (remote->push.nr) {
+                       push_refspec = &remote->push;
                } else if (!(flags & TRANSPORT_PUSH_MIRROR))
                        setup_default_push_refspecs(remote);
        }
@@ -416,7 +423,7 @@ static int do_push(const char *repo, int flags,
                                transport_get(remote, url[i]);
                        if (flags & TRANSPORT_PUSH_OPTIONS)
                                transport->push_options = push_options;
-                       if (push_with_options(transport, flags))
+                       if (push_with_options(transport, push_refspec, flags))
                                errs++;
                }
        } else {
@@ -424,7 +431,7 @@ static int do_push(const char *repo, int flags,
                        transport_get(remote, NULL);
                if (flags & TRANSPORT_PUSH_OPTIONS)
                        transport->push_options = push_options;
-               if (push_with_options(transport, flags))
+               if (push_with_options(transport, push_refspec, flags))
                        errs++;
        }
        return !!errs;
@@ -465,6 +472,7 @@ static void set_push_cert_flags(int *flags, int v)
 
 static int git_push_config(const char *k, const char *v, void *cb)
 {
+       const char *slot_name;
        int *flags = cb;
        int status;
 
@@ -503,6 +511,25 @@ static int git_push_config(const char *k, const char *v, void *cb)
                int val = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
                recurse_submodules = val;
+       } else if (!strcmp(k, "push.pushoption")) {
+               if (!v)
+                       return config_error_nonbool(k);
+               else
+                       if (!*v)
+                               string_list_clear(&push_options_config, 0);
+                       else
+                               string_list_append(&push_options_config, v);
+               return 0;
+       } else if (!strcmp(k, "color.push")) {
+               push_use_color = git_config_colorbool(k, v);
+               return 0;
+       } else if (skip_prefix(k, "color.push.", &slot_name)) {
+               int slot = parse_push_color_slot(slot_name);
+               if (slot < 0)
+                       return 0;
+               if (!v)
+                       return config_error_nonbool(k);
+               return color_parse(v, push_colors[slot]);
        }
 
        return git_default_config(k, v, NULL);
@@ -515,7 +542,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int push_cert = -1;
        int rc;
        const char *repo = NULL;        /* default repository */
-       struct string_list push_options = STRING_LIST_INIT_DUP;
+       struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
+       struct string_list *push_options;
        const struct string_list_item *item;
 
        struct option options[] = {
@@ -530,13 +558,13 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
                OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
                { OPTION_CALLBACK,
-                 0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
+                 0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
                  N_("require old value of ref to be at this value"),
-                 PARSE_OPT_OPTARG, parseopt_push_cas_option },
-               { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "check|on-demand|no",
+                 PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option },
+               { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)",
                        N_("control recursive pushing of submodules"),
                        PARSE_OPT_OPTARG, option_parse_recurse_submodules },
-               OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")),
+               OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE),
                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")),
                OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"),
@@ -548,10 +576,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
                        TRANSPORT_PUSH_FOLLOW_TAGS),
                { OPTION_CALLBACK,
-                 0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
+                 0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
                  PARSE_OPT_OPTARG, option_parse_push_signed },
                OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
-               OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
+               OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
                OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
                                TRANSPORT_FAMILY_IPV4),
                OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -562,12 +590,29 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        packet_trace_identity("push");
        git_config(git_push_config, &flags);
        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+       push_options = (push_options_cmdline.nr
+               ? &push_options_cmdline
+               : &push_options_config);
        set_push_cert_flags(&flags, push_cert);
 
        if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
                die(_("--delete is incompatible with --all, --mirror and --tags"));
        if (deleterefs && argc < 2)
                die(_("--delete doesn't make sense without any refs"));
+       if (flags & TRANSPORT_PUSH_ALL) {
+               if (tags)
+                       die(_("--all and --tags are incompatible"));
+               if (argc >= 2)
+                       die(_("--all can't be combined with refspecs"));
+       }
+       if (flags & TRANSPORT_PUSH_MIRROR) {
+               if (tags)
+                       die(_("--mirror and --tags are incompatible"));
+               if (argc >= 2)
+                       die(_("--mirror can't be combined with refspecs"));
+       }
+       if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
+               die(_("--all and --mirror are incompatible"));
 
        if (recurse_submodules == RECURSE_SUBMODULES_CHECK)
                flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
@@ -577,19 +622,20 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                flags |= TRANSPORT_RECURSE_SUBMODULES_ONLY;
 
        if (tags)
-               add_refspec("refs/tags/*");
+               refspec_append(&rs, "refs/tags/*");
 
        if (argc > 0) {
                repo = argv[0];
                set_refspecs(argv + 1, argc - 1, repo);
        }
 
-       for_each_string_list_item(item, &push_options)
+       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);
-       string_list_clear(&push_options, 0);
+       rc = do_push(repo, flags, push_options);
+       string_list_clear(&push_options_cmdline, 0);
+       string_list_clear(&push_options_config, 0);
        if (rc == -1)
                usage_with_options(push_usage, options);
        else