Merge branch 'jk/ref-filter-colors'

"%C(color name)" in the pretty print format always produced ANSI
color escape codes, which was an early design mistake.  They now
honor the configuration (e.g. "color.ui = never") and also tty-ness
of the output medium.

* jk/ref-filter-colors:
  ref-filter: consult want_color() before emitting colors
  pretty: respect color settings for %C placeholders
  rev-list: pass diffopt->use_colors through to pretty-print
  for-each-ref: load config earlier
  color: check color.ui in git_default_config()
  ref-filter: pass ref_format struct to atom parsers
  ref-filter: factor out the parsing of sorting atoms
  ref-filter: make parse_ref_filter_atom a private function
  ref-filter: provide a function for parsing sort options
  ref-filter: move need_color_reset_at_eol into ref_format
  ref-filter: abstract ref format into its own struct
  ref-filter: simplify automatic color reset
  t: use test_decode_color rather than literal ANSI codes
  docs/for-each-ref: update pointer to color syntax
  check return value of verify_ref_format()
This commit is contained in:
Junio C Hamano 2017-08-11 13:26:58 -07:00
commit 15595ce438
22 changed files with 366 additions and 224 deletions

View File

@ -156,8 +156,10 @@ HEAD::
otherwise. otherwise.
color:: color::
Change output color. Followed by `:<colorname>`, where names Change output color. Followed by `:<colorname>`, where color
are described in `color.branch.*`. names are described under Values in the "CONFIGURATION FILE"
section of linkgit:git-config[1]. For example,
`%(color:bold red)`.
align:: align::
Left-, middle-, or right-align the content between Left-, middle-, or right-align the content between

View File

@ -173,13 +173,17 @@ endif::git-rev-list[]
- '%Cblue': switch color to blue - '%Cblue': switch color to blue
- '%Creset': reset color - '%Creset': reset color
- '%C(...)': color specification, as described under Values in the - '%C(...)': color specification, as described under Values in the
"CONFIGURATION FILE" section of linkgit:git-config[1]; "CONFIGURATION FILE" section of linkgit:git-config[1].
adding `auto,` at the beginning (e.g. `%C(auto,red)`) will emit By default, colors are shown only when enabled for log output (by
color only when colors are enabled for log output (by `color.diff`, `color.diff`, `color.ui`, or `--color`, and respecting the `auto`
`color.ui`, or `--color`, and respecting the `auto` settings of the settings of the former if we are going to a terminal). `%C(auto,...)`
former if we are going to a terminal). `auto` alone (i.e. is accepted as a historical synonym for the default (e.g.,
`%C(auto)`) will turn on auto coloring on the next placeholders `%C(auto,red)`). Specifying `%C(always,...) will show the colors
until the color is switched again. even when color is not otherwise enabled (though consider
just using `--color=always` to enable color for the whole output,
including this format and anything else git might color). `auto`
alone (i.e. `%C(auto)`) will turn on auto coloring on the next
placeholders until the color is switched again.
- '%m': left (`<`), right (`>`) or boundary (`-`) mark - '%m': left (`<`), right (`>`) or boundary (`-`) mark
- '%n': newline - '%n': newline
- '%%': a raw '%' - '%%': a raw '%'

View File

@ -92,7 +92,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
return config_error_nonbool(var); return config_error_nonbool(var);
return color_parse(value, branch_colors[slot]); return color_parse(value, branch_colors[slot]);
} }
return git_color_default_config(var, value, cb); return git_default_config(var, value, cb);
} }
static const char *branch_get_color(enum color_branch ix) static const char *branch_get_color(enum color_branch ix)
@ -383,7 +383,7 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
return strbuf_detach(&fmt, NULL); return strbuf_detach(&fmt, NULL);
} }
static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format) static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
{ {
int i; int i;
struct ref_array array; struct ref_array array;
@ -407,14 +407,17 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (filter->verbose) if (filter->verbose)
maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
if (!format) if (!format->format)
format = to_free = build_format(filter, maxwidth, remote_prefix); format->format = to_free = build_format(filter, maxwidth, remote_prefix);
verify_ref_format(format); format->use_color = branch_use_color;
if (verify_ref_format(format))
die(_("unable to parse format string"));
ref_array_sort(sorting, &array); ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) { for (i = 0; i < array.nr; i++) {
format_ref_array_item(array.items[i], format, 0, &out); format_ref_array_item(array.items[i], format, &out);
if (column_active(colopts)) { if (column_active(colopts)) {
assert(!filter->verbose && "--column and --verbose are incompatible"); assert(!filter->verbose && "--column and --verbose are incompatible");
/* format to a string_list to let print_columns() do its job */ /* format to a string_list to let print_columns() do its job */
@ -549,7 +552,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
struct ref_filter filter; struct ref_filter filter;
int icase = 0; int icase = 0;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
const char *format = NULL; struct ref_format format = REF_FORMAT_INIT;
struct option options[] = { struct option options[] = {
OPT_GROUP(N_("Generic options")), OPT_GROUP(N_("Generic options")),
@ -593,7 +596,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
N_("print only branches of the object"), 0, parse_opt_object_name N_("print only branches of the object"), 0, parse_opt_object_name
}, },
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")), OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END(), OPT_END(),
}; };
@ -667,7 +670,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!sorting) if (!sorting)
sorting = ref_default_sorting(); sorting = ref_default_sorting();
sorting->ignore_case = icase; sorting->ignore_case = icase;
print_ref_list(&filter, sorting, format); print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL); print_columns(&output, colopts, NULL);
string_list_clear(&output, 0); string_list_clear(&output, 0);
return 0; return 0;

View File

@ -125,8 +125,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
return 0; return 0;
} }
/* inspect the color.ui config variable and others */ return git_default_config(var, value, cb);
return git_color_default_config(var, value, cb);
} }
static const char *clean_get_color(enum color_clean ix) static const char *clean_get_color(enum color_clean ix)

View File

@ -17,25 +17,25 @@ static char const * const for_each_ref_usage[] = {
int cmd_for_each_ref(int argc, const char **argv, const char *prefix) int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{ {
int i; int i;
const char *format = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
int maxcount = 0, quote_style = 0, icase = 0; int maxcount = 0, icase = 0;
struct ref_array array; struct ref_array array;
struct ref_filter filter; struct ref_filter filter;
struct ref_format format = REF_FORMAT_INIT;
struct option opts[] = { struct option opts[] = {
OPT_BIT('s', "shell", &quote_style, OPT_BIT('s', "shell", &format.quote_style,
N_("quote placeholders suitably for shells"), QUOTE_SHELL), N_("quote placeholders suitably for shells"), QUOTE_SHELL),
OPT_BIT('p', "perl", &quote_style, OPT_BIT('p', "perl", &format.quote_style,
N_("quote placeholders suitably for perl"), QUOTE_PERL), N_("quote placeholders suitably for perl"), QUOTE_PERL),
OPT_BIT(0 , "python", &quote_style, OPT_BIT(0 , "python", &format.quote_style,
N_("quote placeholders suitably for python"), QUOTE_PYTHON), N_("quote placeholders suitably for python"), QUOTE_PYTHON),
OPT_BIT(0 , "tcl", &quote_style, OPT_BIT(0 , "tcl", &format.quote_style,
N_("quote placeholders suitably for Tcl"), QUOTE_TCL), N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
OPT_GROUP(""), OPT_GROUP(""),
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
N_("field name to sort on"), &parse_opt_ref_sorting), N_("field name to sort on"), &parse_opt_ref_sorting),
OPT_CALLBACK(0, "points-at", &filter.points_at, OPT_CALLBACK(0, "points-at", &filter.points_at,
@ -52,16 +52,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
memset(&array, 0, sizeof(array)); memset(&array, 0, sizeof(array));
memset(&filter, 0, sizeof(filter)); memset(&filter, 0, sizeof(filter));
format.format = "%(objectname) %(objecttype)\t%(refname)";
git_config(git_default_config, NULL);
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
if (maxcount < 0) { if (maxcount < 0) {
error("invalid --count argument: `%d'", maxcount); error("invalid --count argument: `%d'", maxcount);
usage_with_options(for_each_ref_usage, opts); usage_with_options(for_each_ref_usage, opts);
} }
if (HAS_MULTI_BITS(quote_style)) { if (HAS_MULTI_BITS(format.quote_style)) {
error("more than one quoting style?"); error("more than one quoting style?");
usage_with_options(for_each_ref_usage, opts); usage_with_options(for_each_ref_usage, opts);
} }
if (verify_ref_format(format)) if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts); usage_with_options(for_each_ref_usage, opts);
if (!sorting) if (!sorting)
@ -69,9 +73,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
sorting->ignore_case = icase; sorting->ignore_case = icase;
filter.ignore_case = icase; filter.ignore_case = icase;
/* for warn_ambiguous_refs */
git_config(git_default_config, NULL);
filter.name_patterns = argv; filter.name_patterns = argv;
filter.match_as_path = 1; filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN); filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
@ -80,7 +81,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (!maxcount || array.nr < maxcount) if (!maxcount || array.nr < maxcount)
maxcount = array.nr; maxcount = array.nr;
for (i = 0; i < maxcount; i++) for (i = 0; i < maxcount; i++)
show_ref_array_item(array.items[i], format, quote_style); show_ref_array_item(array.items[i], &format);
ref_array_clear(&array); ref_array_clear(&array);
return 0; return 0;
} }

