Skip to content

Commit e48ea7f

Browse files
committed
Merge branch 'colorize-push-errors'
To help users discern large chunks of white text (when the push succeeds) from large chunks of white text (when the push fails), let's add some color to the latter. This closes #1429 and fixes #1422 Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 0dba629 + 25d9816 commit e48ea7f

File tree

8 files changed

+211
-15
lines changed

8 files changed

+211
-15
lines changed

Documentation/config.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,16 @@ clean.requireForce::
11091109
A boolean to make git-clean do nothing unless given -f,
11101110
-i or -n. Defaults to true.
11111111

1112+
color.advice::
1113+
A boolean to enable/disable color in hints (e.g. when a push
1114+
failed, see `advice.*` for a list). May be set to `always`,
1115+
`false` (or `never`) or `auto` (or `true`), in which case colors
1116+
are used only when the error output goes to a terminal. If
1117+
unset, then the value of `color.ui` is used (`auto` by default).
1118+
1119+
color.advice.hint::
1120+
Use customized color for hints.
1121+
11121122
color.branch::
11131123
A boolean to enable/disable color in the output of
11141124
linkgit:git-branch[1]. May be set to `always`,
@@ -1211,6 +1221,15 @@ color.pager::
12111221
A boolean to enable/disable colored output when the pager is in
12121222
use (default is true).
12131223

1224+
color.push::
1225+
A boolean to enable/disable color in push errors. May be set to
1226+
`always`, `false` (or `never`) or `auto` (or `true`), in which
1227+
case colors are used only when the error output goes to a terminal.
1228+
If unset, then the value of `color.ui` is used (`auto` by default).
1229+
1230+
color.push.error::
1231+
Use customized color for push errors.
1232+
12141233
color.showBranch::
12151234
A boolean to enable/disable color in the output of
12161235
linkgit:git-show-branch[1]. May be set to `always`,
@@ -1239,6 +1258,15 @@ color.status.<slot>::
12391258
status short-format), or
12401259
`unmerged` (files which have unmerged changes).
12411260

1261+
color.transport::
1262+
A boolean to enable/disable color when pushes are rejected. May be
1263+
set to `always`, `false` (or `never`) or `auto` (or `true`), in which
1264+
case colors are used only when the error output goes to a terminal.
1265+
If unset, then the value of `color.ui` is used (`auto` by default).
1266+
1267+
color.transport.rejected::
1268+
Use customized color when a push was rejected.
1269+
12421270
color.ui::
12431271
This variable determines the default value for variables such
12441272
as `color.diff` and `color.grep` that control the use of color

advice.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "cache.h"
22
#include "config.h"
3+
#include "color.h"
34

45
int advice_push_update_rejected = 1;
56
int advice_push_non_ff_current = 1;
@@ -20,6 +21,33 @@ int advice_add_embedded_repo = 1;
2021
int advice_ignored_hook = 1;
2122
int advice_waiting_for_editor = 1;
2223

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+
2351
static struct {
2452
const char *name;
2553
int *preference;
@@ -59,7 +87,10 @@ void advise(const char *advice, ...)
5987

6088
for (cp = buf.buf; *cp; cp = np) {
6189
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));
6394
if (*np)
6495
np++;
6596
}
@@ -68,9 +99,23 @@ void advise(const char *advice, ...)
6899

69100
int git_default_advice_config(const char *var, const char *value)
70101
{
71-
const char *k;
102+
const char *k, *slot_name;
72103
int i;
73104

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+
74119
if (!skip_prefix(var, "advice.", &k))
75120
return 0;
76121

builtin/push.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,40 @@
1212
#include "submodule.h"
1313
#include "submodule-config.h"
1414
#include "send-pack.h"
15+
#include "color.h"
1516

1617
static const char * const push_usage[] = {
1718
N_("git push [<options>] [<repository> [<refspec>...]]"),
1819
NULL,
1920
};
2021

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+
2149
static int thin = 1;
2250
static int deleterefs;
2351
static const char *receivepack;
@@ -337,8 +365,11 @@ static int push_with_options(struct transport *transport, int flags)
337365
fprintf(stderr, _("Pushing to %s\n"), transport->url);
338366
err = transport_push(transport, refspec_nr, refspec, flags,
339367
&reject_reasons);
340-
if (err != 0)
368+
if (err != 0) {
369+
fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
341370
error(_("failed to push some refs to '%s'"), transport->url);
371+
fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET));
372+
}
342373

