Unlike the previous commit, this dims colors for each metadata field individually. Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> --- builtin/blame.c | 83 ++++++++++++++++++++++++++++++++++++++++++++----- t/t8012-blame-colors.sh | 38 ++++++++++++++++++++++ 2 files changed, 113 insertions(+), 8 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 7b9c6e8676..60c8dadf4b 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -323,6 +323,7 @@ static const char *format_time(timestamp_t time, const char *tz_str, #define OUTPUT_SHOW_EMAIL 0400 #define OUTPUT_LINE_PORCELAIN 01000 #define OUTPUT_COLOR_LINE 02000 +#define OUTPUT_COLOR_FIELDS 04000 static void emit_porcelain_details(struct blame_origin *suspect, int repeat) { @@ -370,7 +371,56 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, putchar('\n'); } -static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) +static int had_same_field_previously(int opt, int field, + struct blame_entry *ent, + struct blame_entry *prev) +{ + struct commit_info ci, prev_ci; + + switch (field) { + case OUTPUT_SHOW_SCORE: + return ent->score == prev->score; + case OUTPUT_SHOW_NAME: + return prev->suspect && + !strcmp(ent->suspect->path, prev->suspect->path); + case OUTPUT_SHOW_NUMBER: + return ent->s_lno == prev->s_lno + prev->num_lines - 1; + + case OUTPUT_NO_AUTHOR: + get_commit_info(ent->suspect->commit, &ci, 1); + get_commit_info(prev->suspect->commit, &prev_ci, 1); + return ((opt & OUTPUT_SHOW_EMAIL) && + !strcmp(ci.author_mail.buf, prev_ci.author_mail.buf)) || + !strcmp(ci.author.buf, prev_ci.author.buf); + default: + BUG("unknown field"); + } + return 0; +} + +static void setup_field_color(int opt, int cnt, int field, + struct blame_entry *ent, + struct blame_entry *prev, + const char **use_color, + const char **reset_color) +{ + if (!(opt & OUTPUT_COLOR_FIELDS)) + return; + + if ((cnt > 0 || + (prev && had_same_field_previously(opt, field, ent, prev)))) { + *use_color = repeated_meta_color; + *reset_color = GIT_COLOR_RESET; + } else { + *use_color = ""; + *reset_color = ""; + } +} + +static void emit_other(struct blame_scoreboard *sb, + struct blame_entry *ent, + struct blame_entry *prev, + int opt) { int cnt; const char *cp; @@ -414,18 +464,27 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int show_raw_time), ent->lno + 1 + cnt); } else { - if (opt & OUTPUT_SHOW_SCORE) + if (opt & OUTPUT_SHOW_SCORE) { + setup_field_color(opt, cnt, OUTPUT_SHOW_SCORE, + ent, prev, &color, &reset); printf(" %s%*d %02d%s", color, max_score_digits, ent->score, ent->suspect->refcnt, reset); - if (opt & OUTPUT_SHOW_NAME) + } + if (opt & OUTPUT_SHOW_NAME) { + setup_field_color(opt, cnt, OUTPUT_SHOW_NAME, + ent, prev, &color, &reset); printf(" %s%-*.*s%s", color, longest_file, longest_file, suspect->path, reset); - if (opt & OUTPUT_SHOW_NUMBER) + } + if (opt & OUTPUT_SHOW_NUMBER) { + setup_field_color(opt, cnt, OUTPUT_SHOW_NUMBER, + ent, prev, &color, &reset); printf(" %s%*d%s", color, max_orig_digits, ent->s_lno + 1 + cnt, reset); + } if (!(opt & OUTPUT_NO_AUTHOR)) { const char *name; int pad; @@ -434,6 +493,8 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int else name = ci.author.buf; pad = longest_author - utf8_strwidth(name); + setup_field_color(opt, cnt, OUTPUT_NO_AUTHOR, + ent, prev, &color, &reset); printf(" %s(%s%*s %10s%s", color, name, pad, "", format_time(ci.author_time, @@ -459,7 +520,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int static void output(struct blame_scoreboard *sb, int option) { - struct blame_entry *ent; + struct blame_entry *ent, *prev = NULL; if (option & OUTPUT_PORCELAIN) { for (ent = sb->ent; ent; ent = ent->next) { @@ -481,7 +542,8 @@ static void output(struct blame_scoreboard *sb, int option) if (option & OUTPUT_PORCELAIN) emit_porcelain(sb, ent, option); else { - emit_other(sb, ent, option); + emit_other(sb, ent, prev, option); + prev = ent; } } } @@ -699,6 +761,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL), OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE), OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE), + OPT_BIT(0, "color-fields", &output_option, N_("color redundant metadata fields from previous line differently"), OUTPUT_COLOR_FIELDS), /* * The following two options are parsed by parse_revision_opt() @@ -757,6 +820,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix) revs.diffopt.flags.follow_renames = 0; argc = parse_options_end(&ctx); + if ((output_option & OUTPUT_COLOR_LINE) && + (output_option & OUTPUT_COLOR_FIELDS)) + die(_("cannot ask for colored lines and fields at the same time")); + if (incremental || (output_option & OUTPUT_PORCELAIN)) { if (show_progress > 0) die(_("--progress can't be used with --incremental or porcelain formats")); @@ -960,8 +1027,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) if (!(output_option & OUTPUT_PORCELAIN)) { find_alignment(&sb, &output_option); - if ((output_option & OUTPUT_COLOR_LINE) && - !repeated_meta_color) + if (!repeated_meta_color && + (output_option & (OUTPUT_COLOR_LINE | OUTPUT_COLOR_FIELDS))) repeated_meta_color = GIT_COLOR_DARK; } diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh index a2b7090cef..18f9c9a16d 100755 --- a/t/t8012-blame-colors.sh +++ b/t/t8012-blame-colors.sh @@ -15,4 +15,42 @@ test_expect_success 'colored blame colors contiguous lines' ' test_line_count = 3 H.expect ' +test_expect_success 'colored blame colors continuous fields' ' + + git mv hello.c world.c && + git commit -a -m "moved file" && + cat <<-EOF >> world.c && + void world() + { + puts("world"); + } + EOF + git add world.c && + GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@xxxxxxxx" \ + git commit -m "forgot to add changes to moved file" && + + git blame --abbrev=12 --color-fields world.c >actual.raw && + test_decode_color <actual.raw >actual && + + grep "<BOLD;BLACK>hello.c" actual > colored_hello.expect && + grep "hello.c" actual > all_hello.expect && + test_line_count = 9 colored_hello.expect && + test_line_count = 10 all_hello.expect && + + grep "<BOLD;BLACK>world.c" actual > colored_world.expect && + grep "world.c" actual > all_world.expect && + test_line_count = 3 colored_world.expect && + test_line_count = 4 all_world.expect && + + grep "(F" actual > all_F.expect && + grep "<BOLD;BLACK>(F" actual > colored_F.expect && + test_line_count = 8 all_F.expect && + test_line_count = 5 colored_F.expect && + + grep "(H" actual > all_H.expect && + grep "<BOLD;BLACK>(H" actual > colored_H.expect && + test_line_count = 5 all_H.expect && + test_line_count = 3 colored_H.expect +' + test_done -- 2.16.0.rc0.223.g4a4ac83678-goog