threads / patch / 47863

patchColorize some errors on stderr

Subject: [PATCH 0/1] Colorize some errors on stderr

## tl;dr

22 messages between Feb 16, 2018 and Apr 21, 2018. Diffs are folded; open one to read it.

replies: 21people: 5as markdown or json

Johannes Schindelin· Feb 16, 2018, 12:25 UTC · lore

This is an RFC because it tries to introduce a fundamental new color feature: Coloring messages *on stderr*.

So far, pretty much everything in color.[ch] assumed that you want to color only stuff on stdout.

However, in this case, a user (who became a contributor!) wanted some messages that are printed to stderr and were missed by his colleagues to be colored.

The contribution comes via Pull Request from the Git for Windows project:
	https://github.com/git-for-windows/git/pull/1429
Now, what would be possible solutions for this?
- introduce `int fd` in `want_color()` (and callees) so that we can make
  a distinction whether we want to detect whether stdout or stderr is connected
  to a tty
- introduce a separate `want_color_stderr()` (we still would need to decide
  whether we want a config setting for this)
- not color stderr, ever

Also, I did not have too much time to dig into the question how to test this in Git's test suite. Do we already have tests that generate fake server-side errors onto which I could piggy-back a new test case?

Thoughts? Suggestions? Help?
Ryan Dammrose (1):
  Colorize push errors
 advice.c       | 42 +++++++++++++++++++++++++++++++++++++++--
 builtin/push.c | 38 +++++++++++++++++++++++++++++++++++++
 transport.c    | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 136 insertions(+), 3 deletions(-)
base-commit: b2e45c695d09f6a31ce09347ae0a5d2cdfe9dd4e
Published-As: https://github.com/dscho/git/releases/tag/colorize-push-errors-v1
Fetch-It-Via: git fetch https://github.com/dscho/git colorize-push-errors-v1
-- 
2.16.1.windows.4
Johannes Schindelin· Feb 16, 2018, 12:25 UTC · re: Johannes Schindelin · lore

[PATCH 1/1] Colorize push errors

From: Ryan Dammrose <ryandammrose@gmail.com>

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.

My solution was to try to change the stderr and hint colors (red and yellow, respectively) so whenever there is a failure when pushing to a remote repository that fails, it is more noticeable.

The challenge was that it seemed that stderr has never been colored and I attempted to utilize what was already established; but this meant using functions like want_color() even if it targets stdout while I wanted to target stderr.

Additionally, to check for all rejection types, I did a strstr check in transport.c, but this code could be problematic if there is need for translation.

Signed-off-by: Ryan Dammrose ryandammrose@gmail.com
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 advice.c       | 42 +++++++++++++++++++++++++++++++++++++++--
 builtin/push.c | 38 +++++++++++++++++++++++++++++++++++++
 transport.c    | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 136 insertions(+), 3 deletions(-)
Show changes to 3 files +136 −3

advice.c, builtin/push.c, transport.c

diff --git a/advice.c b/advice.c
index 406efc183ba..42460877ef6 100644
--- a/advice.c
+++ b/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;
@@ -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, "advice"))
+		return ADVICE_COLOR_HINT;
+	return -1;
+}
+
+static const char *advise_get_color(enum color_advice ix)
+{
+	if (want_color(advice_use_color))
+		return advice_colors[ix];
+	return "";
+}
+
 static struct {
 	const char *name;
 	int *preference;
@@ -59,7 +87,8 @@ 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++;
 	}
@@ -68,9 +97,18 @@ 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 (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;
 
diff --git a/builtin/push.c b/builtin/push.c
index 1c28427d82e..997ccab52ac 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -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(push_use_color))
+		return push_colors[ix];
+	return "";
+}
+
 static int thin = 1;
 static int deleterefs;
 static const char *receivepack;
@@ -338,7 +366,9 @@ static int push_with_options(struct transport *transport, int flags)
 	err = transport_push(transport, refspec_nr, refspec, 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)
@@ -467,6 +497,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;
 
@@ -514,6 +545,13 @@ 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)) {
+		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);
diff --git a/transport.c b/transport.c
index 00d48b5b565..dd98dd84b05 100644
--- a/transport.c
+++ b/transport.c
@@ -18,6 +18,50 @@
 #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[] = {
+		"transport.color.reset",
+		"transport.color.rejected"
+	};
+	char *value;
+	int i;
+	static int initialized;
+
+	if (initialized)
+		return 0;
+	initialized = 1;
+
+	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(transport_use_color))
+		return transport_colors[ix];
+	return "";
+}
 
 static void set_upstreams(struct transport *transport, struct ref *refs,
 	int pretend)