343374
err |= transport_disconnect(transport);
344375
if (!err)
@@ -467,6 +498,7 @@ static void set_push_cert_flags(int *flags, int v)
467498

468499
static int git_push_config(const char *k, const char *v, void *cb)
469500
{
501+
const char *slot_name;
470502
int *flags = cb;
471503
int status;
472504

@@ -514,6 +546,16 @@ static int git_push_config(const char *k, const char *v, void *cb)
514546
else
515547
string_list_append(&push_options_config, v);
516548
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]);
517559
}
518560

519561
return git_default_config(k, v, NULL);

color.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -319,18 +319,20 @@ int git_config_colorbool(const char *var, const char *value)
319319
return GIT_COLOR_AUTO;
320320
}
321321

322-
static int check_auto_color(void)
322+
static int check_auto_color(int fd)
323323
{
324-
if (color_stdout_is_tty < 0)
325-
color_stdout_is_tty = isatty(1);
326-
if (color_stdout_is_tty || (pager_in_use() && pager_use_color)) {
324+
static int color_stderr_is_tty = -1;
325+
int *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty;
326+
if (*is_tty_p < 0)
327+
*is_tty_p = isatty(fd);
328+
if (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) {
327329
if (!is_terminal_dumb())
328330
return 1;
329331
}
330332
return 0;
331333
}
332334

333-
int want_color(int var)
335+
int want_color_fd(int fd, int var)
334336
{
335337
/*
336338
* NEEDSWORK: This function is sometimes used from multiple threads, and
@@ -339,15 +341,15 @@ int want_color(int var)
339341
* is listed in .tsan-suppressions for the time being.
340342
*/
341343

342-
static int want_auto = -1;
344+
static int want_auto[3] = { -1, -1, -1 };
343345

344346
if (var < 0)
345347
var = git_use_color_default;
346348

347349
if (var == GIT_COLOR_AUTO) {
348-
if (want_auto < 0)
349-
want_auto = check_auto_color();
350-
return want_auto;
350+
if (want_auto[fd] < 0)
351+
want_auto[fd] = check_auto_color(fd);
352+
return want_auto[fd];
351353
}
352354
return var;
353355
}

color.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ int git_config_colorbool(const char *var, const char *value);
8888
* Return a boolean whether to use color, where the argument 'var' is
8989
* one of GIT_COLOR_UNKNOWN, GIT_COLOR_NEVER, GIT_COLOR_ALWAYS, GIT_COLOR_AUTO.
9090
*/
91-
int want_color(int var);
91+
int want_color_fd(int fd, int var);
92+
#define want_color(colorbool) want_color_fd(1, (colorbool))
93+
#define want_color_stderr(colorbool) want_color_fd(2, (colorbool))
9294

9395
/*
9496
* Translate a Git color from 'value' into a string that the terminal can

config.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1357,7 +1357,7 @@ int git_default_config(const char *var, const char *value, void *cb)
13571357
if (starts_with(var, "mailmap."))
13581358
return git_default_mailmap_config(var, value);
13591359

1360-
if (starts_with(var, "advice."))
1360+
if (starts_with(var, "advice.") || starts_with(var, "color.advice"))
13611361
return git_default_advice_config(var, value);
13621362

13631363
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {

t/t5541-http-push-smart.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,5 +377,17 @@ test_expect_success 'push status output scrubs password' '
377377
grep "^To $HTTPD_URL/smart/test_repo.git" status
378378
'
379379

380+
test_expect_success 'colorize errors/hints' '
381+
cd "$ROOT_PATH"/test_repo_clone &&
382+
test_must_fail git -c color.transport=always -c color.advice=always \
383+
-c color.push=always \
384+
push origin origin/master^:master 2>act &&
385+
test_decode_color <act >decoded &&
386+
test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
387+
test_i18ngrep "<RED>error: failed to push some refs" decoded &&
388+
test_i18ngrep "<YELLOW>hint: " decoded &&
389+
test_i18ngrep ! "^hint: " decoded
390+
'
391+
380392
stop_httpd
381393
test_done

transport.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,56 @@
1818
#include "sha1-array.h"
1919
#include "sigchain.h"
2020
#include "transport-internal.h"
21+
#include "color.h"
22+
23+
static int transport_use_color = -1;
24+
static char transport_colors[][COLOR_MAXLEN] = {
25+
GIT_COLOR_RESET,
26+
GIT_COLOR_RED /* REJECTED */
27+
};
28+
29+
enum color_transport {
30+
TRANSPORT_COLOR_RESET = 0,
31+
TRANSPORT_COLOR_REJECTED = 1
32+
};
33+
34+
static int transport_color_config(void)
35+
{
36+
const char *keys[] = {
37+
"color.transport.reset",
38+
"color.transport.rejected"
39+
}, *key = "color.transport";
40+
char *value;
41+
int i;
42+
static int initialized;
43+
44+
if (initialized)
45+
return 0;
46+
initialized = 1;
47+
48+
if (!git_config_get_string(key, &value))
49+
transport_use_color = git_config_colorbool(key, value);
50+
51+
if (!want_color_stderr(transport_use_color))
52+
return 0;
53+
54+
for (i = 0; i < ARRAY_SIZE(keys); i++)
55+
if (!git_config_get_string(keys[i], &value)) {
56+
if (!value)
57+
return config_error_nonbool(keys[i]);
58+
if (color_parse(value, transport_colors[i]) < 0)
59+
return -1;
60+
}
61+
62+
return 0;
63+
}
64+
65+
static const char *transport_get_color(enum color_transport ix)
66+
{
67+
if (want_color_stderr(transport_use_color))
68+
return transport_colors[ix];
69+
return "";
70+
}
2171