View File

@ -284,7 +284,7 @@ static int wait_all(void)
static int grep_cmd_config(const char *var, const char *value, void *cb) static int grep_cmd_config(const char *var, const char *value, void *cb)
{ {
int st = grep_config(var, value, cb); int st = grep_config(var, value, cb);
if (git_color_default_config(var, value, cb) < 0) if (git_default_config(var, value, cb) < 0)
st = -1; st = -1;
if (!strcmp(var, "grep.threads")) { if (!strcmp(var, "grep.threads")) {

View File

@ -122,6 +122,7 @@ static void show_commit(struct commit *commit, void *data)
ctx.date_mode_explicit = revs->date_mode_explicit; ctx.date_mode_explicit = revs->date_mode_explicit;
ctx.fmt = revs->commit_format; ctx.fmt = revs->commit_format;
ctx.output_encoding = get_log_output_encoding(); ctx.output_encoding = get_log_output_encoding();
ctx.color = revs->diffopt.use_color;
pretty_print_commit(&ctx, commit, &buf); pretty_print_commit(&ctx, commit, &buf);
if (buf.len) { if (buf.len) {
if (revs->commit_format != CMIT_FMT_ONELINE) if (revs->commit_format != CMIT_FMT_ONELINE)

View File

@ -554,7 +554,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
return 0; return 0;
} }
return git_color_default_config(var, value, cb); return git_default_config(var, value, cb);
} }
static int omit_in_dense(struct commit *commit, struct commit **rev, int n) static int omit_in_dense(struct commit *commit, struct commit **rev, int n)

View File

@ -32,7 +32,8 @@ static const char * const git_tag_usage[] = {
static unsigned int colopts; static unsigned int colopts;
static int force_sign_annotate; static int force_sign_annotate;
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format) static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
struct ref_format *format)
{ {
struct ref_array array; struct ref_array array;
char *to_free = NULL; char *to_free = NULL;
@ -43,23 +44,24 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
if (filter->lines == -1) if (filter->lines == -1)
filter->lines = 0; filter->lines = 0;
if (!format) { if (!format->format) {
if (filter->lines) { if (filter->lines) {
to_free = xstrfmt("%s %%(contents:lines=%d)", to_free = xstrfmt("%s %%(contents:lines=%d)",
"%(align:15)%(refname:lstrip=2)%(end)", "%(align:15)%(refname:lstrip=2)%(end)",
filter->lines); filter->lines);
format = to_free; format->format = to_free;
} else } else
format = "%(refname:lstrip=2)"; format->format = "%(refname:lstrip=2)";
} }
verify_ref_format(format); if (verify_ref_format(format))
die(_("unable to parse format string"));
filter->with_commit_tag_algo = 1; filter->with_commit_tag_algo = 1;
filter_refs(&array, filter, FILTER_REFS_TAGS); filter_refs(&array, filter, FILTER_REFS_TAGS);
ref_array_sort(sorting, &array); ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) for (i = 0; i < array.nr; i++)
show_ref_array_item(array.items[i], format, 0); show_ref_array_item(array.items[i], format);
ref_array_clear(&array); ref_array_clear(&array);
free(to_free); free(to_free);
@ -105,17 +107,17 @@ static int verify_tag(const char *name, const char *ref,
const struct object_id *oid, const void *cb_data) const struct object_id *oid, const void *cb_data)
{ {
int flags; int flags;
const char *fmt_pretty = cb_data; const struct ref_format *format = cb_data;
flags = GPG_VERIFY_VERBOSE; flags = GPG_VERIFY_VERBOSE;
if (fmt_pretty) if (format->format)
flags = GPG_VERIFY_OMIT_STATUS; flags = GPG_VERIFY_OMIT_STATUS;
if (gpg_verify_tag(oid, name, flags)) if (gpg_verify_tag(oid, name, flags))
return -1; return -1;
if (fmt_pretty) if (format->format)
pretty_print_ref(name, oid->hash, fmt_pretty); pretty_print_ref(name, oid->hash, format);
return 0; return 0;
} }
@ -134,30 +136,6 @@ static const char tag_template_nocleanup[] =
"Lines starting with '%c' will be kept; you may remove them" "Lines starting with '%c' will be kept; you may remove them"
" yourself if you want to.\n"); " yourself if you want to.\n");
/* Parse arg given and add it the ref_sorting array */
static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
{
struct ref_sorting *s;
int len;
s = xcalloc(1, sizeof(*s));
s->next = *sorting_tail;
*sorting_tail = s;
if (*arg == '-') {
s->reverse = 1;
arg++;
}
if (skip_prefix(arg, "version:", &arg) ||
skip_prefix(arg, "v:", &arg))
s->version = 1;
len = strlen(arg);
s->atom = parse_ref_filter_atom(arg, arg+len);
return 0;
}
static int git_tag_config(const char *var, const char *value, void *cb) static int git_tag_config(const char *var, const char *value, void *cb)
{ {
int status; int status;
@ -166,7 +144,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "tag.sort")) { if (!strcmp(var, "tag.sort")) {
if (!value) if (!value)
return config_error_nonbool(var); return config_error_nonbool(var);
parse_sorting_string(value, sorting_tail); parse_ref_sorting(sorting_tail, value);
return 0; return 0;
} }
@ -392,7 +370,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf err = STRBUF_INIT; struct strbuf err = STRBUF_INIT;
struct ref_filter filter; struct ref_filter filter;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
const char *format = NULL; struct ref_format format = REF_FORMAT_INIT;
int icase = 0; int icase = 0;
struct option options[] = { struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
@ -431,7 +409,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT, N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
parse_opt_object_name, (intptr_t) "HEAD" parse_opt_object_name, (intptr_t) "HEAD"
}, },
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), OPT_STRING( 0 , "format", &format.format, N_("format"),
N_("format to use for the output")),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")), OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END() OPT_END()
}; };
@ -483,7 +462,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
run_column_filter(colopts, &copts); run_column_filter(colopts, &copts);
} }
filter.name_patterns = argv; filter.name_patterns = argv;
ret = list_tags(&filter, sorting, format); ret = list_tags(&filter, sorting, &format);
if (column_active(colopts)) if (column_active(colopts))
stop_column_filter(); stop_column_filter();
return ret; return ret;
@ -501,9 +480,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (cmdmode == 'd') if (cmdmode == 'd')
return for_each_tag_name(argv, delete_tag, NULL); return for_each_tag_name(argv, delete_tag, NULL);
if (cmdmode == 'v') { if (cmdmode == 'v') {
if (format) if (format.format && verify_ref_format(&format))
verify_ref_format(format); usage_with_options(git_tag_usage, options);
return for_each_tag_name(argv, verify_tag, format); return for_each_tag_name(argv, verify_tag, &format);
} }
if (msg.given || msgfile) { if (msg.given || msgfile) {

View File

@ -32,11 +32,11 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{ {
int i = 1, verbose = 0, had_error = 0; int i = 1, verbose = 0, had_error = 0;
unsigned flags = 0; unsigned flags = 0;
char *fmt_pretty = NULL; struct ref_format format = REF_FORMAT_INIT;
const struct option verify_tag_options[] = { const struct option verify_tag_options[] = {
OPT__VERBOSE(&verbose, N_("print tag contents")), OPT__VERBOSE(&verbose, N_("print tag contents")),
OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW),
OPT_STRING( 0 , "format", &fmt_pretty, N_("format"), N_("format to use for the output")), OPT_STRING(0, "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END() OPT_END()
}; };
@ -50,8 +50,10 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
if (verbose) if (verbose)
flags |= GPG_VERIFY_VERBOSE; flags |= GPG_VERIFY_VERBOSE;
if (fmt_pretty) { if (format.format) {
verify_ref_format(fmt_pretty); if (verify_ref_format(&format))
usage_with_options(verify_tag_usage,
verify_tag_options);
flags |= GPG_VERIFY_OMIT_STATUS; flags |= GPG_VERIFY_OMIT_STATUS;
} }
@ -69,8 +71,8 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
continue; continue;
} }
if (fmt_pretty) if (format.format)
pretty_print_ref(name, oid.hash, fmt_pretty); pretty_print_ref(name, oid.hash, &format);
} }
return had_error; return had_error;
} }