@@ -338,7 +382,11 @@ 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);
+		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);
 		if (from)
 			fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
 		else
@@ -487,6 +535,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) {
@@ -553,6 +604,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);
@@ -997,6 +1051,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();
-- 
2.16.1.windows.4
Junio C Hamano· Feb 16, 2018, 19:21 UTC · re: Johannes Schindelin · lore

Re: [PATCH 0/1] Colorize some errors on stderr

Johannes Schindelin <johannes.schindelin@gmx.de> writes:
Show 8 quoted lines
> Now, what would be possible solutions for this?
>
> - introduce `int fd` in `want_color()` (and callees) so that we can make
>   a distinction whether we want to detect whether stdout or stderr is connected
>   to a tty
>
> - introduce a separate `want_color_stderr()` (we still would need to decide
>   whether we want a config setting for this)

Between the above two, there probably aren't so big a difference, but in order to avoid disrupting existing callers of want_color() while possibly sharing as much code between the old and new callers, perhaps:

	extern int want_color_fd(int fd, int colorbool);
	#define want_color(colorbool) want_color_fd(1, (colorbool))
	#define want_color_stderr(colorbool) want_color_fd(2, (colorbool))

We should honor configuration at two levels, just like the colors on stdout, i.e. color in which individual items are painted (e.g. color.diff.filename, color.advice.hint) and whether we use colors in UI at all (e.g. color.ui). I do not think it is necessary or even at the right granularity to allow settings like "do color stdout but do not color errors".

> - not color stderr, ever

This is my personal preference, but that does not and should not carry too much weight ;-)

Johannes Schindelin· Apr 6, 2018, 11:21 UTC · re: Junio C Hamano · lore

Re: [PATCH 0/1] Colorize some errors on stderr

Hi Junio,

[and welcome back, at least I hope you only read this after a good and relaxing vacation]

On Fri, 16 Feb 2018, Junio C Hamano wrote:
Show 19 quoted lines
> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > Now, what would be possible solutions for this?
> >
> > - introduce `int fd` in `want_color()` (and callees) so that we can make
> >   a distinction whether we want to detect whether stdout or stderr is connected
> >   to a tty
> >
> > - introduce a separate `want_color_stderr()` (we still would need to decide
> >   whether we want a config setting for this)
> 
> Between the above two, there probably aren't so big a difference, but
> in order to avoid disrupting existing callers of want_color() while
> possibly sharing as much code between the old and new callers,
> perhaps:
> 
> 	extern int want_color_fd(int fd, int colorbool);
> 	#define want_color(colorbool) want_color_fd(1, (colorbool))
> 	#define want_color_stderr(colorbool) want_color_fd(2, (colorbool))
I made it so.

Note that I also had to change the check_auto_color() function, and while want_color_fd() can have a "private" record of previous results, check_auto_color() needs to use the global color_stdout_is_tty (so that builtin/config.c can edit it, for use in `git config --colorbool <name> [stdout-is-tty]`).

> We should honor configuration at two levels, just like the colors on
> stdout, i.e. color in which individual items are painted (e.g.
> color.diff.filename, color.advice.hint) and whether we use colors in
> UI at all (e.g. color.ui).
This is how v2 does it.

Thanks for your suggestions, Dscho

Johannes Schindelin· Apr 5, 2018, 22:48 UTC · re: Johannes Schindelin · lore

[PATCH v2 0/4] 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.

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
Johannes Schindelin· Apr 5, 2018, 22:48 UTC · re: Johannes Schindelin · lore

[PATCH v2 1/4] color: introduce support for colorizing stderr

So far, we only ever asked whether stdout wants to be colorful. In the upcoming patches, we will want to make push errors more prominent, which are printed to stderr, though.

So let's refactor the want_color() function into a want_color_fd() function (which expects to be called with fd == 1 or fd == 2 for stdout and stderr, respectively), and then define the macro `want_color()` to use the want_color_fd() function.

And then also add a macro `want_color_stderr()`, for convenience and for documentation.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 color.c | 20 +++++++++++---------
 color.h |  4 +++-
 2 files changed, 14 insertions(+), 10 deletions(-)
Show changes to 2 files +14 −10

color.c, color.h

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
-- 
2.17.0.windows.1.4.g7e4058d72e3
Johannes Schindelin· Apr 5, 2018, 22:48 UTC · re: Johannes Schindelin · lore

[PATCH v2 2/4] push: colorize errors

