Skip to content

Commit daa0c3d

Browse files
peffgitster
authored andcommitted
color: delay auto-color decision until point of use
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e269eb7 commit daa0c3d

File tree

11 files changed

+59
-19
lines changed

11 files changed

+59
-19
lines changed

builtin/branch.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
8888

8989
static const char *branch_get_color(enum color_branch ix)
9090
{
91-
if (branch_use_color > 0)
91+
if (want_color(branch_use_color))
9292
return branch_colors[ix];
9393
return "";
9494
}

builtin/config.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ static int get_colorbool(int print)
330330
get_colorbool_found = git_use_color_default;
331331
}
332332

333+
get_colorbool_found = want_color(get_colorbool_found);
334+
333335
if (print) {
334336
printf("%s\n", get_colorbool_found ? "true" : "false");
335337
return 0;

builtin/show-branch.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ static const char **default_arg;
2626

2727
static const char *get_color_code(int idx)
2828
{
29-
if (showbranch_use_color)
29+
if (want_color(showbranch_use_color))
3030
return column_colors_ansi[idx % column_colors_ansi_max];
3131
return "";
3232
}
3333

3434
static const char *get_color_reset_code(void)
3535
{
36-
if (showbranch_use_color)
36+
if (want_color(showbranch_use_color))
3737
return GIT_COLOR_RESET;
3838
return "";
3939
}

color.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ int git_config_colorbool(const char *var, const char *value)
166166
if (!strcasecmp(value, "always"))
167167
return 1;
168168
if (!strcasecmp(value, "auto"))
169-
goto auto_color;
169+
return GIT_COLOR_AUTO;
170170
}
171171

172172
if (!var)
@@ -177,7 +177,11 @@ int git_config_colorbool(const char *var, const char *value)
177177
return 0;
178178