View File

@ -361,14 +361,6 @@ int git_color_config(const char *var, const char *value, void *cb)
return 0; return 0;
} }
int git_color_default_config(const char *var, const char *value, void *cb)
{
if (git_color_config(var, value, cb) < 0)
return -1;
return git_default_config(var, value, cb);
}
void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb) void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
{ {
if (*color) if (*color)

View File

@ -16,6 +16,7 @@
#include "string-list.h" #include "string-list.h"
#include "utf8.h" #include "utf8.h"
#include "dir.h" #include "dir.h"
#include "color.h"
struct config_source { struct config_source {
struct config_source *prev; struct config_source *prev;
@ -1350,6 +1351,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
if (starts_with(var, "advice.")) if (starts_with(var, "advice."))
return git_default_advice_config(var, value); return git_default_advice_config(var, value);
if (git_color_config(var, value, dummy) < 0)
return -1;
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
pager_use_color = git_config_bool(var,value); pager_use_color = git_config_bool(var,value);
return 0; return 0;

3
diff.c
View File

@ -299,9 +299,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0; return 0;
} }
if (git_color_config(var, value, cb) < 0)
return -1;
return git_diff_basic_config(var, value, cb); return git_diff_basic_config(var, value, cb);
} }

View File

@ -947,6 +947,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
struct format_commit_context *c) struct format_commit_context *c)
{ {
const char *rest = placeholder; const char *rest = placeholder;
const char *basic_color = NULL;
if (placeholder[1] == '(') { if (placeholder[1] == '(') {
const char *begin = placeholder + 2; const char *begin = placeholder + 2;
@ -955,23 +956,41 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
if (!end) if (!end)
return 0; return 0;
if (skip_prefix(begin, "auto,", &begin)) { if (skip_prefix(begin, "auto,", &begin)) {
if (!want_color(c->pretty_ctx->color)) if (!want_color(c->pretty_ctx->color))
return end - placeholder + 1; return end - placeholder + 1;
} else if (skip_prefix(begin, "always,", &begin)) {
/* nothing to do; we do not respect want_color at all */
} else {
/* the default is the same as "auto" */
if (!want_color(c->pretty_ctx->color))
return end - placeholder + 1;
} }
if (color_parse_mem(begin, end - begin, color) < 0) if (color_parse_mem(begin, end - begin, color) < 0)
die(_("unable to parse --pretty format")); die(_("unable to parse --pretty format"));
strbuf_addstr(sb, color); strbuf_addstr(sb, color);
return end - placeholder + 1; return end - placeholder + 1;
} }
/*
* We handle things like "%C(red)" above; for historical reasons, there
* are a few colors that can be specified without parentheses (and
* they cannot support things like "auto" or "always" at all).
*/
if (skip_prefix(placeholder + 1, "red", &rest)) if (skip_prefix(placeholder + 1, "red", &rest))
strbuf_addstr(sb, GIT_COLOR_RED); basic_color = GIT_COLOR_RED;
else if (skip_prefix(placeholder + 1, "green", &rest)) else if (skip_prefix(placeholder + 1, "green", &rest))
strbuf_addstr(sb, GIT_COLOR_GREEN); basic_color = GIT_COLOR_GREEN;
else if (skip_prefix(placeholder + 1, "blue", &rest)) else if (skip_prefix(placeholder + 1, "blue", &rest))
strbuf_addstr(sb, GIT_COLOR_BLUE); basic_color = GIT_COLOR_BLUE;
else if (skip_prefix(placeholder + 1, "reset", &rest)) else if (skip_prefix(placeholder + 1, "reset", &rest))
strbuf_addstr(sb, GIT_COLOR_RESET); basic_color = GIT_COLOR_RESET;
if (basic_color && want_color(c->pretty_ctx->color))
strbuf_addstr(sb, basic_color);
return rest - placeholder; return rest - placeholder;
} }

