push: colorize errors

This is an attempt to resolve an issue I experience with people that are new to Git -- especially colleagues in a team setting -- where they miss that their push to a remote location failed because the failure and success both return a block of white text. An example is if I push something to a remote repository and then a colleague attempts to push to the same remote repository and the push fails because it requires them to pull first, but they don't notice because a success and failure both return a block of white text. They then continue about their business, thinking it has been successfully pushed. This patch colorizes the errors and hints (in red and yellow, respectively) so whenever there is a failure when pushing to a remote repository that fails, it is more noticeable. [jes: fixed a couple bugs, added the color.{advice,push,transport} settings, refactored to use want_color_stderr().] Signed-off-by: Ryan Dammrose ryandammrose@gmail.com Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ryan Dammrose committed Apr 21, 2018 at 12:10 UTC 960786e7618942357c45e7e4c234f113e0aea9b1
4 files changed +157 -5
advice.c
+47 -2
@@ -1,5 +1,6 @@
1 #include "cache.h"
2 #include "config.h"
3 +#include "color.h"
4
5 int advice_push_update_rejected = 1;
6 int advice_push_non_ff_current = 1;
@@ -20,6 +21,33 @@ int advice_add_embedded_repo = 1;
21 int advice_ignored_hook = 1;
22 int advice_waiting_for_editor = 1;
23
24 +static int advice_use_color = -1;
25 +static char advice_colors[][COLOR_MAXLEN] = {
26 + GIT_COLOR_RESET,
27 + GIT_COLOR_YELLOW, /* HINT */
28 +};
29 +
30 +enum color_advice {
31 + ADVICE_COLOR_RESET = 0,
32 + ADVICE_COLOR_HINT = 1,
33 +};
34 +
35 +static int parse_advise_color_slot(const char *slot)
36 +{
37 + if (!strcasecmp(slot, "reset"))
38 + return ADVICE_COLOR_RESET;
39 + if (!strcasecmp(slot, "hint"))
40 + return ADVICE_COLOR_HINT;
41 + return -1;
42 +}
43 +
44 +static const char *advise_get_color(enum color_advice ix)
45 +{
46 + if (want_color_stderr(advice_use_color))
47 + return advice_colors[ix];
48 + return "";
49 +}
50 +
51 static struct {
52 const char *name;
53 int *preference;
@@ -59,7 +87,10 @@ void advise(const char *advice, ...)
87
88 for (cp = buf.buf; *cp; cp = np) {
89 np = strchrnul(cp, '\n');
62 - fprintf(stderr, _("hint: %.*s\n"), (int)(np - cp), cp);
90 + fprintf(stderr, _("%shint: %.*s%s\n"),
91 + advise_get_color(ADVICE_COLOR_HINT),
92 + (int)(np - cp), cp,
93 + advise_get_color(ADVICE_COLOR_RESET));
94 if (*np)
95 np++;
96 }
@@ -68,9 +99,23 @@ void advise(const char *advice, ...)
99
100 int git_default_advice_config(const char *var, const char *value)
101 {
71 - const char *k;
102 + const char *k, *slot_name;
103 int i;
104
105 + if (!strcmp(var, "color.advice")) {
106 + advice_use_color = git_config_colorbool(var, value);
107 + return 0;
108 + }
109 +
110 + if (skip_prefix(var, "color.advice.", &slot_name)) {
111 + int slot = parse_advise_color_slot(slot_name);
112 + if (slot < 0)
113 + return 0;
114 + if (!value)
115 + return config_error_nonbool(var);
116 + return color_parse(value, advice_colors[slot]);
117 + }
118 +
119 if (!skip_prefix(var, "advice.", &k))
120 return 0;
121
builtin/push.c
+43 -1
@@ -12,12 +12,40 @@
12 #include "submodule.h"
13 #include "submodule-config.h"
14 #include "send-pack.h"
15 +#include "color.h"
16
17 static const char * const push_usage[] = {
18 N_("git push [<options>] [<repository> [<refspec>...]]"),
19 NULL,
20 };
21
22 +static int push_use_color = -1;
23 +static char push_colors[][COLOR_MAXLEN] = {
24 + GIT_COLOR_RESET,
25 + GIT_COLOR_RED, /* ERROR */
26 +};
27 +
28 +enum color_push {
29 + PUSH_COLOR_RESET = 0,
30 + PUSH_COLOR_ERROR = 1
31 +};
32 +
33 +static int parse_push_color_slot(const char *slot)
34 +{
35 + if (!strcasecmp(slot, "reset"))
36 + return PUSH_COLOR_RESET;
37 + if (!strcasecmp(slot, "error"))
38 + return PUSH_COLOR_ERROR;
39 + return -1;
40 +}
41 +
42 +static const char *push_get_color(enum color_push ix)
43 +{
44 + if (want_color_stderr(push_use_color))
45 + return push_colors[ix];
46 + return "";
47 +}
48 +
49 static int thin = 1;
50 static int deleterefs;
51 static const char *receivepack;
@@ -337,8 +365,11 @@ static int push_with_options(struct transport *transport, int flags)
365 fprintf(stderr, _("Pushing to %s\n"), transport->url);
366 err = transport_push(transport, refspec_nr, refspec, flags,
367 &reject_reasons);
340 - if (err != 0)
368 + if (err != 0) {
369 + fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
370 error(_("failed to push some refs to '%s'"), transport->url);
371 + fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET));
372 + }
373
374 err |= transport_disconnect(transport);
375 if (!err)
@@ -467,6 +498,7 @@ static void set_push_cert_flags(int *flags, int v)
498
499 static int git_push_config(const char *k, const char *v, void *cb)
500 {
501 + const char *slot_name;
502 int *flags = cb;
503 int status;
504
@@ -514,6 +546,16 @@ static int git_push_config(const char *k, const char *v, void *cb)
546 else
547 string_list_append(&push_options_config, v);
548 return 0;
549 + } else if (!strcmp(k, "color.push")) {
550 + push_use_color = git_config_colorbool(k, v);
551 + return 0;
552 + } else if (skip_prefix(k, "color.push.", &slot_name)) {
553 + int slot = parse_push_color_slot(slot_name);
554 + if (slot < 0)
555 + return 0;
556 + if (!v)
557 + return config_error_nonbool(k);
558 + return color_parse(v, push_colors[slot]);
559 }
560
561 return git_default_config(k, v, NULL);
config.c
+1 -1
@@ -1365,7 +1365,7 @@ int git_default_config(const char *var, const char *value, void *dummy)
1365 if (starts_with(var, "mailmap."))
1366 return git_default_mailmap_config(var, value);
1367
1368 - if (starts_with(var, "advice."))
1368 + if (starts_with(var, "advice.") || starts_with(var, "color.advice"))
1369 return git_default_advice_config(var, value);
1370
1371 if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
transport.c
+66 -1
@@ -19,6 +19,56 @@
19 #include "sigchain.h"
20 #include "transport-internal.h"
21 #include "object-store.h"
22 +#include "color.h"
23 +
24 +static int transport_use_color = -1;
25 +static char transport_colors[][COLOR_MAXLEN] = {
26 + GIT_COLOR_RESET,
27 + GIT_COLOR_RED /* REJECTED */
28 +};
29 +
30 +enum color_transport {
31 + TRANSPORT_COLOR_RESET = 0,
32 + TRANSPORT_COLOR_REJECTED = 1
33 +};
34 +
35 +static int transport_color_config(void)
36 +{
37 + const char *keys[] = {
38 + "color.transport.reset",
39 + "color.transport.rejected"
40 + }, *key = "color.transport";
41 + char *value;
42 + int i;
43 + static int initialized;
44 +
45 + if (initialized)
46 + return 0;
47 + initialized = 1;
48 +
49 + if (!git_config_get_string(key, &value))
50 + transport_use_color = git_config_colorbool(key, value);
51 +
52 + if (!want_color_stderr(transport_use_color))
53 + return 0;
54 +
55 + for (i = 0; i < ARRAY_SIZE(keys); i++)
56 + if (!git_config_get_string(keys[i], &value)) {
57 + if (!value)
58 + return config_error_nonbool(keys[i]);
59 + if (color_parse(value, transport_colors[i]) < 0)
60 + return -1;
61 + }
62 +
63 + return 0;
64 +}
65 +
66 +static const char *transport_get_color(enum color_transport ix)
67 +{
68 + if (want_color_stderr(transport_use_color))
69 + return transport_colors[ix];
70 + return "";
71 +}
72
73 static void set_upstreams(struct transport *transport, struct ref *refs,
74 int pretend)
@@ -339,7 +389,13 @@ static void print_ref_status(char flag, const char *summary,
389 else
390 fprintf(stdout, "%s\n", summary);
391 } else {
342 - fprintf(stderr, " %c %-*s ", flag, summary_width, summary);
392 + const char *red = "", *reset = "";
393 + if (push_had_errors(to)) {
394 + red = transport_get_color(TRANSPORT_COLOR_REJECTED);
395 + reset = transport_get_color(TRANSPORT_COLOR_RESET);
396 + }
397 + fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width,
398 + summary, reset);
399 if (from)
400 fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
401 else
@@ -488,6 +544,9 @@ void transport_print_push_status(const char *dest, struct ref *refs,
544 char *head;
545 int summary_width = transport_summary_width(refs);
546
547 + if (transport_color_config() < 0)
548 + warning(_("could not parse transport.color.* config"));
549 +
550 head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
551
552 if (verbose) {
@@ -554,6 +613,9 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
613 struct send_pack_args args;
614 int ret;
615
616 + if (transport_color_config() < 0)
617 + return -1;
618 +
619 if (!data->got_remote_heads) {
620 struct ref *tmp_refs;
621 connect_setup(transport, 1);
@@ -998,6 +1060,9 @@ int transport_push(struct transport *transport,
1060 *reject_reasons = 0;
1061 transport_verify_remote_names(refspec_nr, refspec);
1062
1063 + if (transport_color_config() < 0)
1064 + return -1;
1065 +
1066 if (transport->vtable->push_refs) {
1067 struct ref *remote_refs;
1068 struct ref *local_refs = get_local_heads();