color: use GIT_COLOR_* instead of numeric constants

Long ago Git's decision to show color for a subsytem was stored in a tri-state variable: it could be true (1), false (0), or unknown (-1). But since daa0c3d971 (color: delay auto-color decision until point of use, 2011-08-17) we want to carry around a new state, "auto", which bases the decision on the tty-ness of stdout (rather than collapsing that "auto" state to a true/false immediately). That commit introduced a set of GIT_COLOR_* defines to represent each state: UNKNOWN, ALWAYS, NEVER, and AUTO. But it only used the AUTO value, and left alone code using bare 0/1/-1 values. And of course since then we've grown many new spots that use those bare values. Let's switch all of these to use the named constants. That should make the code a bit easier to read, as it is more obvious that we're representing a color decision. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Sep 16, 2025 at 16:13 UTC 3c3e9b830383364316ba07730aecbc47a680b513
21 files changed +42 -40
add-interactive.c
+5 -4
@@ -42,7 +42,7 @@ static int check_color_config(struct repository *r, const char *var)
42 int ret;
43
44 if (repo_config_get_value(r, var, &value))
45 - ret = -1;
45 + ret = GIT_COLOR_UNKNOWN;
46 else
47 ret = git_config_colorbool(var, value);
48
@@ -51,7 +51,8 @@ static int check_color_config(struct repository *r, const char *var)
51 * the value parsed by git_color_config(), which may not have been
52 * called by the main command.
53 */
54 - if (ret < 0 && !repo_config_get_value(r, "color.ui", &value))
54 + if (ret == GIT_COLOR_UNKNOWN &&
55 + !repo_config_get_value(r, "color.ui", &value))
56 ret = git_config_colorbool("color.ui", value);
57
58 return want_color(ret);
@@ -130,8 +131,8 @@ void clear_add_i_state(struct add_i_state *s)
131 FREE_AND_NULL(s->interactive_diff_filter);
132 FREE_AND_NULL(s->interactive_diff_algorithm);
133 memset(s, 0, sizeof(*s));
133 - s->use_color_interactive = -1;
134 - s->use_color_diff = -1;
134 + s->use_color_interactive = GIT_COLOR_UNKNOWN;
135 + s->use_color_diff = GIT_COLOR_UNKNOWN;
136 }
137
138 /*
advice.c
+1 -1
@@ -7,7 +7,7 @@
7 #include "help.h"
8 #include "string-list.h"
9
10 -static int advice_use_color = -1;
10 +static int advice_use_color = GIT_COLOR_UNKNOWN;
11 static char advice_colors[][COLOR_MAXLEN] = {
12 GIT_COLOR_RESET,
13 GIT_COLOR_YELLOW, /* HINT */
builtin/add.c
+1 -1
@@ -200,7 +200,7 @@ static int edit_patch(struct repository *repo,
200
201 argc = setup_revisions(argc, argv, &rev, NULL);
202 rev.diffopt.output_format = DIFF_FORMAT_PATCH;
203 - rev.diffopt.use_color = 0;
203 + rev.diffopt.use_color = GIT_COLOR_NEVER;
204 rev.diffopt.flags.ignore_dirty_submodules = 1;
205 out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
206 rev.diffopt.file = xfdopen(out, "w");
builtin/am.c
+2 -2
@@ -1408,7 +1408,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
1408 rev_info.no_commit_id = 1;
1409 rev_info.diffopt.flags.binary = 1;
1410 rev_info.diffopt.flags.full_index = 1;
1411 - rev_info.diffopt.use_color = 0;
1411 + rev_info.diffopt.use_color = GIT_COLOR_NEVER;
1412 rev_info.diffopt.file = fp;
1413 rev_info.diffopt.close_file = 1;
1414 add_pending_object(&rev_info, &commit->object, "");
@@ -1441,7 +1441,7 @@ static void write_index_patch(const struct am_state *state)
1441 rev_info.disable_stdin = 1;
1442 rev_info.no_commit_id = 1;
1443 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
1444 - rev_info.diffopt.use_color = 0;
1444 + rev_info.diffopt.use_color = GIT_COLOR_NEVER;
1445 rev_info.diffopt.file = fp;
1446 rev_info.diffopt.close_file = 1;
1447 add_pending_object(&rev_info, &tree->object, "");
builtin/branch.c
+1 -1
@@ -46,7 +46,7 @@ static struct object_id head_oid;
46 static int recurse_submodules = 0;
47 static int submodule_propagate_branches = 0;
48
49 -static int branch_use_color = -1;
49 +static int branch_use_color = GIT_COLOR_UNKNOWN;
50 static char branch_colors[][COLOR_MAXLEN] = {
51 GIT_COLOR_RESET,
52 GIT_COLOR_NORMAL, /* PLAIN */
builtin/clean.c
+1 -1
@@ -64,7 +64,7 @@ static const char *color_interactive_slots[] = {
64 [CLEAN_COLOR_RESET] = "reset",
65 };
66
67 -static int clean_use_color = -1;
67 +static int clean_use_color = GIT_COLOR_UNKNOWN;
68 static char clean_colors[][COLOR_MAXLEN] = {
69 [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
70 [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
builtin/commit.c
+1 -1
@@ -1016,7 +1016,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
1016 status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); /* Add new line for clarity */
1017
1018 saved_color_setting = s->use_color;
1019 - s->use_color = 0;
1019 + s->use_color = GIT_COLOR_NEVER;
1020 committable = run_status(s->fp, index_file, prefix, 1, s);
1021 s->use_color = saved_color_setting;
1022 string_list_clear_func(&s->change, change_data_free);
builtin/config.c
+6 -6
@@ -594,23 +594,23 @@ static int get_colorbool(const struct config_location_options *opts,
594 {
595 struct get_colorbool_config_data data = {
596 .get_colorbool_slot = var,
597 - .get_colorbool_found = -1,
598 - .get_diff_color_found = -1,
599 - .get_color_ui_found = -1,
597 + .get_colorbool_found = GIT_COLOR_UNKNOWN,
598 + .get_diff_color_found = GIT_COLOR_UNKNOWN,
599 + .get_color_ui_found = GIT_COLOR_UNKNOWN,
600 };
601
602 config_with_options(git_get_colorbool_config, &data,
603 &opts->source, the_repository,
604 &opts->options);
605
606 - if (data.get_colorbool_found < 0) {
606 + if (data.get_colorbool_found == GIT_COLOR_UNKNOWN) {
607 if (!strcmp(data.get_colorbool_slot, "color.diff"))
608 data.get_colorbool_found = data.get_diff_color_found;
609 - if (data.get_colorbool_found < 0)
609 + if (data.get_colorbool_found == GIT_COLOR_UNKNOWN)
610 data.get_colorbool_found = data.get_color_ui_found;
611 }
612
613 - if (data.get_colorbool_found < 0)
613 + if (data.get_colorbool_found == GIT_COLOR_UNKNOWN)
614 /* default value if none found in config */
615 data.get_colorbool_found = GIT_COLOR_AUTO;
616
builtin/grep.c
+1 -1
@@ -1091,7 +1091,7 @@ int cmd_grep(int argc,
1091 if (show_in_pager == default_pager)
1092 show_in_pager = git_pager(the_repository, 1);
1093 if (show_in_pager) {
1094 - opt.color = 0;
1094 + opt.color = GIT_COLOR_NEVER;
1095 opt.name_only = 1;
1096 opt.null_following_name = 1;
1097 opt.output_priv = &path_list;
builtin/push.c
+1 -1
@@ -27,7 +27,7 @@ static const char * const push_usage[] = {
27 NULL,
28 };
29
30 -static int push_use_color = -1;
30 +static int push_use_color = GIT_COLOR_UNKNOWN;
31 static char push_colors[][COLOR_MAXLEN] = {
32 GIT_COLOR_RESET,
33 GIT_COLOR_RED, /* ERROR */
builtin/range-diff.c
+2 -1
@@ -6,6 +6,7 @@
6 #include "parse-options.h"
7 #include "range-diff.h"
8 #include "config.h"
9 +#include "color.h"
10
11
12 static const char * const builtin_range_diff_usage[] = {
@@ -66,7 +67,7 @@ int cmd_range_diff(int argc,
67
68 /* force color when --dual-color was used */
69 if (!simple_color)
69 - diffopt.use_color = 1;
70 + diffopt.use_color = GIT_COLOR_ALWAYS;
71
72 /* If `--diff-merges` was specified, imply `--merges` */
73 if (diff_merges_arg.nr) {
builtin/show-branch.c
+1 -1
@@ -29,7 +29,7 @@ static const char*const show_branch_usage[] = {
29 NULL
30 };
31
32 -static int showbranch_use_color = -1;
32 +static int showbranch_use_color = GIT_COLOR_UNKNOWN;
33
34 static struct strvec default_args = STRVEC_INIT;
35
color.c
+6 -6
@@ -373,19 +373,19 @@ int git_config_colorbool(const char *var, const char *value)
373 {
374 if (value) {
375 if (!strcasecmp(value, "never"))
376 - return 0;
376 + return GIT_COLOR_NEVER;
377 if (!strcasecmp(value, "always"))
378 - return 1;
378 + return GIT_COLOR_ALWAYS;
379 if (!strcasecmp(value, "auto"))
380 return GIT_COLOR_AUTO;
381 }
382
383 if (!var)
384 - return -1;
384 + return GIT_COLOR_UNKNOWN;
385
386 /* Missing or explicit false to turn off colorization */
387 if (!git_config_bool(var, value))
388 - return 0;
388 + return GIT_COLOR_NEVER;
389
390 /* any normal truth value defaults to 'auto' */
391 return GIT_COLOR_AUTO;
@@ -418,7 +418,7 @@ int want_color_fd(int fd, int var)
418 if (fd < 1 || fd >= ARRAY_SIZE(want_auto))
419 BUG("file descriptor out of range: %d", fd);
420
421 - if (var < 0)
421 + if (var == GIT_COLOR_UNKNOWN)
422 var = git_use_color_default;
423
424 if (var == GIT_COLOR_AUTO) {
@@ -426,7 +426,7 @@ int want_color_fd(int fd, int var)
426 want_auto[fd] = check_auto_color(fd);
427 return want_auto[fd];
428 }
429 - return var;
429 + return var == GIT_COLOR_ALWAYS;
430 }
431
432 int git_color_config(const char *var, const char *value, void *cb UNUSED)
diff.c
+3 -3
@@ -57,7 +57,7 @@ static int diff_detect_rename_default;
57 static int diff_indent_heuristic = 1;
58 static int diff_rename_limit_default = 1000;
59 static int diff_suppress_blank_empty;
60 -static int diff_use_color_default = -1;
60 +static int diff_use_color_default = GIT_COLOR_UNKNOWN;
61 static int diff_color_moved_default;
62 static int diff_color_moved_ws_default;
63 static int diff_context_default = 3;
@@ -5259,7 +5259,7 @@ static int diff_opt_color_words(const struct option *opt,
5259 struct diff_options *options = opt->value;
5260
5261 BUG_ON_OPT_NEG(unset);
5262 - options->use_color = 1;
5262 + options->use_color = GIT_COLOR_ALWAYS;
5263 options->word_diff = DIFF_WORDS_COLOR;
5264 options->word_regex = arg;
5265 return 0;
@@ -5581,7 +5581,7 @@ static int diff_opt_word_diff(const struct option *opt,
5581 if (!strcmp(arg, "plain"))
5582 options->word_diff = DIFF_WORDS_PLAIN;
5583 else if (!strcmp(arg, "color")) {
5584 - options->use_color = 1;
5584 + options->use_color = GIT_COLOR_ALWAYS;
5585 options->word_diff = DIFF_WORDS_COLOR;
5586 }
5587 else if (!strcmp(arg, "porcelain"))
grep.h
+1 -1
@@ -198,7 +198,7 @@ struct grep_opt {
198 [GREP_COLOR_SEP] = GIT_COLOR_CYAN, \
199 }, \
200 .only_matching = 0, \
201 - .color = -1, \
201 + .color = GIT_COLOR_UNKNOWN, \
202 .output = std_output, \
203 }
204
parse-options-cb.c
+1 -1
@@ -55,7 +55,7 @@ int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
55 if (!arg)
56 arg = unset ? "never" : (const char *)opt->defval;
57 value = git_config_colorbool(NULL, arg);
58 - if (value < 0)
58 + if (value == GIT_COLOR_UNKNOWN)
59 return error(_("option `%s' expects \"always\", \"auto\", or \"never\""),
60 opt->long_name);
61 *(int *)opt->value = value;
pretty.c
+1 -1
@@ -1462,7 +1462,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1462 } else {
1463 int ret = parse_color(sb, placeholder, c);
1464 if (ret)
1465 - c->auto_color = 0;
1465 + c->auto_color = GIT_COLOR_NEVER;
1466 /*
1467 * Otherwise, we decided to treat %C<unknown>
1468 * as a literal string, and the previous
ref-filter.h
+1 -1
@@ -111,7 +111,7 @@ struct ref_format {
111 .exclude = STRVEC_INIT, \
112 }
113 #define REF_FORMAT_INIT { \
114 - .use_color = -1, \
114 + .use_color = GIT_COLOR_UNKNOWN, \
115 }
116
117 /* Macros for checking --merged and --no-merged options */
sideband.c
+2 -2
@@ -29,14 +29,14 @@ static struct keyword_entry keywords[] = {
29 /* Returns a color setting (GIT_COLOR_NEVER, etc). */
30 static int use_sideband_colors(void)
31 {
32 - static int use_sideband_colors_cached = -1;
32 + static int use_sideband_colors_cached = GIT_COLOR_UNKNOWN;
33
34 const char *key = "color.remote";
35 struct strbuf sb = STRBUF_INIT;
36 const char *value;
37 int i;
38
39 - if (use_sideband_colors_cached >= 0)
39 + if (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)
40 return use_sideband_colors_cached;
41
42 if (!repo_config_get_string_tmp(the_repository, key, &value))
transport.c
+1 -1
@@ -30,7 +30,7 @@
30 #include "color.h"
31 #include "bundle-uri.h"
32
33 -static int transport_use_color = -1;
33 +static int transport_use_color = GIT_COLOR_UNKNOWN;
34 static char transport_colors[][COLOR_MAXLEN] = {
35 GIT_COLOR_RESET,
36 GIT_COLOR_RED /* REJECTED */
wt-status.c
+3 -3
@@ -148,7 +148,7 @@ void wt_status_prepare(struct repository *r, struct wt_status *s)
148 memcpy(s->color_palette, default_wt_status_colors,
149 sizeof(default_wt_status_colors));
150 s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
151 - s->use_color = -1;
151 + s->use_color = GIT_COLOR_UNKNOWN;
152 s->relative_paths = 1;
153 s->branch = refs_resolve_refdup(get_main_ref_store(the_repository),
154 "HEAD", 0, NULL, NULL);
@@ -1164,7 +1164,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
1164 * before.
1165 */
1166 if (s->fp != stdout) {
1167 - rev.diffopt.use_color = 0;
1167 + rev.diffopt.use_color = GIT_COLOR_NEVER;
1168 wt_status_add_cut_line(s);
1169 }
1170 if (s->verbose > 1 && s->committable) {
@@ -2164,7 +2164,7 @@ static void wt_shortstatus_print(struct wt_status *s)
2164
2165 static void wt_porcelain_print(struct wt_status *s)
2166 {
2167 - s->use_color = 0;
2167 + s->use_color = GIT_COLOR_NEVER;
2168 s->relative_paths = 0;
2169 s->prefix = NULL;
2170 s->no_gettext = 1;