View File

@ -97,14 +97,19 @@ static struct used_atom {
} u; } u;
} *used_atom; } *used_atom;
static int used_atom_cnt, need_tagged, need_symref; static int used_atom_cnt, need_tagged, need_symref;
static int need_color_reset_at_eol;
static void color_atom_parser(struct used_atom *atom, const char *color_value) static void color_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *color_value)
{ {
if (!color_value) if (!color_value)
die(_("expected format: %%(color:<color>)")); die(_("expected format: %%(color:<color>)"));
if (color_parse(color_value, atom->u.color) < 0) if (color_parse(color_value, atom->u.color) < 0)
die(_("unrecognized color: %%(color:%s)"), color_value); die(_("unrecognized color: %%(color:%s)"), color_value);
/*
* We check this after we've parsed the color, which lets us complain
* about syntactically bogus color names even if they won't be used.
*/
if (!want_color(format->use_color))
color_parse("", atom->u.color);
} }
static void refname_atom_parser_internal(struct refname_atom *atom, static void refname_atom_parser_internal(struct refname_atom *atom,
@ -127,7 +132,7 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
die(_("unrecognized %%(%s) argument: %s"), name, arg); die(_("unrecognized %%(%s) argument: %s"), name, arg);
} }
static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) static void remote_ref_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
struct string_list params = STRING_LIST_INIT_DUP; struct string_list params = STRING_LIST_INIT_DUP;
int i; int i;
@ -161,28 +166,28 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
string_list_clear(&params, 0); string_list_clear(&params, 0);
} }
static void body_atom_parser(struct used_atom *atom, const char *arg) static void body_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
if (arg) if (arg)
die(_("%%(body) does not take arguments")); die(_("%%(body) does not take arguments"));
atom->u.contents.option = C_BODY_DEP; atom->u.contents.option = C_BODY_DEP;
} }
static void subject_atom_parser(struct used_atom *atom, const char *arg) static void subject_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
if (arg) if (arg)
die(_("%%(subject) does not take arguments")); die(_("%%(subject) does not take arguments"));
atom->u.contents.option = C_SUB; atom->u.contents.option = C_SUB;
} }
static void trailers_atom_parser(struct used_atom *atom, const char *arg) static void trailers_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
if (arg) if (arg)
die(_("%%(trailers) does not take arguments")); die(_("%%(trailers) does not take arguments"));
atom->u.contents.option = C_TRAILERS; atom->u.contents.option = C_TRAILERS;
} }
static void contents_atom_parser(struct used_atom *atom, const char *arg) static void contents_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
if (!arg) if (!arg)
atom->u.contents.option = C_BARE; atom->u.contents.option = C_BARE;
@ -202,7 +207,7 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)
die(_("unrecognized %%(contents) argument: %s"), arg); die(_("unrecognized %%(contents) argument: %s"), arg);
} }
static void objectname_atom_parser(struct used_atom *atom, const char *arg) static void objectname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
if (!arg) if (!arg)
atom->u.objectname.option = O_FULL; atom->u.objectname.option = O_FULL;
@ -219,7 +224,7 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)
die(_("unrecognized %%(objectname) argument: %s"), arg); die(_("unrecognized %%(objectname) argument: %s"), arg);
} }
static void refname_atom_parser(struct used_atom *atom, const char *arg) static void refname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
refname_atom_parser_internal(&atom->u.refname, arg, atom->name); refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
} }
@ -235,7 +240,7 @@ static align_type parse_align_position(const char *s)
return -1; return -1;
} }
static void align_atom_parser(struct used_atom *atom, const char *arg) static void align_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
struct align *align = &atom->u.align; struct align *align = &atom->u.align;
struct string_list params = STRING_LIST_INIT_DUP; struct string_list params = STRING_LIST_INIT_DUP;
@ -274,7 +279,7 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)
string_list_clear(&params, 0); string_list_clear(&params, 0);
} }
static void if_atom_parser(struct used_atom *atom, const char *arg) static void if_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
if (!arg) { if (!arg) {
atom->u.if_then_else.cmp_status = COMPARE_NONE; atom->u.if_then_else.cmp_status = COMPARE_NONE;
@ -288,7 +293,7 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)
} }
} }
static void head_atom_parser(struct used_atom *atom, const char *arg) static void head_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{ {
struct object_id unused; struct object_id unused;
@ -298,7 +303,7 @@ static void head_atom_parser(struct used_atom *atom, const char *arg)
static struct { static struct {
const char *name; const char *name;
cmp_type cmp_type; cmp_type cmp_type;
void (*parser)(struct used_atom *atom, const char *arg); void (*parser)(const struct ref_format *format, struct used_atom *atom, const char *arg);
} valid_atom[] = { } valid_atom[] = {
{ "refname" , FIELD_STR, refname_atom_parser }, { "refname" , FIELD_STR, refname_atom_parser },
{ "objecttype" }, { "objecttype" },
@ -365,7 +370,8 @@ struct atom_value {
/* /*
* Used to parse format string and sort specifiers * Used to parse format string and sort specifiers
*/ */
int parse_ref_filter_atom(const char *atom, const char *ep) static int parse_ref_filter_atom(const struct ref_format *format,
const char *atom, const char *ep)
{ {
const char *sp; const char *sp;
const char *arg; const char *arg;
@ -413,7 +419,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
arg = used_atom[at].name + (arg - atom) + 1; arg = used_atom[at].name + (arg - atom) + 1;
memset(&used_atom[at].u, 0, sizeof(used_atom[at].u)); memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
if (valid_atom[i].parser) if (valid_atom[i].parser)
valid_atom[i].parser(&used_atom[at], arg); valid_atom[i].parser(format, &used_atom[at], arg);
if (*atom == '*') if (*atom == '*')
need_tagged = 1; need_tagged = 1;
if (!strcmp(valid_atom[i].name, "symref")) if (!strcmp(valid_atom[i].name, "symref"))
@ -657,24 +663,26 @@ static const char *find_next(const char *cp)
* Make sure the format string is well formed, and parse out * Make sure the format string is well formed, and parse out
* the used atoms. * the used atoms.
*/ */
int verify_ref_format(const char *format) int verify_ref_format(struct ref_format *format)
{ {
const char *cp, *sp; const char *cp, *sp;
need_color_reset_at_eol = 0; format->need_color_reset_at_eol = 0;
for (cp = format; *cp && (sp = find_next(cp)); ) { for (cp = format->format; *cp && (sp = find_next(cp)); ) {
const char *color, *ep = strchr(sp, ')'); const char *color, *ep = strchr(sp, ')');
int at; int at;
if (!ep) if (!ep)
return error(_("malformed format string %s"), sp); return error(_("malformed format string %s"), sp);
/* sp points at "%(" and ep points at the closing ")" */ /* sp points at "%(" and ep points at the closing ")" */
at = parse_ref_filter_atom(sp + 2, ep); at = parse_ref_filter_atom(format, sp + 2, ep);
cp = ep + 1; cp = ep + 1;
if (skip_prefix(used_atom[at].name, "color:", &color)) if (skip_prefix(used_atom[at].name, "color:", &color))
need_color_reset_at_eol = !!strcmp(color, "reset"); format->need_color_reset_at_eol = !!strcmp(color, "reset");
} }
if (format->need_color_reset_at_eol && !want_color(format->use_color))
format->need_color_reset_at_eol = 0;
return 0; return 0;
} }
@ -2060,35 +2068,34 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
} }
} }
void format_ref_array_item(struct ref_array_item *info, const char *format, void format_ref_array_item(struct ref_array_item *info,
int quote_style, struct strbuf *final_buf) const struct ref_format *format,
struct strbuf *final_buf)
{ {
const char *cp, *sp, *ep; const char *cp, *sp, *ep;
struct ref_formatting_state state = REF_FORMATTING_STATE_INIT; struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
state.quote_style = quote_style; state.quote_style = format->quote_style;
push_stack_element(&state.stack); push_stack_element(&state.stack);
for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) { for (cp = format->format; *cp && (sp = find_next(cp)); cp = ep + 1) {
struct atom_value *atomv; struct atom_value *atomv;
ep = strchr(sp, ')'); ep = strchr(sp, ')');
if (cp < sp) if (cp < sp)
append_literal(cp, sp, &state); append_literal(cp, sp, &state);
get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv); get_ref_atom_value(info,
parse_ref_filter_atom(format, sp + 2, ep),
&atomv);
atomv->handler(atomv, &state); atomv->handler(atomv, &state);
} }
if (*cp) { if (*cp) {
sp = cp + strlen(cp); sp = cp + strlen(cp);
append_literal(cp, sp, &state); append_literal(cp, sp, &state);
} }
if (need_color_reset_at_eol) { if (format->need_color_reset_at_eol) {
struct atom_value resetv; struct atom_value resetv;
char color[COLOR_MAXLEN] = ""; resetv.s = GIT_COLOR_RESET;
if (color_parse("reset", color) < 0)
die("BUG: couldn't parse 'reset' as a color");
resetv.s = color;
append_atom(&resetv, &state); append_atom(&resetv, &state);
} }
if (state.stack->prev) if (state.stack->prev)
@ -2097,26 +2104,38 @@ void format_ref_array_item(struct ref_array_item *info, const char *format,
pop_stack_element(&state.stack); pop_stack_element(&state.stack);
} }
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style) void show_ref_array_item(struct ref_array_item *info,
const struct ref_format *format)
{ {
struct strbuf final_buf = STRBUF_INIT; struct strbuf final_buf = STRBUF_INIT;
format_ref_array_item(info, format, quote_style, &final_buf); format_ref_array_item(info, format, &final_buf);
fwrite(final_buf.buf, 1, final_buf.len, stdout); fwrite(final_buf.buf, 1, final_buf.len, stdout);
strbuf_release(&final_buf); strbuf_release(&final_buf);
putchar('\n'); putchar('\n');
} }
void pretty_print_ref(const char *name, const unsigned char *sha1, void pretty_print_ref(const char *name, const unsigned char *sha1,
const char *format) const struct ref_format *format)
{ {
struct ref_array_item *ref_item; struct ref_array_item *ref_item;
ref_item = new_ref_array_item(name, sha1, 0); ref_item = new_ref_array_item(name, sha1, 0);
ref_item->kind = ref_kind_from_refname(name); ref_item->kind = ref_kind_from_refname(name);
show_ref_array_item(ref_item, format, 0); show_ref_array_item(ref_item, format);
free_array_item(ref_item); free_array_item(ref_item);
} }
static int parse_sorting_atom(const char *atom)
{
/*
* This parses an atom using a dummy ref_format, since we don't
* actually care about the formatting details.
*/
struct ref_format dummy = REF_FORMAT_INIT;
const char *end = atom + strlen(atom);
return parse_ref_filter_atom(&dummy, atom, end);
}
/* If no sorting option is given, use refname to sort as default */ /* If no sorting option is given, use refname to sort as default */
struct ref_sorting *ref_default_sorting(void) struct ref_sorting *ref_default_sorting(void)
{ {
@ -2125,18 +2144,13 @@ struct ref_sorting *ref_default_sorting(void)
struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting)); struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
sorting->next = NULL; sorting->next = NULL;
sorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name)); sorting->atom = parse_sorting_atom(cstr_name);
return sorting; return sorting;
} }
int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset) void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
{ {
struct ref_sorting **sorting_tail = opt->value;
struct ref_sorting *s; struct ref_sorting *s;
int len;
if (!arg) /* should --no-sort void the list ? */
return -1;
s = xcalloc(1, sizeof(*s)); s = xcalloc(1, sizeof(*s));
s->next = *sorting_tail; s->next = *sorting_tail;
@ -2149,8 +2163,14 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
if (skip_prefix(arg, "version:", &arg) || if (skip_prefix(arg, "version:", &arg) ||
skip_prefix(arg, "v:", &arg)) skip_prefix(arg, "v:", &arg))
s->version = 1; s->version = 1;
len = strlen(arg); s->atom = parse_sorting_atom(arg);
s->atom = parse_ref_filter_atom(arg, arg+len); }
int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
{
if (!arg) /* should --no-sort void the list ? */
return -1;
parse_ref_sorting(opt->value, arg);
return 0; return 0;
} }

