Unlike the previous commit, this dims colors for each metadata field individually. Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> --- builtin/blame.c | 82 +++++++++++++++++++++++++++++++++++++++++++------ t/t8012-blame-colors.sh | 38 +++++++++++++++++++++++ 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 09afb4d3ea..39e8c0b222 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,6 +371,33 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, putchar('\n'); } +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 inline void colors_unset(const char **use_color, const char **reset_color) { *use_color = ""; @@ -382,17 +410,36 @@ static inline void colors_set(const char **use_color, const char **reset_color) *reset_color = GIT_COLOR_RESET; } +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)))) + colors_set(use_color, reset_color); + else + colors_unset(use_color, reset_color); +} + static void setup_line_color(int opt, int cnt, const char **use_color, const char **reset_color) { colors_unset(use_color, reset_color); - if ((opt & OUTPUT_COLOR_LINE) && cnt > 0) + if ((opt & (OUTPUT_COLOR_LINE | OUTPUT_COLOR_FIELDS)) && cnt > 0) colors_set(use_color, reset_color); } -static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) +static void emit_other(struct blame_scoreboard *sb, + struct blame_entry *ent, + struct blame_entry *prev, + int opt) { int cnt; const char *cp; @@ -432,18 +479,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, &col, &rcol); printf(" %s%*d %02d%s", col, max_score_digits, ent->score, ent->suspect->refcnt, rcol); - if (opt & OUTPUT_SHOW_NAME) + } + if (opt & OUTPUT_SHOW_NAME) { + setup_field_color(opt, cnt, OUTPUT_SHOW_NAME, + ent, prev, &col, &rcol); printf(" %s%-*.*s%s", col, longest_file, longest_file, suspect->path, rcol); - if (opt & OUTPUT_SHOW_NUMBER) + } + if (opt & OUTPUT_SHOW_NUMBER) { + setup_field_color(opt, cnt, OUTPUT_SHOW_NUMBER, + ent, prev, &col, &rcol); printf(" %s%*d%s", col, max_orig_digits, ent->s_lno + 1 + cnt, rcol); + } if (!(opt & OUTPUT_NO_AUTHOR)) { const char *name; int pad; @@ -452,6 +508,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, &col, &rcol); printf(" %s(%s%*s %10s%s", col, name, pad, "", format_time(ci.author_time, @@ -477,7 +535,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) { @@ -499,7 +557,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; } } } @@ -717,6 +776,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"), OUTPUT_COLOR_LINE), + OPT_BIT(0, "color-fields", &output_option, N_("color redundant metadata fields from previous line"), OUTPUT_COLOR_FIELDS), /* * The following two options are parsed by parse_revision_opt() @@ -775,6 +835,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")); @@ -978,8 +1042,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 b2de03b777..be491bde9a 100755 --- a/t/t8012-blame-colors.sh +++ b/t/t8012-blame-colors.sh @@ -15,4 +15,42 @@ test_expect_success 'colored blame colors continuous 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.15.1.620.gb9897f4670-goog