From: Ryan Dammrose <ryandammrose@gmail.com>

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>
squash! push: colorize errors
Stop talking about localized errors
---
 advice.c       | 49 ++++++++++++++++++++++++++++++++++--
 builtin/push.c | 44 ++++++++++++++++++++++++++++++++-
 config.c       |  2 +-
 transport.c    | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 157 insertions(+), 5 deletions(-)
Show changes to 4 files +157 −5

advice.c, builtin/push.c, config.c, transport.c

diff --git a/advice.c b/advice.c
index 406efc183ba..2121c1811fd 100644
--- a/advice.c
+++ b/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;
@@ -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, "advice"))
+		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;
@@ -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++;
 	}
@@ -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;
 
diff --git a/builtin/push.c b/builtin/push.c
index 013c20d6164..ac3705370e1 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -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;
@@ -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)
@@ -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;
 
@@ -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);
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/transport.c b/transport.c
index 00d48b5b565..4702b9fbc8f 100644
--- a/transport.c
+++ b/transport.c
@@ -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)
@@ -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
@@ -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) {
@@ -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);
@@ -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();
-- 
2.17.0.windows.1.4.g7e4058d72e3
Jacob Keller· Apr 5, 2018, 23:37 UTC · re: Johannes Schindelin · lore

Re: [PATCH v2 2/4] push: colorize errors

On Thu, Apr 5, 2018 at 3:48 PM, Johannes Schindelin <johannes.schindelin@gmx.de> wrote:

Show 27 quoted lines
> From: Ryan Dammrose <ryandammrose@gmail.com>
>
> 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>
>
> squash! push: colorize errors
>
> Stop talking about localized errors
Guessing you intended to remove this part after squashing?
Didn't see anything else to comment on in the actual code.

Thanks, Jake

Johannes Schindelin· Apr 6, 2018, 11:15 UTC · re: Jacob Keller · lore

Re: [PATCH v2 2/4] push: colorize errors

Hi Jake,
On Thu, 5 Apr 2018, Jacob Keller wrote:
Show 31 quoted lines
> On Thu, Apr 5, 2018 at 3:48 PM, Johannes Schindelin
> <johannes.schindelin@gmx.de> wrote:
> > From: Ryan Dammrose <ryandammrose@gmail.com>
> >
> > 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>
> >
> > squash! push: colorize errors
> >
> > Stop talking about localized errors
> 
> Guessing you intended to remove this part after squashing?
Hah! You caught be red-handed.

This was intended as a reminder, as you probably guessed, to remove any mentions of "localized errors" because I had verified that there was no localized error message; besides, I replaced the call to strstr() looking at the error message with a call to push_had_errors() (i.e. using the ref_status instead). So there are definitely no issues about localized errors left.

> Didn't see anything else to comment on in the actual code.

Thank you, Dscho

Johannes Schindelin· Apr 5, 2018, 22:48 UTC · re: Johannes Schindelin · lore

[PATCH v2 3/4] Add a test to verify that push errors are colorful

This actually only tests whether the push errors/hints are colored if the respective color.* config settings are `always`, but in the regular case they default to `auto` (in which case we color the messages when stderr is connected to an interactive terminal), therefore these tests should suffice.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t5541-http-push-smart.sh | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
Show changes to t/t5541-http-push-smart.sh +18 −0
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
-- 
2.17.0.windows.1.4.g7e4058d72e3
Eric Sunshine· Apr 6, 2018, 10:50 UTC · re: Johannes Schindelin · lore

Re: [PATCH v2 3/4] Add a test to verify that push errors are colorful

On Thu, Apr 5, 2018 at 6:48 PM, Johannes Schindelin <johannes.schindelin@gmx.de> wrote:

Show 17 quoted lines
> This actually only tests whether the push errors/hints are colored if
> the respective color.* config settings are `always`, but in the regular
> case they default to `auto` (in which case we color the messages when
> stderr is connected to an interactive terminal), therefore these tests
> should suffice.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
> diff --git 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' '
> +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
This "exp" file is not used by the test.
Show 9 quoted lines
> +       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
> +'
Johannes Schindelin· Apr 6, 2018, 12:13 UTC · re: Eric Sunshine · lore

Re: [PATCH v2 3/4] Add a test to verify that push errors are colorful

Hi Eric,
On Fri, 6 Apr 2018, Eric Sunshine wrote:
Show 21 quoted lines
> On Thu, Apr 5, 2018 at 6:48 PM, Johannes Schindelin
> <johannes.schindelin@gmx.de> wrote:
> > This actually only tests whether the push errors/hints are colored if
> > the respective color.* config settings are `always`, but in the regular
> > case they default to `auto` (in which case we color the messages when
> > stderr is connected to an interactive terminal), therefore these tests
> > should suffice.
> >
> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> > ---
> > diff --git 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' '
> > +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
> 
> This "exp" file is not used by the test.