2272
static void set_upstreams(struct transport *transport, struct ref *refs,
2373
int pretend)
@@ -338,7 +388,13 @@ static void print_ref_status(char flag, const char *summary,
338388
else
339389
fprintf(stdout, "%s\n", summary);
340390
} else {
341-
fprintf(stderr, " %c %-*s ", flag, summary_width, summary);
391+
const char *red = "", *reset = "";
392+
if (push_had_errors(to)) {
393+
red = transport_get_color(TRANSPORT_COLOR_REJECTED);
394+
reset = transport_get_color(TRANSPORT_COLOR_RESET);
395+
}
396+
fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width,
397+
summary, reset);
342398
if (from)
343399
fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
344400
else
@@ -487,6 +543,9 @@ void transport_print_push_status(const char *dest, struct ref *refs,
487543
char *head;
488544
int summary_width = transport_summary_width(refs);
489545

546+
if (transport_color_config() < 0)
547+
warning(_("could not parse transport.color.* config"));
548+
490549
head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
491550

492551
if (verbose) {
@@ -553,6 +612,9 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
553612
struct send_pack_args args;
554613
int ret;
555614

615+
if (transport_color_config() < 0)
616+
return -1;
617+
556618
if (!data->got_remote_heads) {
557619
struct ref *tmp_refs;
558620
connect_setup(transport, 1);
@@ -997,6 +1059,9 @@ int transport_push(struct transport *transport,
9971059
*reject_reasons = 0;
9981060
transport_verify_remote_names(refspec_nr, refspec);
9991061

1062+
if (transport_color_config() < 0)
1063+
return -1;
1064+
10001065
if (transport->vtable->push_refs) {
10011066
struct ref *remote_refs;
10021067
struct ref *local_refs = get_local_heads();

0 commit comments

Comments
 (0)