When we read a color value either from a config file or from
the command line, we use git_config_colorbool to convert it
from the tristate always/never/auto into a single yes/no
boolean value.
This has some timing implications with respect to starting
a pager.
If we start (or decide not to start) the pager before
checking the colorbool, everything is fine. Either isatty(1)
will give us the right information, or we will properly
check for pager_in_use().
However, if we decide to start a pager after we have checked
the colorbool, things are not so simple. If stdout is a tty,
then we will have already decided to use color. However, the
user may also have configured color.pager not to use color
with the pager. In this case, we need to actually turn off
color. Unfortunately, the pager code has no idea which color
variables were turned on (and there are many of them
throughout the code, and they may even have been manipulated
after the colorbool selection by something like "--color" on
the command line).
This bug can be seen any time a pager is started after
config and command line options are checked. This has
affected "git diff" since
89d07f7 (diff: don't run pager if
user asked for a diff style exit code, 2007-08-12). It has
also affect the log family since
1fda91b (Fix 'git log'
early pager startup error case, 2010-08-24).
This patch splits the notion of parsing a colorbool and
actually checking the configuration. The "use_color"
variables now have an additional possible value,
GIT_COLOR_AUTO. Users of the variable should use the new
"want_color()" wrapper, which will lazily determine and
cache the auto-color decision.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
static const char *branch_get_color(enum color_branch ix)
{
- if (branch_use_color > 0)
+ if (want_color(branch_use_color))
return branch_colors[ix];
return "";
}
get_colorbool_found = git_use_color_default;
}
+ get_colorbool_found = want_color(get_colorbool_found);
+
if (print) {
printf("%s\n", get_colorbool_found ? "true" : "false");
return 0;
static const char *get_color_code(int idx)
{
- if (showbranch_use_color)
+ if (want_color(showbranch_use_color))
return column_colors_ansi[idx % column_colors_ansi_max];
return "";
}
static const char *get_color_reset_code(void)
{
- if (showbranch_use_color)
+ if (want_color(showbranch_use_color))
return GIT_COLOR_RESET;
return "";
}
if (!strcasecmp(value, "always"))
return 1;
if (!strcasecmp(value, "auto"))
- goto auto_color;
+ return GIT_COLOR_AUTO;
}
if (!var)
return 0;
/* any normal truth value defaults to 'auto' */
- auto_color:
+ return GIT_COLOR_AUTO;
+}
+
+static int check_auto_color(void)
+{
if (color_stdout_is_tty < 0)
color_stdout_is_tty = isatty(1);
if (color_stdout_is_tty || (pager_in_use() && pager_use_color)) {
return 0;
}
+int want_color(int var)
+{
+ static int want_auto = -1;
+
+ if (var == GIT_COLOR_AUTO) {
+ if (want_auto < 0)
+ want_auto = check_auto_color();
+ return want_auto;
+ }
+ return var > 0;
+}
+
int git_color_default_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "color.ui")) {
/* A special value meaning "no color selected" */
#define GIT_COLOR_NIL "NIL"
+/*
+ * The first three are chosen to match common usage in the code, and what is
+ * returned from git_config_colorbool. The "auto" value can be returned from
+ * config_colorbool, and will be converted by want_color() into either 0 or 1.
+ */
+#define GIT_COLOR_UNKNOWN -1
+#define GIT_COLOR_NEVER 0
+#define GIT_COLOR_ALWAYS 1
+#define GIT_COLOR_AUTO 2
+
/*
* This variable stores the value of color.ui
*/
int git_color_default_config(const char *var, const char *value, void *cb);
int git_config_colorbool(const char *var, const char *value);
+int want_color(int var);
void color_parse(const char *value, const char *var, char *dst);
void color_parse_mem(const char *value, int len, const char *var, char *dst);
__attribute__((format (printf, 3, 4)))
size_two = fill_textconv(textconv_two, two, &data_two);
memset(&ecbdata, 0, sizeof(ecbdata));
- ecbdata.color_diff = o->use_color > 0;
+ ecbdata.color_diff = want_color(o->use_color);
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
ecbdata.opt = o;
const char *diff_get_color(int diff_use_color, enum color_diff ix)
{
- if (diff_use_color > 0)
+ if (want_color(diff_use_color))
return diff_colors[ix];
return "";
}
memset(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
- ecbdata.color_diff = o->use_color > 0;
+ ecbdata.color_diff = want_color(o->use_color);
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
break;
}
}
- if (o->use_color > 0) {
+ if (want_color(o->use_color)) {
struct diff_words_style *st = ecbdata.diff_words->style;
st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
*/
fill_metainfo(msg, name, other, one, two, o, p,
&must_show_header,
- o->use_color > 0 && !pgm);
+ want_color(o->use_color) && !pgm);
xfrm_msg = msg->len ? msg->buf : NULL;
}
options->use_color = 1;
else if (!prefixcmp(arg, "--color=")) {
int value = git_config_colorbool(NULL, arg+8);
- if (value == 0)
- options->use_color = 0;
- else if (value > 0)
- options->use_color = 1;
- else
+ if (value < 0)
return error("option `color' expects \"always\", \"auto\", or \"never\"");
+ options->use_color = value;
}
else if (!strcmp(arg, "--no-color"))
options->use_color = 0;
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
{
- if (graph->revs->diffopt.use_color <= 0)
+ if (!want_color(graph->revs->diffopt.use_color))
return column_colors_max;
return graph->default_column_color;
}
static void output_color(struct grep_opt *opt, const void *data, size_t size,
const char *color)
{
- if (opt->color && color && color[0]) {
+ if (want_color(opt->color) && color && color[0]) {
opt->output(opt, color, strlen(color));
opt->output(opt, data, size);
opt->output(opt, GIT_COLOR_RESET, strlen(GIT_COLOR_RESET));
static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
{
- if (decorate_use_color > 0)
+ if (want_color(decorate_use_color))
return decoration_colors[ix];
return "";
}
colorful paginated.out
'
+test_expect_success TTY 'colors are suppressed by color.pager' '
+ rm -f paginated.out &&
+ test_config color.ui auto &&
+ test_config color.pager false &&
+ (
+ TERM=vt100 &&
+ export TERM &&
+ test_terminal git log
+ ) &&
+ ! colorful paginated.out
+'
+
test_expect_success 'color when writing to a file intended for a pager' '
rm -f colorful.log &&
test_config color.ui auto ||
static const char *color(int slot, struct wt_status *s)
{
- const char *c = s->use_color > 0 ? s->color_palette[slot] : "";
+ const char *c = "";
+ if (want_color(s->use_color))
+ c = s->color_palette[slot];
if (slot == WT_STATUS_ONBRANCH && color_is_nil(c))
c = s->color_palette[WT_STATUS_HEADER];
return c;