Indeed! I was *so* sure I had removed it, but a `git stash` must have made that change go away before I could commit it.

Will be fixed in v3.

Ciao, Dscho

Johannes Schindelin· Apr 5, 2018, 22:48 UTC · re: Johannes Schindelin · lore

[PATCH v2 4/4] Document the new color.* settings to colorize push errors/hints

Let's make it easier for users to find out how to customize these colors.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Documentation/config.txt | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
Show changes to Documentation/config.txt +28 −0
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
-- 
2.17.0.windows.1.4.g7e4058d72e3
Eric Sunshine· Apr 6, 2018, 10:56 UTC · re: Johannes Schindelin · lore

Re: [PATCH v2 4/4] Document the new color.* settings to colorize push errors/hints

On Thu, Apr 5, 2018 at 6:48 PM, Johannes Schindelin <johannes.schindelin@gmx.de> wrote:

Show 15 quoted lines
> Let's make it easier for users to find out how to customize these colors.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> @@ -1088,6 +1088,16 @@ clean.requireForce::
> +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.
Is "color.advice.advice" correct?
Johannes Schindelin· Apr 6, 2018, 12:15 UTC · re: Eric Sunshine · lore

Re: [PATCH v2 4/4] Document the new color.* settings to colorize push errors/hints

Hi Eric,
On Fri, 6 Apr 2018, Eric Sunshine wrote:
Show 19 quoted lines
> On Thu, Apr 5, 2018 at 6:48 PM, Johannes Schindelin
> <johannes.schindelin@gmx.de> wrote:
> > Let's make it easier for users to find out how to customize these colors.
> >
> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> > ---
> > diff --git a/Documentation/config.txt b/Documentation/config.txt
> > @@ -1088,6 +1088,16 @@ clean.requireForce::
> > +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.
> 
> Is "color.advice.advice" correct?

As per the patch, yes. But you're right, it sounds silly. Will change to `color.advice.hint`, okay?

Will be fixed in v3, Dscho

Eric Sunshine· Apr 7, 2018, 06:55 UTC · re: Johannes Schindelin · lore

Re: [PATCH v2 4/4] Document the new color.* settings to colorize push errors/hints

On Fri, Apr 6, 2018 at 8:15 AM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

Show 10 quoted lines
> On Fri, 6 Apr 2018, Eric Sunshine wrote:
>> On Thu, Apr 5, 2018 at 6:48 PM, Johannes Schindelin
>> <johannes.schindelin@gmx.de> wrote:
>> > +color.advice.advice::
>> > +       Use customized color for hints.
>>
>> Is "color.advice.advice" correct?
>
> As per the patch, yes. But you're right, it sounds silly. Will change to
> `color.advice.hint`, okay?
That's more understandable. Thanks.
Johannes Schindelin· Apr 21, 2018, 10:09 UTC · re: Johannes Schindelin · lore

[PATCH v3 0/4] 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.

The original contribution came in via Pull Request from the Git for Windows project:

	https://github.com/git-for-windows/git/pull/1429

I almost forgot to submit v3. Guess it's a good thing I have a project management system with tickets I can move through a story board...

Changes since v2:
- removed a bogus "squash! ..." from the commit message of 2/4
- removed the unnecessary here-doc from the test added in 4/4
- renamed color.advice.advice to color.advice.hint in 2/4 and 4/4
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 | 12 +++++++
 transport.c                | 67 +++++++++++++++++++++++++++++++++++++-
 8 files changed, 211 insertions(+), 15 deletions(-)
base-commit: fe0a9eaf31dd0c349ae4308498c33a5c3794b293
Published-As: https://github.com/dscho/git/releases/tag/colorize-push-errors-v3
Fetch-It-Via: git fetch https://github.com/dscho/git colorize-push-errors-v3
Interdiff vs v2:
 diff --git a/Documentation/config.txt b/Documentation/config.txt
 index 1fef60a7301..2cea0c6c899 100644
 --- a/Documentation/config.txt
 +++ b/Documentation/config.txt
 @@ -1095,7 +1095,7 @@ color.advice::
  	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::
 +color.advice.hint::
  	Use customized color for hints.
  
  color.branch::
 diff --git a/advice.c b/advice.c
 index 2121c1811fd..89fda1de55b 100644
 --- a/advice.c
 +++ b/advice.c
 @@ -36,7 +36,7 @@ static int parse_advise_color_slot(const char *slot)
  {
  	if (!strcasecmp(slot, "reset"))
  		return ADVICE_COLOR_RESET;
 -	if (!strcasecmp(slot, "advice"))
 +	if (!strcasecmp(slot, "hint"))
  		return ADVICE_COLOR_HINT;
  	return -1;
  }
 diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
 index 1c2be98dc2b..a2af693068f 100755
 --- a/t/t5541-http-push-smart.sh
 +++ b/t/t5541-http-push-smart.sh
 @@ -379,12 +379,6 @@ test_expect_success 'push status output scrubs password' '
  
  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 &&
