Subject: Re: [PATCH] log: decorate detached HEAD differently

From: Junio C Hamano <[email-masked]>
Date: Mon, 09 Mar 2015 19:03:18 -0700

Michael J Gruber <[email-masked]> writes:

> Junio C Hamano venit, vidit, dixit 06.03.2015 20:03:
>> Michael J Gruber <[email-masked]> writes:
>>>     
>>>     Note that now a checked branch is listed twice, once as target of the
>>>     HEAD, once as branch: They are two different refs and colored differently.
>> 
>> The pointee of HEAD would always be branch and will always appear on
>> the output when you show HEAD->$name_of_that_branch; is it feasible
>> to drop the duplicate, I wonder?
>
> It's doable but not nice, because we cannot take the order in which refs
> are processed for granted.

That is true, but when we format them into a single line in the
header in response to --decorate (or %d), don't we have all of them
already at hand---does the order still matter?

Here is an illustration of what I had in mind, made on a random
commit I happened to have checked out that does not have your
patches on this topic.  Half of the change is a new helper function,
and the other half is mostly reindenting.

 log-tree.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 63 insertions(+), 8 deletions(-)

diff --git a/log-tree.c b/log-tree.c
index 7f0890e..e0c5865 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -173,6 +173,43 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre
 }
 
 /*
+ * Do we have HEAD in the output, and also the branch it points at?
+ * If so, find that decoration entry for that current branch.
+ */
+static const struct name_decoration *current_pointed_by_HEAD(const struct name_decoration *decoration)
+{
+	const struct name_decoration *list, *head = NULL;
+	const char *branch_name = NULL;
+	unsigned char unused[20];
+	int rru_flags;
+
+	/* First find HEAD */
+	for (list = decoration; list; list = list->next)
+		if (list->type == DECORATION_REF_HEAD) {
+			head = list;
+			break;
+		}
+	if (!head)
+		return NULL;
+
+	/* Now resolve and find the matching current branch */
+	branch_name = resolve_ref_unsafe("HEAD", 0, unused, &rru_flags);
+	if (!(rru_flags & REF_ISSYMREF))
+		return NULL;
+	if (!skip_prefix(branch_name, "refs/heads/", &branch_name))
+		return NULL;
+
+	/* OK, do we have that ref in the list? */
+	for (list = decoration; list; list = list->next)
+		if ((list->type == DECORATION_REF_LOCAL) &&
+		    !strcmp(branch_name, list->name)) {
+			return list;
+		}
+
+	return NULL;
+}
+
+/*
  * The caller makes sure there is no funny color before calling.
  * format_decorations_extended makes sure the same after return.
  */
@@ -184,6 +221,7 @@ void format_decorations_extended(struct strbuf *sb,
 			const char *suffix)
 {
 	const struct name_decoration *decoration;
+	const struct name_decoration *current_and_HEAD;
 	const char *color_commit =
 		diff_get_color(use_color, DIFF_COMMIT);
 	const char *color_reset =
@@ -192,15 +230,32 @@ void format_decorations_extended(struct strbuf *sb,
 	decoration = get_name_decoration(&commit->object);
 	if (!decoration)
 		return;
+
+	current_and_HEAD = current_pointed_by_HEAD(decoration);
 	while (decoration) {
-		strbuf_addstr(sb, color_commit);
-		strbuf_addstr(sb, prefix);
-		strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
-		if (decoration->type == DECORATION_REF_TAG)
-			strbuf_addstr(sb, "tag: ");
-		strbuf_addstr(sb, decoration->name);
-		strbuf_addstr(sb, color_reset);
-		prefix = separator;
+		/*
+		 * When both current and HEAD are there, only
+		 * show HEAD->current where HEAD would have
+		 * appeaed, skipping the entry for current.
+		 */
+		if (current_and_HEAD && decoration == current_and_HEAD)
+			;
+		else {
+			strbuf_addstr(sb, color_commit);
+			strbuf_addstr(sb, prefix);
+			strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
+			if (decoration->type == DECORATION_REF_TAG)
+				strbuf_addstr(sb, "tag: ");
+
+			if (current_and_HEAD &&
+			    decoration->type == DECORATION_REF_HEAD)
+				strbuf_addf(sb, "HEAD->%s", current_and_HEAD->name);
+			else
+				strbuf_addstr(sb, decoration->name);
+			strbuf_addstr(sb, color_reset);
+
+			prefix = separator;
+		}
 		decoration = decoration->next;
 	}
 	strbuf_addstr(sb, color_commit);