View File

@ -72,6 +72,21 @@ struct ref_filter {
verbose; verbose;
}; };
struct ref_format {
/*
* Set these to define the format; make sure you call
* verify_ref_format() afterwards to finalize.
*/
const char *format;
int quote_style;
int use_color;
/* Internal state to ref-filter */
int need_color_reset_at_eol;
};
#define REF_FORMAT_INIT { NULL, 0, -1 }
/* Macros for checking --merged and --no-merged options */ /* Macros for checking --merged and --no-merged options */
#define _OPT_MERGED_NO_MERGED(option, filter, h) \ #define _OPT_MERGED_NO_MERGED(option, filter, h) \
{ OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \ { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
@ -90,17 +105,18 @@ struct ref_filter {
int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type); int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
/* Clear all memory allocated to ref_array */ /* Clear all memory allocated to ref_array */
void ref_array_clear(struct ref_array *array); void ref_array_clear(struct ref_array *array);
/* Parse format string and sort specifiers */
int parse_ref_filter_atom(const char *atom, const char *ep);
/* Used to verify if the given format is correct and to parse out the used atoms */ /* Used to verify if the given format is correct and to parse out the used atoms */
int verify_ref_format(const char *format); int verify_ref_format(struct ref_format *format);
/* Sort the given ref_array as per the ref_sorting provided */ /* Sort the given ref_array as per the ref_sorting provided */
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array); void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
/* Based on the given format and quote_style, fill the strbuf */ /* Based on the given format and quote_style, fill the strbuf */
void format_ref_array_item(struct ref_array_item *info, const char *format, void format_ref_array_item(struct ref_array_item *info,
int quote_style, struct strbuf *final_buf); const struct ref_format *format,
struct strbuf *final_buf);
/* Print the ref using the given format and quote_style */ /* Print the ref using the given format and quote_style */
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style); void show_ref_array_item(struct ref_array_item *info, const struct ref_format *format);
/* Parse a single sort specifier and add it to the list */
void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *atom);
/* Callback function for parsing the sort option */ /* Callback function for parsing the sort option */
int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset); int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
/* Default sort option based on refname */ /* Default sort option based on refname */
@ -117,6 +133,6 @@ void setup_ref_filter_porcelain_msg(void);
* name must be a fully qualified refname. * name must be a fully qualified refname.
*/ */
void pretty_print_ref(const char *name, const unsigned char *sha1, void pretty_print_ref(const char *name, const unsigned char *sha1,
const char *format); const struct ref_format *format);
#endif /* REF_FILTER_H */ #endif /* REF_FILTER_H */

