decorate: color each token separately

Wrap "tag:" prefixes and the arrows in "HEAD -> branch" decorations in their own color sequences. Otherwise, if --graph is used, tag names or arrows can end up uncolored when %w width formatting breaks a line just before them. This is because --graph resets the color after doing its drawing at the start of a line. Amend test t4207-log-decoration-colors.sh accordingly. Signed-off-by: Andy Koppe <andy.koppe@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Andy Koppe committed Aug 20, 2023 at 19:50 UTC dcb347f83780b67ba586fda67a5c494b31354eae
2 files changed +36 -22
log-tree.c
+11 -3
@@ -342,26 +342,34 @@ void format_decorations(struct strbuf *sb,
342 * appeared, skipping the entry for current.
343 */
344 if (decoration != current_and_HEAD) {
345 + const char *color =
346 + decorate_get_color(use_color, decoration->type);
347 +
348 if (*prefix) {
349 strbuf_addstr(sb, color_commit);
350 strbuf_addstr(sb, prefix);
351 strbuf_addstr(sb, color_reset);
352 }
353
351 - strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
352 - if (decoration->type == DECORATION_REF_TAG)
354 + if (decoration->type == DECORATION_REF_TAG) {
355 + strbuf_addstr(sb, color);
356 strbuf_addstr(sb, "tag: ");
357 + strbuf_addstr(sb, color_reset);
358 + }
359
360 + strbuf_addstr(sb, color);
361 show_name(sb, decoration);
362 + strbuf_addstr(sb, color_reset);
363
364 if (current_and_HEAD &&
365 decoration->type == DECORATION_REF_HEAD) {
366 + strbuf_addstr(sb, color);
367 strbuf_addstr(sb, " -> ");
368 strbuf_addstr(sb, color_reset);
369 strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
370 show_name(sb, current_and_HEAD);
371 + strbuf_addstr(sb, color_reset);
372 }
364 - strbuf_addstr(sb, color_reset);
373
374 prefix = separator;
375 }
t/t4207-log-decoration-colors.sh
+25 -19
@@ -53,15 +53,17 @@ cmp_filtered_decorations () {
53 # to this test since it does not contain any decoration, hence --first-parent
54 test_expect_success 'commit decorations colored correctly' '
55 cat >expect <<-EOF &&
56 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
57 -${c_reset}${c_branch}main${c_reset}${c_commit}, \
58 -${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
59 -${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
60 -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit}, \
56 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
57 +${c_HEAD} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
58 +${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
59 +${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
60 +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
61 +${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
62 ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
62 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset} \
63 -On main: Changes to A.t
64 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
63 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
64 +${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
65 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
66 +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
67 EOF
68
69 git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual &&
@@ -76,12 +78,14 @@ test_expect_success 'test coloring with replace-objects' '
78 git replace HEAD~1 HEAD~2 &&
79
80 cat >expect <<-EOF &&
79 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
80 -${c_reset}${c_branch}main${c_reset}${c_commit}, \
81 -${c_reset}${c_tag}tag: D${c_reset}${c_commit})${c_reset} D
82 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: C${c_reset}${c_commit}, \
81 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
82 +${c_HEAD} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
83 +${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit})${c_reset} D
84 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
85 +${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_commit}, \
86 ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B
84 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
87 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
88 +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
89 EOF
90
91 git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
@@ -100,13 +104,15 @@ test_expect_success 'test coloring with grafted commit' '
104 git replace --graft HEAD HEAD~2 &&
105
106 cat >expect <<-EOF &&
103 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
104 -${c_reset}${c_branch}main${c_reset}${c_commit}, \
105 -${c_reset}${c_tag}tag: D${c_reset}${c_commit}, \
107 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
108 +${c_HEAD} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
109 +${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit}, \
110 ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D
107 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
108 -${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
109 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
111 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
112 +${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
113 +${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
114 + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
115 +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
116 EOF
117
118 git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&