Skip to content

Commit

Permalink
Merge branch 'colorize-push-errors'
Browse files Browse the repository at this point in the history
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 <johannes.schindelin@gmx.de>
  • Loading branch information
dscho committed May 29, 2018
2 parents 6122cbb + 675ed7e commit a60968c
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 15 deletions.
28 changes: 28 additions & 0 deletions Documentation/config.txt
Expand Up @@ -1109,6 +1109,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.hint::
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`,
Expand Down Expand Up @@ -1211,6 +1221,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`,
Expand Down Expand Up @@ -1239,6 +1258,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
Expand Down
49 changes: 47 additions & 2 deletions advice.c
@@ -1,5 +1,6 @@
#include "cache.h"
#include "config.h"
#include "color.h"

int advice_push_update_rejected = 1;
int advice_push_non_ff_current = 1;
Expand All @@ -20,6 +21,33 @@ int advice_add_embedded_repo = 1;
int advice_ignored_hook = 1;
int advice_waiting_for_editor = 1;

static int advice_use_color = -1;
static char advice_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
GIT_COLOR_YELLOW, /* HINT */
};

enum color_advice {
ADVICE_COLOR_RESET = 0,
ADVICE_COLOR_HINT = 1,
};

static int parse_advise_color_slot(const char *slot)
{
if (!strcasecmp(slot, "reset"))
return ADVICE_COLOR_RESET;
if (!strcasecmp(slot, "hint"))
return ADVICE_COLOR_HINT;
return -1;
}

static const char *advise_get_color(enum color_advice ix)
{
if (want_color_stderr(advice_use_color))
return advice_colors[ix];
return "";
}

static struct {
const char *name;
int *preference;
Expand Down Expand Up @@ -59,7 +87,10 @@ void advise(const char *advice, ...)

for (cp = buf.buf; *cp; cp = np) {
np = strchrnul(cp, '\n');
fprintf(stderr, _("hint: %.*s\n"), (int)(np - cp), cp);
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++;
}
Expand All @@ -68,9 +99,23 @@ void advise(const char *advice, ...)

int git_default_advice_config(const char *var, const char *value)
{
const char *k;
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)
return 0;
if (!value)
return config_error_nonbool(var);
return color_parse(value, advice_colors[slot]);
}

if (!skip_prefix(var, "advice.", &k))
return 0;

Expand Down
44 changes: 43 additions & 1 deletion builtin/push.c
Expand Up @@ -12,12 +12,40 @@
#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;
Expand Down Expand Up @@ -337,8 +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)
Expand Down Expand Up @@ -467,6 +498,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;

Expand Down Expand Up @@ -514,6 +546,16 @@ 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 (!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);
Expand Down
20 changes: 11 additions & 9 deletions color.c
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion color.h
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion config.c
Expand Up @@ -1357,7 +1357,7 @@ int git_default_config(const char *var, const char *value, void *cb)
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")) {
Expand Down
12 changes: 12 additions & 0 deletions t/t5541-http-push-smart.sh
Expand Up @@ -377,5 +377,17 @@ 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 &&
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
67 changes: 66 additions & 1 deletion transport.c
Expand Up @@ -18,6 +18,56 @@
#include "sha1-array.h"
#include "sigchain.h"
#include "transport-internal.h"
#include "color.h"

static int transport_use_color = -1;
static char transport_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
GIT_COLOR_RED /* REJECTED */
};

enum color_transport {
TRANSPORT_COLOR_RESET = 0,
TRANSPORT_COLOR_REJECTED = 1
};

static int transport_color_config(void)
{
const char *keys[] = {
"color.transport.reset",
"color.transport.rejected"
}, *key = "color.transport";
char *value;
int i;
static int initialized;

if (initialized)
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)
return config_error_nonbool(keys[i]);
if (color_parse(value, transport_colors[i]) < 0)
return -1;
}

return 0;
}

static const char *transport_get_color(enum color_transport ix)
{
if (want_color_stderr(transport_use_color))
return transport_colors[ix];
return "";
}

static void set_upstreams(struct transport *transport, struct ref *refs,
int pretend)
Expand Down Expand Up @@ -338,7 +388,13 @@ static void print_ref_status(char flag, const char *summary,
else
fprintf(stdout, "%s\n", summary);
} 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
Expand Down Expand Up @@ -487,6 +543,9 @@ void transport_print_push_status(const char *dest, struct ref *refs,
char *head;
int summary_width = transport_summary_width(refs);

if (transport_color_config() < 0)
warning(_("could not parse transport.color.* config"));

head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);

if (verbose) {
Expand Down Expand Up @@ -553,6 +612,9 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
struct send_pack_args args;
int ret;

if (transport_color_config() < 0)
return -1;

if (!data->got_remote_heads) {
struct ref *tmp_refs;
connect_setup(transport, 1);
Expand Down Expand Up @@ -997,6 +1059,9 @@ int transport_push(struct transport *transport,
*reject_reasons = 0;
transport_verify_remote_names(refspec_nr, refspec);

if (transport_color_config() < 0)
return -1;

if (transport->vtable->push_refs) {
struct ref *remote_refs;
struct ref *local_refs = get_local_heads();
Expand Down

0 comments on commit a60968c

Please sign in to comment.