builtin/blame: dim uninteresting metadata lines

When using git-blame lots of lines contain redundant information, for example in hunks that consist of multiple lines, the metadata (commit name, author, date) are repeated. A reader may not be interested in those, so offer an option to color the information that is repeated from the previous line differently. Traditionally, we use CYAN for lines that are less interesting than others (e.g. hunk header), so go with that. The command line option '--color-lines' will trigger the coloring of repeated lines, and the config option 'color.blame.colorLines' is provided to select the color. Setting the config option doesn't imply that repeated lines are colored. A later patch will introduce a config to enable this mode by default. Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stefan Beller committed Apr 23, 2018 at 17:08 UTC cdc2d5f11f1ab931d2f39fefc6197ea1cab220a6
3 files changed +59 -4
Documentation/config.txt
+5
@@ -1218,6 +1218,11 @@ color.status.<slot>::
1218 status short-format), or
1219 `unmerged` (files which have unmerged changes).
1220
1221 +color.blame.repeatedLines::
1222 + Use the customized color for the part of git-blame output that
1223 + is repeated meta information per line (such as commit id,
1224 + author name, date and timezone). Defaults to cyan.
1225 +
1226 color.ui::
1227 This variable determines the default value for variables such
1228 as `color.diff` and `color.grep` that control the use of color
builtin/blame.c
+35 -4
@@ -7,6 +7,7 @@
7
8 #include "cache.h"
9 #include "config.h"
10 +#include "color.h"
11 #include "builtin.h"
12 #include "commit.h"
13 #include "diff.h"
@@ -46,6 +47,7 @@ static int xdl_opts;
47 static int abbrev = -1;
48 static int no_whole_file_rename;
49 static int show_progress;
50 +static char repeated_meta_color[COLOR_MAXLEN];
51
52 static struct date_mode blame_date_mode = { DATE_ISO8601 };
53 static size_t blame_date_width;
@@ -316,10 +318,11 @@ static const char *format_time(timestamp_t time, const char *tz_str,
318 #define OUTPUT_PORCELAIN 010
319 #define OUTPUT_SHOW_NAME 020
320 #define OUTPUT_SHOW_NUMBER 040
319 -#define OUTPUT_SHOW_SCORE 0100
320 -#define OUTPUT_NO_AUTHOR 0200
321 +#define OUTPUT_SHOW_SCORE 0100
322 +#define OUTPUT_NO_AUTHOR 0200
323 #define OUTPUT_SHOW_EMAIL 0400
322 -#define OUTPUT_LINE_PORCELAIN 01000
324 +#define OUTPUT_LINE_PORCELAIN 01000
325 +#define OUTPUT_COLOR_LINE 02000
326
327 static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
328 {
@@ -375,6 +378,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
378 struct commit_info ci;
379 char hex[GIT_MAX_HEXSZ + 1];
380 int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
381 + const char *color = NULL, *reset = NULL;
382
383 get_commit_info(suspect->commit, &ci, 1);
384 oid_to_hex_r(hex, &suspect->commit->object.oid);
@@ -384,6 +388,18 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
388 char ch;
389 int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
390
391 + if (opt & OUTPUT_COLOR_LINE) {
392 + if (cnt > 0) {
393 + color = repeated_meta_color;
394 + reset = GIT_COLOR_RESET;
395 + } else {
396 + color = NULL;
397 + reset = NULL;
398 + }
399 + }
400 + if (color)
401 + fputs(color, stdout);
402 +
403 if (suspect->commit->object.flags & UNINTERESTING) {
404 if (blank_boundary)
405 memset(hex, ' ', length);
@@ -433,6 +449,8 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
449 printf(" %*d) ",
450 max_digits, ent->lno + 1 + cnt);
451 }
452 + if (reset)
453 + fputs(reset, stdout);
454 do {
455 ch = *cp++;
456 putchar(ch);
@@ -607,6 +625,12 @@ static int git_blame_config(const char *var, const char *value, void *cb)
625 parse_date_format(value, &blame_date_mode);
626 return 0;
627 }
628 + if (!strcmp(var, "color.blame.repeatedlines")) {
629 + if (color_parse_mem(value, strlen(value), repeated_meta_color))
630 + warning(_("invalid color '%s' in color.blame.repeatedLines"),
631 + value);
632 + return 0;
633 + }
634
635 if (git_diff_heuristic_config(var, value, cb) < 0)
636 return -1;
@@ -681,6 +705,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
705 OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
706 OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
707 OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
708 + OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
709
710 /*
711 * The following two options are parsed by parse_revision_opt()
@@ -940,8 +965,14 @@ parse_done:
965
966 blame_coalesce(&sb);
967
943 - if (!(output_option & OUTPUT_PORCELAIN))
968 + if (!(output_option & OUTPUT_PORCELAIN)) {
969 find_alignment(&sb, &output_option);
970 + if (!*repeated_meta_color &&
971 + (output_option & OUTPUT_COLOR_LINE))
972 + strcpy(repeated_meta_color, GIT_COLOR_CYAN);
973 + }
974 + if (output_option & OUTPUT_ANNOTATE_COMPAT)
975 + output_option &= ~OUTPUT_COLOR_LINE;
976
977 output(&sb, output_option);
978 free((void *)sb.final_buf);
t/t8012-blame-colors.sh new
+19
@@ -0,0 +1,19 @@
1 +#!/bin/sh
2 +
3 +test_description='colored git blame'
4 +. ./test-lib.sh
5 +
6 +PROG='git blame -c'
7 +. "$TEST_DIRECTORY"/annotate-tests.sh
8 +
9 +test_expect_success 'colored blame colors contiguous lines' '
10 + git -c color.blame.repeatedLines=yellow blame --color-lines --abbrev=12 hello.c >actual.raw &&
11 + test_decode_color <actual.raw >actual &&
12 + grep "<YELLOW>" <actual >darkened &&
13 + grep "(F" darkened > F.expect &&
14 + grep "(H" darkened > H.expect &&
15 + test_line_count = 2 F.expect &&
16 + test_line_count = 3 H.expect
17 +'
18 +
19 +test_done