View File

@ -2,6 +2,7 @@
test_description='git branch display tests' test_description='git branch display tests'
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success 'make commits' ' test_expect_success 'make commits' '
echo content >file && echo content >file &&
@ -239,4 +240,34 @@ test_expect_success 'git branch --format option' '
test_i18ncmp expect actual test_i18ncmp expect actual
' '
test_expect_success "set up color tests" '
echo "<RED>master<RESET>" >expect.color &&
echo "master" >expect.bare &&
color_args="--format=%(color:red)%(refname:short) --list master"
'
test_expect_success '%(color) omitted without tty' '
TERM=vt100 git branch $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.bare actual
'
test_expect_success TTY '%(color) present with tty' '
test_terminal env TERM=vt100 git branch $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success 'color.branch=always overrides auto-color' '
git -c color.branch=always branch $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success '--color overrides auto-color' '
git branch --color $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_done test_done

View File

@ -7,11 +7,6 @@ test_description='Test for "git log --decorate" colors'
. ./test-lib.sh . ./test-lib.sh
get_color ()
{
git config --get-color no.such.slot "$1"
}
test_expect_success setup ' test_expect_success setup '
git config diff.color.commit yellow && git config diff.color.commit yellow &&
git config color.decorate.branch green && git config color.decorate.branch green &&
@ -20,14 +15,14 @@ test_expect_success setup '
git config color.decorate.stash magenta && git config color.decorate.stash magenta &&
git config color.decorate.HEAD cyan && git config color.decorate.HEAD cyan &&
c_reset=$(get_color reset) && c_reset="<RESET>" &&
c_commit=$(get_color yellow) && c_commit="<YELLOW>" &&
c_branch=$(get_color green) && c_branch="<GREEN>" &&
c_remoteBranch=$(get_color red) && c_remoteBranch="<RED>" &&
c_tag=$(get_color "reverse bold yellow") && c_tag="<BOLD;REVERSE;YELLOW>" &&
c_stash=$(get_color magenta) && c_stash="<MAGENTA>" &&
c_HEAD=$(get_color cyan) && c_HEAD="<CYAN>" &&
test_commit A && test_commit A &&
git clone . other && git clone . other &&
@ -59,7 +54,8 @@ EOF
# to this test since it does not contain any decoration, hence --first-parent # to this test since it does not contain any decoration, hence --first-parent
test_expect_success 'Commit Decorations Colored Correctly' ' test_expect_success 'Commit Decorations Colored Correctly' '
git log --first-parent --abbrev=10 --all --decorate --oneline --color=always | git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" >out && sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" |
test_decode_color >out &&
test_cmp expected out test_cmp expected out
' '

