Subject: [PATCH] pretty format: fix colors on %+ placeholder newline

From: Raphael Bauer <[email-masked]>
Date: Tue, 5 Apr 2022 17:45:29 +0200

Previously, the color escape codes were not printed again when a %+
placeholder was expanded, which could lead to missing colors.

In particular, the following command on any commit history exercised the
problem:

git log --pretty=format:'%h%Cred%+d test' --graph

The string 'test' should always be in red, but is not when commits have
ref names associated and the %+d placeholder is expanded.
This is also not a problem of any pager since the --graph option adds a
colored pipe symbol at the beginning of the line, which makes re-setting
the color again afterwards necessary.
---
 pretty.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/pretty.c b/pretty.c
index ee6114e3f0..0aabba89ca 100644
--- a/pretty.c
+++ b/pretty.c
@@ -863,6 +863,7 @@ struct format_commit_context {
 	size_t width, indent1, indent2;
 	int auto_color;
 	int padding;
+	char curr_color_escape[COLOR_MAXLEN];
 
 	/* These offsets are relative to the start of the commit message. */
 	struct chunk author;
@@ -1050,6 +1051,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
 		if (color_parse_mem(begin, end - begin, color) < 0)
 			die(_("unable to parse --pretty format"));
 		strbuf_addstr(sb, color);
+		strlcpy(c->curr_color_escape, color, COLOR_MAXLEN);
 		return end - placeholder + 1;
 	}
 
@@ -1067,8 +1069,10 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
 	else if (skip_prefix(placeholder + 1, "reset", &rest))
 		basic_color = GIT_COLOR_RESET;
 
-	if (basic_color && want_color(c->pretty_ctx->color))
+	if (basic_color && want_color(c->pretty_ctx->color)) {
 		strbuf_addstr(sb, basic_color);
+		strlcpy(c->curr_color_escape, basic_color, COLOR_MAXLEN);
+	}
 
 	return rest - placeholder;
 }
@@ -1360,8 +1364,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 	case 'C':
 		if (starts_with(placeholder + 1, "(auto)")) {
 			c->auto_color = want_color(c->pretty_ctx->color);
-			if (c->auto_color && sb->len)
+			if (c->auto_color && sb->len) {
 				strbuf_addstr(sb, GIT_COLOR_RESET);
+				c->curr_color_escape[0] = 0;
+			}
 			return 7; /* consumed 7 bytes, "C(auto)" */
 		} else {
 			int ret = parse_color(sb, placeholder, c);
@@ -1813,9 +1819,11 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
 		while (sb->len && sb->buf[sb->len - 1] == '\n')
 			strbuf_setlen(sb, sb->len - 1);
 	} else if (orig_len != sb->len) {
-		if (magic == ADD_LF_BEFORE_NON_EMPTY)
+		if (magic == ADD_LF_BEFORE_NON_EMPTY) {
 			strbuf_insertstr(sb, orig_len, "\n");
-		else if (magic == ADD_SP_BEFORE_NON_EMPTY)
+			strbuf_insertstr(sb, orig_len + 1,
+				((struct format_commit_context *)context)->curr_color_escape);
+		} else if (magic == ADD_SP_BEFORE_NON_EMPTY)
 			strbuf_insertstr(sb, orig_len, " ");
 	}
 	return consumed + 1;
-- 
2.35.1