From 0f6f5a4022de5904926cd528c681468e3f635256 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Dec 2007 17:26:11 -0800 Subject: [PATCH 1/3] git config --get-colorbool This adds an option to help scripts find out color settings from the configuration file. git config --get-colorbool color.diff inspects color.diff variable, and exits with status 0 (i.e. success) if color is to be used. It exits with status 1 otherwise. If a script wants "true"/"false" answer to the standard output of the command, it can pass an additional boolean parameter to its command line, telling if its standard output is a terminal, like this: git config --get-colorbool color.diff true When called like this, the command outputs "true" to its standard output if color is to be used (i.e. "color.diff" says "always", "auto", or "true"), and "false" otherwise. Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 10 +++++++++ builtin-branch.c | 2 +- builtin-config.c | 41 +++++++++++++++++++++++++++++++++++- color.c | 6 ++++-- color.h | 2 +- diff.c | 2 +- wt-status.c | 2 +- 7 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 7640450787..98509b4f44 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -21,6 +21,7 @@ SYNOPSIS 'git-config' [] --remove-section name 'git-config' [] [-z|--null] -l | --list 'git-config' [] --get-color name [default] +'git-config' [] --get-colorbool name [stdout-is-tty] DESCRIPTION ----------- @@ -135,6 +136,15 @@ See also <>. output without getting confused e.g. by values that contain line breaks. +--get-colorbool name [stdout-is-tty]:: + + Find the color setting for `name` (e.g. `color.diff`) and output + "true" or "false". `stdout-is-tty` should be either "true" or + "false", and is taken into account when configuration says + "auto". If `stdout-is-tty` is missing, then checks the standard + output of the command itself, and exits with status 0 if color + is to be used, or exits with status 1 otherwise. + --get-color name default:: Find the color configured for `name` (e.g. `color.diff.new`) and diff --git a/builtin-branch.c b/builtin-branch.c index c64768beb2..089cae5929 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -65,7 +65,7 @@ static int parse_branch_color_slot(const char *var, int ofs) static int git_branch_config(const char *var, const char *value) { if (!strcmp(var, "color.branch")) { - branch_use_color = git_config_colorbool(var, value); + branch_use_color = git_config_colorbool(var, value, -1); return 0; } if (!prefixcmp(var, "color.branch.")) { diff --git a/builtin-config.c b/builtin-config.c index 6175dc3738..d10b03f50c 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -3,7 +3,7 @@ #include "color.h" static const char git_config_set_usage[] = -"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default]"; +"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; static char *key; static regex_t *key_regexp; @@ -208,6 +208,43 @@ static int get_color(int argc, const char **argv) return 0; } +static int stdout_is_tty; +static int get_colorbool_found; +static int git_get_colorbool_config(const char *var, const char *value) +{ + if (!strcmp(var, get_color_slot)) + get_colorbool_found = + git_config_colorbool(var, value, stdout_is_tty); + return 0; +} + +static int get_colorbool(int argc, const char **argv) +{ + /* + * git config --get-colorbool [] + * + * returns "true" or "false" depending on how + * is configured. + */ + + if (argc == 2) + stdout_is_tty = git_config_bool("command line", argv[1]); + else if (argc == 1) + stdout_is_tty = isatty(1); + else + usage(git_config_set_usage); + get_colorbool_found = 0; + get_color_slot = argv[0]; + git_config(git_get_colorbool_config); + + if (argc == 1) { + return get_colorbool_found ? 0 : 1; + } else { + printf("%s\n", get_colorbool_found ? "true" : "false"); + return 0; + } +} + int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = 0; @@ -283,6 +320,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 0; } else if (!strcmp(argv[1], "--get-color")) { return get_color(argc-2, argv+2); + } else if (!strcmp(argv[1], "--get-colorbool")) { + return get_colorbool(argc-2, argv+2); } else break; argc--; diff --git a/color.c b/color.c index 97cfbda31a..7bd424a8f6 100644 --- a/color.c +++ b/color.c @@ -116,7 +116,7 @@ bad: die("bad config value '%s' for variable '%s'", value, var); } -int git_config_colorbool(const char *var, const char *value) +int git_config_colorbool(const char *var, const char *value, int stdout_is_tty) { if (value) { if (!strcasecmp(value, "never")) @@ -133,7 +133,9 @@ int git_config_colorbool(const char *var, const char *value) /* any normal truth value defaults to 'auto' */ auto_color: - if (isatty(1) || (pager_in_use && pager_use_color)) { + if (stdout_is_tty < 0) + stdout_is_tty = isatty(1); + if (stdout_is_tty || (pager_in_use && pager_use_color)) { char *term = getenv("TERM"); if (term && strcmp(term, "dumb")) return 1; diff --git a/color.h b/color.h index 68098006ed..ff63513d39 100644 --- a/color.h +++ b/color.h @@ -4,7 +4,7 @@ /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ #define COLOR_MAXLEN 24 -int git_config_colorbool(const char *var, const char *value); +int git_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *var, const char *value, char *dst); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); diff --git a/diff.c b/diff.c index 6b54959610..be6cf687a4 100644 --- a/diff.c +++ b/diff.c @@ -146,7 +146,7 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { - diff_use_color_default = git_config_colorbool(var, value); + diff_use_color_default = git_config_colorbool(var, value, -1); return 0; } if (!strcmp(var, "diff.renames")) { diff --git a/wt-status.c b/wt-status.c index d35386dae1..02dbb751db 100644 --- a/wt-status.c +++ b/wt-status.c @@ -391,7 +391,7 @@ void wt_status_print(struct wt_status *s) int git_status_config(const char *k, const char *v) { if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { - wt_status_use_color = git_config_colorbool(k, v); + wt_status_use_color = git_config_colorbool(k, v, -1); return 0; } if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { From b4c61ed6d35ea00494f953a27abbc902617972ec Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Dec 2007 00:50:23 -0800 Subject: [PATCH 2/3] Color support for "git-add -i" This is mostly lifted from earlier series by Dan Zwell, but updated to use "git config --get-color" and "git config --get-colorbool" to make it simpler and more consistent with commands written in C. A new configuration color.interactive variable is like color.diff and color.status, and controls if "git-add -i" uses color. A set of configuration variables, color.interactive., are used to define what color is used for the prompt, header, and help text. For perl scripts, Git.pm provides $repo->get_color() method, which takes the slot name and the default color, and returns the terminal escape sequence to color the output text. $repo->get_colorbool() method can be used to check if color is set to be used for a given operation. Signed-off-by: Junio C Hamano --- Documentation/config.txt | 12 ++++ git-add--interactive.perl | 119 +++++++++++++++++++++++++++++++------- perl/Git.pm | 35 +++++++++++ 3 files changed, 146 insertions(+), 20 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 0e45ec547c..736fcd71cc 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -391,6 +391,18 @@ color.diff.:: whitespace). The values of these variables may be specified as in color.branch.. +color.interactive:: + When set to `always`, always use colors in `git add --interactive`. + When false (or `never`), never. When set to `true` or `auto`, use + colors only when the output is to the terminal. Defaults to false. + +color.interactive.:: + Use customized color for `git add --interactive` + output. `` may be `prompt`, `header`, or `help`, for + three distinct types of normal output from interactive + programs. The values of these variables may be specified as + in color.branch.. + color.pager:: A boolean to enable/disable colored output when the pager is in use (default is true). diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 335c2c6b56..1019a72d6c 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -1,6 +1,55 @@ #!/usr/bin/perl -w use strict; +use Git; + +# Prompt colors: +my ($prompt_color, $header_color, $help_color, $normal_color); +# Diff colors: +my ($new_color, $old_color, $fraginfo_color, $metainfo_color, $whitespace_color); + +my ($use_color, $diff_use_color); +my $repo = Git->repository(); + +$use_color = $repo->get_colorbool('color.interactive'); + +if ($use_color) { + # Set interactive colors: + + # Grab the 3 main colors in git color string format, with sane + # (visible) defaults: + $prompt_color = $repo->get_color("color.interactive.prompt", "bold blue"); + $header_color = $repo->get_color("color.interactive.header", "bold"); + $help_color = $repo->get_color("color.interactive.help", "red bold"); + $normal_color = $repo->get_color("", "reset"); + + # Do we also set diff colors? + $diff_use_color = $repo->get_colorbool('color.diff'); + if ($diff_use_color) { + $new_color = $repo->get_color("color.diff.new", "green"); + $old_color = $repo->get_color("color.diff.old", "red"); + $fraginfo_color = $repo->get_color("color.diff.frag", "cyan"); + $metainfo_color = $repo->get_color("color.diff.meta", "bold"); + $whitespace_color = $repo->get_color("color.diff.whitespace", "normal red"); + } +} + +sub colored { + my $color = shift; + my $string = join("", @_); + + if ($use_color) { + # Put a color code at the beginning of each line, a reset at the end + # color after newlines that are not at the end of the string + $string =~ s/(\n+)(.)/$1$color$2/g; + # reset before newlines + $string =~ s/(\n+)/$normal_color$1/g; + # codes at beginning and end (if necessary): + $string =~ s/^/$color/; + $string =~ s/$/$normal_color/ unless $string =~ /\n$/; + } + return $string; +} # command line options my $patch_mode; @@ -246,10 +295,20 @@ sub is_valid_prefix { sub highlight_prefix { my $prefix = shift; my $remainder = shift; - return $remainder unless defined $prefix; - return is_valid_prefix($prefix) ? - "[$prefix]$remainder" : - "$prefix$remainder"; + + if (!defined $prefix) { + return $remainder; + } + + if (!is_valid_prefix($prefix)) { + return "$prefix$remainder"; + } + + if (!$use_color) { + return "[$prefix]$remainder"; + } + + return "$prompt_color$prefix$normal_color$remainder"; } sub list_and_choose { @@ -266,7 +325,7 @@ sub list_and_choose { if (!$opts->{LIST_FLAT}) { print " "; } - print "$opts->{HEADER}\n"; + print colored $header_color, "$opts->{HEADER}\n"; } for ($i = 0; $i < @stuff; $i++) { my $chosen = $chosen[$i] ? '*' : ' '; @@ -304,7 +363,7 @@ sub list_and_choose { return if ($opts->{LIST_ONLY}); - print $opts->{PROMPT}; + print colored $prompt_color, $opts->{PROMPT}; if ($opts->{SINGLETON}) { print "> "; } @@ -371,7 +430,7 @@ sub list_and_choose { } sub singleton_prompt_help_cmd { - print <<\EOF ; + print colored $help_color, <<\EOF ; Prompt help: 1 - select a numbered item foo - select item based on unique prefix @@ -380,7 +439,7 @@ EOF } sub prompt_help_cmd { - print <<\EOF ; + print colored $help_color, <<\EOF ; Prompt help: 1 - select a single item 3-5 - select a range of items @@ -477,6 +536,31 @@ sub parse_diff { return @hunk; } +sub colored_diff_hunk { + my ($text) = @_; + # return the text, so that it can be passed to print() + my @ret; + for (@$text) { + if (!$diff_use_color) { + push @ret, $_; + next; + } + + if (/^\+/) { + push @ret, colored($new_color, $_); + } elsif (/^\-/) { + push @ret, colored($old_color, $_); + } elsif (/^\@/) { + push @ret, colored($fraginfo_color, $_); + } elsif (/^ /) { + push @ret, colored($normal_color, $_); + } else { + push @ret, colored($metainfo_color, $_); + } + } + return @ret; +} + sub hunk_splittable { my ($text) = @_; @@ -671,7 +755,7 @@ sub coalesce_overlapping_hunks { } sub help_patch_cmd { - print <<\EOF ; + print colored $help_color, <<\EOF ; y - stage this hunk n - do not stage this hunk a - stage this and all the remaining hunks in the file @@ -710,9 +794,7 @@ sub patch_update_file { my ($ix, $num); my $path = shift; my ($head, @hunk) = parse_diff($path); - for (@{$head->{TEXT}}) { - print; - } + print colored_diff_hunk($head->{TEXT}); $num = scalar @hunk; $ix = 0; @@ -754,10 +836,8 @@ sub patch_update_file { if (hunk_splittable($hunk[$ix]{TEXT})) { $other .= '/s'; } - for (@{$hunk[$ix]{TEXT}}) { - print; - } - print "Stage this hunk [y/n/a/d$other/?]? "; + print colored_diff_hunk($hunk[$ix]{TEXT}); + print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? "; my $line = ; if ($line) { if ($line =~ /^y/i) { @@ -811,7 +891,7 @@ sub patch_update_file { elsif ($other =~ /s/ && $line =~ /^s/) { my @split = split_hunk($hunk[$ix]{TEXT}); if (1 < @split) { - print "Split into ", + print colored $header_color, "Split into ", scalar(@split), " hunks.\n"; } splice(@hunk, $ix, 1, @@ -894,8 +974,7 @@ sub diff_cmd { HEADER => $status_head, }, @mods); return if (!@them); - system(qw(git diff-index -p --cached HEAD --), - map { $_->{VALUE} } @them); + system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them); } sub quit_cmd { @@ -904,7 +983,7 @@ sub quit_cmd { } sub help_cmd { - print <<\EOF ; + print colored $help_color, <<\EOF ; status - show paths with changes update - add working tree state to the staged set of changes revert - revert staged set of changes back to the HEAD version diff --git a/perl/Git.pm b/perl/Git.pm index 7468460f9a..a2812ea612 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -581,6 +581,41 @@ sub config_int { }; } +=item get_colorbool ( NAME ) + +Finds if color should be used for NAMEd operation from the configuration, +and returns boolean (true for "use color", false for "do not use color"). + +=cut + +sub get_colorbool { + my ($self, $var) = @_; + my $stdout_to_tty = (-t STDOUT) ? "true" : "false"; + my $use_color = $self->command_oneline('config', '--get-colorbool', + $var, $stdout_to_tty); + return ($use_color eq 'true'); +} + +=item get_color ( SLOT, COLOR ) + +Finds color for SLOT from the configuration, while defaulting to COLOR, +and returns the ANSI color escape sequence: + + print $repo->get_color("color.interactive.prompt", "underline blue white"); + print "some text"; + print $repo->get_color("", "normal"); + +=cut + +sub get_color { + my ($self, $slot, $default) = @_; + my $color = $self->command_oneline('config', '--get-color', $slot, $default); + if (!defined $color) { + $color = ""; + } + return $color; +} + =item ident ( TYPE | IDENTSTR ) =item ident_person ( TYPE | IDENTSTR | IDENTARRAY ) From 69243c2b956d69636ae04c184bc8063a6fab728a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Dec 2007 22:12:07 -0800 Subject: [PATCH 3/3] config --get-colorbool: diff.color is a deprecated synonym to color.diff The applications can ask for color.diff but the configuration of old timer users can still instruct it to use color with diff.color this way. Signed-off-by: Junio C Hamano --- builtin-config.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/builtin-config.c b/builtin-config.c index d10b03f50c..e4a12e3166 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -210,11 +210,17 @@ static int get_color(int argc, const char **argv) static int stdout_is_tty; static int get_colorbool_found; +static int get_diff_color_found; static int git_get_colorbool_config(const char *var, const char *value) { - if (!strcmp(var, get_color_slot)) + if (!strcmp(var, get_color_slot)) { get_colorbool_found = git_config_colorbool(var, value, stdout_is_tty); + } + if (!strcmp(var, "diff.color")) { + get_diff_color_found = + git_config_colorbool(var, value, stdout_is_tty); + } return 0; } @@ -233,10 +239,18 @@ static int get_colorbool(int argc, const char **argv) stdout_is_tty = isatty(1); else usage(git_config_set_usage); - get_colorbool_found = 0; + get_colorbool_found = -1; + get_diff_color_found = -1; get_color_slot = argv[0]; git_config(git_get_colorbool_config); + if (get_colorbool_found < 0) { + if (!strcmp(get_color_slot, "color.diff")) + get_colorbool_found = get_diff_color_found; + if (get_colorbool_found < 0) + get_colorbool_found = 0; + } + if (argc == 1) { return get_colorbool_found ? 0 : 1; } else {