179179
/* any normal truth value defaults to 'auto' */
180-
auto_color:
180+
return GIT_COLOR_AUTO;
181+
}
182+
183+
static int check_auto_color(void)
184+
{
181185
if (color_stdout_is_tty < 0)
182186
color_stdout_is_tty = isatty(1);
183187
if (color_stdout_is_tty || (pager_in_use() && pager_use_color)) {
@@ -188,6 +192,18 @@ int git_config_colorbool(const char *var, const char *value)
188192
return 0;
189193
}
190194

195+
int want_color(int var)
196+
{
197+
static int want_auto = -1;
198+
199+
if (var == GIT_COLOR_AUTO) {
200+
if (want_auto < 0)
201+
want_auto = check_auto_color();
202+
return want_auto;
203+
}
204+
return var > 0;
205+
}
206+
191207
int git_color_default_config(const char *var, const char *value, void *cb)
192208
{
193209
if (!strcmp(var, "color.ui")) {

color.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ struct strbuf;
4848
/* A special value meaning "no color selected" */
4949
#define GIT_COLOR_NIL "NIL"
5050

51+
/*
52+
* The first three are chosen to match common usage in the code, and what is
53+
* returned from git_config_colorbool. The "auto" value can be returned from
54+
* config_colorbool, and will be converted by want_color() into either 0 or 1.
55+
*/
56+
#define GIT_COLOR_UNKNOWN -1
57+
#define GIT_COLOR_NEVER 0
58+
#define GIT_COLOR_ALWAYS 1
59+
#define GIT_COLOR_AUTO 2
60+
5161
/*
5262
* This variable stores the value of color.ui
5363
*/
@@ -69,6 +79,7 @@ extern int color_stdout_is_tty;
6979
int git_color_default_config(const char *var, const char *value, void *cb);
7080

7181
int git_config_colorbool(const char *var, const char *value);
82+
int want_color(int var);
7283
void color_parse(const char *value, const char *var, char *dst);
7384
void color_parse_mem(const char *value, int len, const char *var, char *dst);
7485
__attribute__((format (printf, 3, 4)))

diff.c

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ static void emit_rewrite_diff(const char *name_a,
622622
size_two = fill_textconv(textconv_two, two, &data_two);
623623

624624
memset(&ecbdata, 0, sizeof(ecbdata));
625-
ecbdata.color_diff = o->use_color > 0;
625+
ecbdata.color_diff = want_color(o->use_color);
626626
ecbdata.found_changesp = &o->found_changes;
627627
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
628628
ecbdata.opt = o;
@@ -1003,7 +1003,7 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
10031003

10041004
const char *diff_get_color(int diff_use_color, enum color_diff ix)
10051005
{
1006-
if (diff_use_color > 0)
1006+
if (want_color(diff_use_color))
10071007
return diff_colors[ix];
10081008
return "";
10091009
}
@@ -2133,7 +2133,7 @@ static void builtin_diff(const char *name_a,
21332133
memset(&xecfg, 0, sizeof(xecfg));
21342134
memset(&ecbdata, 0, sizeof(ecbdata));
21352135
ecbdata.label_path = lbl;
2136-
ecbdata.color_diff = o->use_color > 0;
2136+
ecbdata.color_diff = want_color(o->use_color);
21372137
ecbdata.found_changesp = &o->found_changes;
21382138
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
21392139
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
@@ -2181,7 +2181,7 @@ static void builtin_diff(const char *name_a,
21812181
break;
21822182
}
21832183
}
2184-
if (o->use_color > 0) {
2184+
if (want_color(o->use_color)) {
21852185
struct diff_words_style *st = ecbdata.diff_words->style;
21862186
st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
21872187
st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
@@ -2831,7 +2831,7 @@ static void run_diff_cmd(const char *pgm,
28312831
*/
28322832
fill_metainfo(msg, name, other, one, two, o, p,
28332833
&must_show_header,
2834-
o->use_color > 0 && !pgm);
2834+
want_color(o->use_color) && !pgm);
28352835
xfrm_msg = msg->len ? msg->buf : NULL;
28362836
}
28372837

@@ -3374,12 +3374,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
33743374
options->use_color = 1;
33753375
else if (!prefixcmp(arg, "--color=")) {
33763376
int value = git_config_colorbool(NULL, arg+8);
3377-
if (value == 0)
3378-
options->use_color = 0;
3379-
else if (value > 0)
3380-
options->use_color = 1;
3381-
else
3377+
if (value < 0)
33823378
return error("option `color' expects \"always\", \"auto\", or \"never\"");
3379+
options->use_color = value;
33833380
}
33843381
else if (!strcmp(arg, "--no-color"))
33853382
options->use_color = 0;

graph.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph)
347347

348348
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
349349
{
350-
if (graph->revs->diffopt.use_color <= 0)
350+
if (!want_color(graph->revs->diffopt.use_color))
351351
return column_colors_max;
352352
return graph->default_column_color;
353353
}

grep.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ static int word_char(char ch)
430430
static void output_color(struct grep_opt *opt, const void *data, size_t size,
431431
const char *color)
432432
{
433-
if (opt->color && color && color[0]) {
433+
if (want_color(opt->color) && color && color[0]) {
434434
opt->output(opt, color, strlen(color));
435435
opt->output(opt, data, size);
436436
opt->output(opt, GIT_COLOR_RESET, strlen(GIT_COLOR_RESET));

log-tree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static char decoration_colors[][COLOR_MAXLEN] = {
3131

3232
static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
3333
{
34-
if (decorate_use_color > 0)
34+
if (want_color(decorate_use_color))
3535
return decoration_colors[ix];
3636
return "";
3737
}

t/t7006-pager.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,18 @@ test_expect_success TTY 'color when writing to a pager' '
167167
colorful paginated.out
168168
'
169169

170+
test_expect_success TTY 'colors are suppressed by color.pager' '
171+
rm -f paginated.out &&
172+
test_config color.ui auto &&
173+
test_config color.pager false &&
174+
(
175+
TERM=vt100 &&
176+
export TERM &&
177+
test_terminal git log
178+
) &&
179+
! colorful paginated.out
180+
'
181+
170182
test_expect_success 'color when writing to a file intended for a pager' '
171183
rm -f colorful.log &&
172184
test_config color.ui auto ||

0 commit comments

Comments
 (0)