View File

@ -59,10 +59,14 @@ test_format () {
} }
# Feed to --format to provide predictable colored sequences. # Feed to --format to provide predictable colored sequences.
BASIC_COLOR='%Credfoo%Creset'
COLOR='%C(red)foo%C(reset)'
AUTO_COLOR='%C(auto,red)foo%C(auto,reset)' AUTO_COLOR='%C(auto,red)foo%C(auto,reset)'
ALWAYS_COLOR='%C(always,red)foo%C(always,reset)'
has_color () { has_color () {
printf '\033[31mfoo\033[m\n' >expect && test_decode_color <"$1" >decoded &&
test_cmp expect "$1" echo "<RED>foo<RESET>" >expect &&
test_cmp expect decoded
} }
has_no_color () { has_no_color () {
@ -170,62 +174,84 @@ $added
EOF EOF
test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF test_expect_success 'basic colors' '
commit $head2 cat >expect <<-EOF &&
foobarbazxyzzy commit $head2
commit $head1 <RED>foo<GREEN>bar<BLUE>baz<RESET>xyzzy
foobarbazxyzzy EOF
EOF format="%Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy" &&
git rev-list --color --format="$format" -1 master >actual.raw &&
test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF test_decode_color <actual.raw >actual &&
commit $head2 test_cmp expect actual
foo
commit $head1
foo
EOF
test_expect_success '%C(auto,...) does not enable color by default' '
git log --format=$AUTO_COLOR -1 >actual &&
has_no_color actual
' '
test_expect_success '%C(auto,...) enables colors for color.diff' ' test_expect_success 'advanced colors' '
git -c color.diff=always log --format=$AUTO_COLOR -1 >actual && cat >expect <<-EOF &&
has_color actual commit $head2
<BOLD;RED;BYELLOW>foo<RESET>
EOF
format="%C(red yellow bold)foo%C(reset)" &&
git rev-list --color --format="$format" -1 master >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect actual
' '
test_expect_success '%C(auto,...) enables colors for color.ui' ' for spec in \
git -c color.ui=always log --format=$AUTO_COLOR -1 >actual && "%Cred:$BASIC_COLOR" \
has_color actual "%C(...):$COLOR" \
' "%C(auto,...):$AUTO_COLOR"
do
test_expect_success '%C(auto,...) respects --color' ' desc=${spec%%:*}
git log --format=$AUTO_COLOR -1 --color >actual && color=${spec#*:}
has_color actual test_expect_success "$desc does not enable color by default" '
' git log --format=$color -1 >actual &&
test_expect_success '%C(auto,...) respects --no-color' '
git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual &&
has_no_color actual
'
test_expect_success TTY '%C(auto,...) respects --color=auto (stdout is tty)' '
test_terminal env TERM=vt100 \
git log --format=$AUTO_COLOR -1 --color=auto >actual &&
has_color actual
'
test_expect_success '%C(auto,...) respects --color=auto (stdout not tty)' '
(
TERM=vt100 && export TERM &&
git log --format=$AUTO_COLOR -1 --color=auto >actual &&
has_no_color actual has_no_color actual
) '
test_expect_success "$desc enables colors for color.diff" '
git -c color.diff=always log --format=$color -1 >actual &&
has_color actual
'
test_expect_success "$desc enables colors for color.ui" '
git -c color.ui=always log --format=$color -1 >actual &&
has_color actual
'
test_expect_success "$desc respects --color" '
git log --format=$color -1 --color >actual &&
has_color actual
'
test_expect_success "$desc respects --no-color" '
git -c color.ui=always log --format=$color -1 --no-color >actual &&
has_no_color actual
'
test_expect_success TTY "$desc respects --color=auto (stdout is tty)" '
test_terminal env TERM=vt100 \
git log --format=$color -1 --color=auto >actual &&
has_color actual
'
test_expect_success "$desc respects --color=auto (stdout not tty)" '
(
TERM=vt100 && export TERM &&
git log --format=$color -1 --color=auto >actual &&
has_no_color actual
)
'
done
test_expect_success '%C(always,...) enables color even without tty' '
git log --format=$ALWAYS_COLOR -1 >actual &&
has_color actual
' '
test_expect_success '%C(auto) respects --color' ' test_expect_success '%C(auto) respects --color' '
git log --color --format="%C(auto)%H" -1 >actual && git log --color --format="%C(auto)%H" -1 >actual.raw &&
printf "\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect && test_decode_color <actual.raw >actual &&
echo "<YELLOW>$(git rev-parse HEAD)<RESET>" >expect &&
test_cmp expect actual test_cmp expect actual
' '
@ -235,6 +261,17 @@ test_expect_success '%C(auto) respects --no-color' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'rev-list %C(auto,...) respects --color' '
git rev-list --color --format="%C(auto,green)foo%C(auto,reset)" \
-1 HEAD >actual.raw &&
test_decode_color <actual.raw >actual &&
cat >expect <<-EOF &&
commit $(git rev-parse HEAD)
<GREEN>foo<RESET>
EOF
test_cmp expect actual
'
iconv -f utf-8 -t $test_encoding > commit-msg <<EOF iconv -f utf-8 -t $test_encoding > commit-msg <<EOF
Test printing of complex bodies Test printing of complex bodies

View File

@ -7,6 +7,7 @@ test_description='for-each-ref test'
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh . "$TEST_DIRECTORY"/lib-gpg.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
# Mon Jul 3 23:18:43 2006 +0000 # Mon Jul 3 23:18:43 2006 +0000
datestamp=1151968723 datestamp=1151968723
@ -412,21 +413,33 @@ test_expect_success 'Check for invalid refname format' '
test_must_fail git for-each-ref --format="%(refname:INVALID)" test_must_fail git for-each-ref --format="%(refname:INVALID)"
' '
get_color () test_expect_success 'set up color tests' '
{ cat >expected.color <<-EOF &&
git config --get-color no.such.slot "$1" $(git rev-parse --short refs/heads/master) <GREEN>master<RESET>
} $(git rev-parse --short refs/remotes/origin/master) <GREEN>origin/master<RESET>
$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
EOF
sed "s/<[^>]*>//g" <expected.color >expected.bare &&
color_format="%(objectname:short) %(color:green)%(refname:short)"
'
cat >expected <<EOF test_expect_success TTY '%(color) shows color with a tty' '
$(git rev-parse --short refs/heads/master) $(get_color green)master$(get_color reset) test_terminal env TERM=vt100 \
$(git rev-parse --short refs/remotes/origin/master) $(get_color green)origin/master$(get_color reset) git for-each-ref --format="$color_format" >actual.raw &&
$(git rev-parse --short refs/tags/testtag) $(get_color green)testtag$(get_color reset) test_decode_color <actual.raw >actual &&
$(git rev-parse --short refs/tags/two) $(get_color green)two$(get_color reset) test_cmp expected.color actual
EOF '
test_expect_success 'Check %(color:...) ' ' test_expect_success '%(color) does not show color without tty' '
git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)" >actual && TERM=vt100 git for-each-ref --format="$color_format" >actual &&
test_cmp expected actual test_cmp expected.bare actual
'
test_expect_success 'color.ui=always can override tty check' '
git -c color.ui=always for-each-ref --format="$color_format" >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expected.color actual
' '
cat >expected <<\EOF cat >expected <<\EOF

View File

@ -9,6 +9,7 @@ Tests for operations with tags.'
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh . "$TEST_DIRECTORY"/lib-gpg.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
# creating and listing lightweight tags: # creating and listing lightweight tags:
@ -1900,6 +1901,30 @@ test_expect_success '--format should list tags as per format given' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success "set up color tests" '
echo "<RED>v1.0<RESET>" >expect.color &&
echo "v1.0" >expect.bare &&
color_args="--format=%(color:red)%(refname:short) --list v1.0"
'
test_expect_success '%(color) omitted without tty' '
TERM=vt100 git tag $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.bare actual
'
test_expect_success TTY '%(color) present with tty' '
test_terminal env TERM=vt100 git tag $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success 'color.ui=always overrides auto-color' '
git -c color.ui=always tag $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success 'setup --merged test tags' ' test_expect_success 'setup --merged test tags' '
git tag mergetest-1 HEAD~2 && git tag mergetest-1 HEAD~2 &&
git tag mergetest-2 HEAD~1 && git tag mergetest-2 HEAD~1 &&

View File

@ -42,6 +42,7 @@ test_decode_color () {
function name(n) { function name(n) {
if (n == 0) return "RESET"; if (n == 0) return "RESET";
if (n == 1) return "BOLD"; if (n == 1) return "BOLD";
if (n == 7) return "REVERSE";
if (n == 30) return "BLACK"; if (n == 30) return "BLACK";
if (n == 31) return "RED"; if (n == 31) return "RED";
if (n == 32) return "GREEN"; if (n == 32) return "GREEN";