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. The original contribution came in via Pull Request from the Git for Windows project: https://github.com/git-for-windows/git/pull/1429 Changes since v1: - implemented want_color_fd() as suggested by Junio - added color.{advice,push,transport} to be able to force this thing on - added a test - added documentation to `git config`'s man page - fixed a bug where the push config looked at color.advice.<slot> - fixed a bug where `color.advise.<slot>` was not parsed because git_default_config() would fail to hand `color.advise.*` settings to git_default_advice_config() - wrapped a couple of too-long lines - changed the strstr() hack to detect push failures to use push_had_errors() instead - renamed the transport.color.* settings to color.transport.* (to make them consistent with all the other color.<category>.<slot> settings) Johannes Schindelin (3): color: introduce support for colorizing stderr Add a test to verify that push errors are colorful Document the new color.* settings to colorize push errors/hints Ryan Dammrose (1): push: colorize errors Documentation/config.txt | 28 ++++++++++++++++ advice.c | 49 ++++++++++++++++++++++++++-- builtin/push.c | 44 ++++++++++++++++++++++++- color.c | 20 +++++++----- color.h | 4 ++- config.c | 2 +- t/t5541-http-push-smart.sh | 18 ++++++++++ transport.c | 67 +++++++++++++++++++++++++++++++++++++- 8 files changed, 217 insertions(+), 15 deletions(-) base-commit: 468165c1d8a442994a825f3684528361727cd8c0 Published-As: https://github.com/dscho/git/releases/tag/colorize-push-errors-v2 Fetch-It-Via: git fetch https://github.com/dscho/git colorize-push-errors-v2 Interdiff vs v1: diff --git a/Documentation/config.txt b/Documentation/config.txt index 4e0cff87f62..40e3828b85f 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1088,6 +1088,16 @@ clean.requireForce:: A boolean to make git-clean do nothing unless given -f, -i or -n. Defaults to true. +color.advice:: + A boolean to enable/disable color in hints (e.g. when a push + failed, see `advice.*` for a list). May be set to `always`, + `false` (or `never`) or `auto` (or `true`), in which case colors + are used only when the error output goes to a terminal. If + unset, then the value of `color.ui` is used (`auto` by default). + +color.advice.advice:: + Use customized color for hints. + color.branch:: A boolean to enable/disable color in the output of linkgit:git-branch[1]. May be set to `always`, @@ -1190,6 +1200,15 @@ color.pager:: A boolean to enable/disable colored output when the pager is in use (default is true). +color.push:: + A boolean to enable/disable color in push errors. May be set to + `always`, `false` (or `never`) or `auto` (or `true`), in which + case colors are used only when the error output goes to a terminal. + If unset, then the value of `color.ui` is used (`auto` by default). + +color.push.error:: + Use customized color for push errors. + color.showBranch:: A boolean to enable/disable color in the output of linkgit:git-show-branch[1]. May be set to `always`, @@ -1218,6 +1237,15 @@ color.status.<slot>:: status short-format), or `unmerged` (files which have unmerged changes). +color.transport:: + A boolean to enable/disable color when pushes are rejected. May be + set to `always`, `false` (or `never`) or `auto` (or `true`), in which + case colors are used only when the error output goes to a terminal. + If unset, then the value of `color.ui` is used (`auto` by default). + +color.transport.rejected:: + Use customized color when a push was rejected. + color.ui:: This variable determines the default value for variables such as `color.diff` and `color.grep` that control the use of color diff --git a/advice.c b/advice.c index 42460877ef6..2121c1811fd 100644 --- a/advice.c +++ b/advice.c @@ -43,7 +43,7 @@ static int parse_advise_color_slot(const char *slot) static const char *advise_get_color(enum color_advice ix) { - if (want_color(advice_use_color)) + if (want_color_stderr(advice_use_color)) return advice_colors[ix]; return ""; } @@ -87,8 +87,10 @@ void advise(const char *advice, ...) for (cp = buf.buf; *cp; cp = np) { np = strchrnul(cp, '\n'); - fprintf(stderr, _("%shint: %.*s%s\n"), advise_get_color(ADVICE_COLOR_HINT), - (int)(np - cp), cp, advise_get_color(ADVICE_COLOR_RESET)); + fprintf(stderr, _("%shint: %.*s%s\n"), + advise_get_color(ADVICE_COLOR_HINT), + (int)(np - cp), cp, + advise_get_color(ADVICE_COLOR_RESET)); if (*np) np++; } @@ -100,6 +102,11 @@ int git_default_advice_config(const char *var, const char *value) const char *k, *slot_name; int i; + if (!strcmp(var, "color.advice")) { + advice_use_color = git_config_colorbool(var, value); + return 0; + } + if (skip_prefix(var, "color.advice.", &slot_name)) { int slot = parse_advise_color_slot(slot_name); if (slot < 0) diff --git a/builtin/push.c b/builtin/push.c index e443dd40de9..ac3705370e1 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -41,7 +41,7 @@ static int parse_push_color_slot(const char *slot) static const char *push_get_color(enum color_push ix) { - if (want_color(push_use_color)) + if (want_color_stderr(push_use_color)) return push_colors[ix]; return ""; } @@ -365,10 +365,11 @@ static int push_with_options(struct transport *transport, int flags) fprintf(stderr, _("Pushing to %s\n"), transport->url); err = transport_push(transport, refspec_nr, refspec, flags, &reject_reasons); - if (err != 0) + 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) @@ -545,7 +546,10 @@ static int git_push_config(const char *k, const char *v, void *cb) else string_list_append(&push_options_config, v); return 0; - } else if (skip_prefix(k, "color.advice.", &slot_name)) { + } 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; diff --git a/color.c b/color.c index f277e72e4ce..c6c6c4f580f 100644 --- a/color.c +++ b/color.c @@ -319,18 +319,20 @@ int git_config_colorbool(const char *var, const char *value) return GIT_COLOR_AUTO; } -static int check_auto_color(void) +static int check_auto_color(int fd) { - if (color_stdout_is_tty < 0) - color_stdout_is_tty = isatty(1); - if (color_stdout_is_tty || (pager_in_use() && pager_use_color)) { + static int color_stderr_is_tty = -1; + int *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty; + if (*is_tty_p < 0) + *is_tty_p = isatty(fd); + if (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) { if (!is_terminal_dumb()) return 1; } return 0; } -int want_color(int var) +int want_color_fd(int fd, int var) { /* * NEEDSWORK: This function is sometimes used from multiple threads, and @@ -339,15 +341,15 @@ int want_color(int var) * is listed in .tsan-suppressions for the time being. */ - static int want_auto = -1; + static int want_auto[3] = { -1, -1, -1 }; if (var < 0) var = git_use_color_default; if (var == GIT_COLOR_AUTO) { - if (want_auto < 0) - want_auto = check_auto_color(); - return want_auto; + if (want_auto[fd] < 0) + want_auto[fd] = check_auto_color(fd); + return want_auto[fd]; } return var; } diff --git a/color.h b/color.h index cd0bcedd084..5b744e1bc68 100644 --- a/color.h +++ b/color.h @@ -88,7 +88,9 @@ int git_config_colorbool(const char *var, const char *value); * Return a boolean whether to use color, where the argument 'var' is * one of GIT_COLOR_UNKNOWN, GIT_COLOR_NEVER, GIT_COLOR_ALWAYS, GIT_COLOR_AUTO. */ -int want_color(int var); +int want_color_fd(int fd, int var); +#define want_color(colorbool) want_color_fd(1, (colorbool)) +#define want_color_stderr(colorbool) want_color_fd(2, (colorbool)) /* * Translate a Git color from 'value' into a string that the terminal can diff --git a/config.c b/config.c index b0c20e6cb8a..3bdbe36a638 100644 --- a/config.c +++ b/config.c @@ -1365,7 +1365,7 @@ int git_default_config(const char *var, const char *value, void *dummy) if (starts_with(var, "mailmap.")) return git_default_mailmap_config(var, value); - if (starts_with(var, "advice.")) + if (starts_with(var, "advice.") || starts_with(var, "color.advice")) return git_default_advice_config(var, value); if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh index 21340e89c96..1c2be98dc2b 100755 --- a/t/t5541-http-push-smart.sh +++ b/t/t5541-http-push-smart.sh @@ -377,5 +377,23 @@ test_expect_success 'push status output scrubs password' ' grep "^To $HTTPD_URL/smart/test_repo.git" status ' +test_expect_success 'colorize errors/hints' ' + cd "$ROOT_PATH"/test_repo_clone && + cat >exp <<-EOF && + To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git + <RED>! [rejected] <RESET> origin/master^ -> master (non-fast-forward) + error: failed to push some refs to '\''http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'\'' + EOF + + test_must_fail git -c color.transport=always -c color.advice=always \ + -c color.push=always \ + push origin origin/master^:master 2>act && + test_decode_color <act >decoded && + test_i18ngrep "<RED>.*rejected.*<RESET>" decoded && + test_i18ngrep "<RED>error: failed to push some refs" decoded && + test_i18ngrep "<YELLOW>hint: " decoded && + test_i18ngrep ! "^hint: " decoded +' + stop_httpd test_done diff --git a/transport.c b/transport.c index dd98dd84b05..4702b9fbc8f 100644 --- a/transport.c +++ b/transport.c @@ -34,9 +34,9 @@ enum color_transport { static int transport_color_config(void) { const char *keys[] = { - "transport.color.reset", - "transport.color.rejected" - }; + "color.transport.reset", + "color.transport.rejected" + }, *key = "color.transport"; char *value; int i; static int initialized; @@ -45,6 +45,12 @@ static int transport_color_config(void) return 0; initialized = 1; + if (!git_config_get_string(key, &value)) + transport_use_color = git_config_colorbool(key, value); + + if (!want_color_stderr(transport_use_color)) + return 0; + for (i = 0; i < ARRAY_SIZE(keys); i++) if (!git_config_get_string(keys[i], &value)) { if (!value) @@ -58,7 +64,7 @@ static int transport_color_config(void) static const char *transport_get_color(enum color_transport ix) { - if (want_color(transport_use_color)) + if (want_color_stderr(transport_use_color)) return transport_colors[ix]; return ""; } @@ -382,11 +388,13 @@ static void print_ref_status(char flag, const char *summary, else fprintf(stdout, "%s\n", summary); } else { - if (strstr(summary, "rejected") != NULL || strstr(summary, "failure") != NULL) - fprintf(stderr, " %s%c %-*s%s ", transport_get_color(TRANSPORT_COLOR_REJECTED), - flag, summary_width, summary, transport_get_color(TRANSPORT_COLOR_RESET)); - else - fprintf(stderr, " %c %-*s ", flag, summary_width, summary); + const char *red = "", *reset = ""; + if (push_had_errors(to)) { + red = transport_get_color(TRANSPORT_COLOR_REJECTED); + reset = transport_get_color(TRANSPORT_COLOR_RESET); + } + fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width, + summary, reset); if (from) fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name)); else -- 2.17.0.windows.1.4.g7e4058d72e3