-- 
2.17.0.windows.1.15.gaa56ade3205
Johannes Schindelin· Apr 21, 2018, 10:09 UTC · re: Johannes Schindelin · lore

[PATCH v3 1/4] color: introduce support for colorizing stderr

So far, we only ever asked whether stdout wants to be colorful. In the upcoming patches, we will want to make push errors more prominent, which are printed to stderr, though.

So let's refactor the want_color() function into a want_color_fd() function (which expects to be called with fd == 1 or fd == 2 for stdout and stderr, respectively), and then define the macro `want_color()` to use the want_color_fd() function.

And then also add a macro `want_color_stderr()`, for convenience and for documentation.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 color.c | 20 +++++++++++---------
 color.h |  4 +++-
 2 files changed, 14 insertions(+), 10 deletions(-)
Show changes to 2 files +14 −10

color.c, color.h

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
-- 
2.17.0.windows.1.15.gaa56ade3205
Johannes Schindelin· Apr 21, 2018, 10:10 UTC · re: Johannes Schindelin · lore

[PATCH v3 2/4] push: colorize errors

From: Ryan Dammrose <ryandammrose@gmail.com>

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>
---
 advice.c       | 49 ++++++++++++++++++++++++++++++++++--
 builtin/push.c | 44 ++++++++++++++++++++++++++++++++-
 config.c       |  2 +-
 transport.c    | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 157 insertions(+), 5 deletions(-)
Show changes to 4 files +157 −5

advice.c, builtin/push.c, config.c, transport.c

diff --git a/advice.c b/advice.c
index 406efc183ba..89fda1de55b 100644
--- a/advice.c
+++ b/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;
@@ -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;
@@ -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++;
 	}
@@ -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;
 
diff --git a/builtin/push.c b/builtin/push.c
index 013c20d6164..ac3705370e1 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -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;
@@ -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)
@@ -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;
 
@@ -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);
diff --git a/config.c b/config.c
index c698988f5e1..f21317a25a7 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/transport.c b/transport.c
index 94eccf29aa3..f72081a5a58 100644
--- a/transport.c
+++ b/transport.c
@@ -19,6 +19,56 @@
 #include "sigchain.h"
 #include "transport-internal.h"
 #include "object-store.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)
@@ -339,7 +389,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
@@ -488,6 +544,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) {
@@ -554,6 +613,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);
@@ -998,6 +1060,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();
-- 
2.17.0.windows.1.15.gaa56ade3205
Johannes Schindelin· Apr 21, 2018, 10:10 UTC · re: Johannes Schindelin · lore

[PATCH v3 3/4] Add a test to verify that push errors are colorful

This actually only tests whether the push errors/hints are colored if the respective color.* config settings are `always`, but in the regular case they default to `auto` (in which case we color the messages when stderr is connected to an interactive terminal), therefore these tests should suffice.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t5541-http-push-smart.sh | 12 ++++++++++++
 1 file changed, 12 insertions(+)
Show changes to t/t5541-http-push-smart.sh +12 −0
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index 21340e89c96..a2af693068f 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -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
-- 
2.17.0.windows.1.15.gaa56ade3205
Johannes Schindelin· Apr 21, 2018, 10:10 UTC · re: Johannes Schindelin · lore

[PATCH v3 4/4] Document the new color.* settings to colorize push errors/hints

Let's make it easier for users to find out how to customize these colors.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Documentation/config.txt | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
Show changes to Documentation/config.txt +28 −0
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2659153cb37..2cea0c6c899 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.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`,
@@ -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
-- 
2.17.0.windows.1.15.gaa56ade3205
Ævar Arnfjörð Bjarmason· Apr 6, 2018, 18:48 UTC · re: Johannes Schindelin · lore

Re: [PATCH 0/1] Colorize some errors on stderr

On Fri, Feb 16 2018, Johannes Schindelin wrote:
> This is an RFC because it tries to introduce a fundamental new color feature:
> Coloring messages *on stderr*.

I missed this the first time around, and don't have anything to add that others haven't covered. Just wanted to say I'd love to get this into git, it's a great UI improvement.

← back to recent threads