Thread (9 messages) 9 messages, 3 authors, 13d ago

Re: git-blame vs. abbrev

From: Laszlo Ersek <hidden>
Date: 2026-07-01 08:45:36

On 6/30/26 23:38, René Scharfe wrote:
quoted hunk ↗ jump to hunk
On 6/30/26 9:49 PM, Junio C Hamano wrote:
quoted
Laszlo Ersek [off-list ref] writes:
quoted
Hi,

when git-blame is passed the "-b" option ("Show blank SHA-1 for boundary
commits"), shouldn't git-blame *stop* reserving a commit hash nibble for
the caret that otherwise marks boundary commits?

More directly, I find it inconvenient that git-blame shows commit hashes
that are one nibble longer (13) than my "core.abbrev" (12) setting;
Just for the sake of aesthetics, I agree that when we are not
showing the boundary mark, it would make sense not to reserve one
column that we know we will never use.
Here's a patch to reserve a column only if marks are actually shown.

I strongly suspect that any line can only have a single mark, but I
didn't bother checking and proving whether that's actually true; the
new code should be able to handle multiple marks just fine.

Misses tests.

René


---
  builtin/blame.c | 61 ++++++++++++++++++++++++++++++++++---------------
  1 file changed, 42 insertions(+), 19 deletions(-)
diff --git a/builtin/blame.c b/builtin/blame.c
index ffbd3ce5c5..0e747a43f2 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -453,6 +453,39 @@ static void determine_line_heat(struct commit_info *ci, const char **dest_color)
  	*dest_color = colorfield[i].col;
  }
  
+static size_t handle_marks(const struct blame_entry *ent, int opt, bool print)
+{
+	size_t len = 0;
+
+	if ((ent->suspect->commit->object.flags & UNINTERESTING) &&
+	    !blank_boundary && !(opt & OUTPUT_ANNOTATE_COMPAT)) {
+		if (print)
+			putchar('^');
+		len++;
+	}
+	if (mark_unblamable_lines && ent->unblamable) {
+		if (print)
+			putchar('*');
+		len++;
+	}
+	if (mark_ignored_lines && ent->ignored) {
+		if (print)
+			putchar('?');
+		len++;
+	}
+	return len;
+}
+
+static size_t print_marks(const struct blame_entry *ent, int opt)
+{
+	return handle_marks(ent, opt, true);
+}
+
+static size_t count_marks(const struct blame_entry *ent, int opt)
+{
+	return handle_marks(ent, opt, false);
+}
+
  static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent,
  		       int opt, struct blame_entry *prev_ent)
  {
@@ -499,23 +532,10 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent,
  		if (color)
  			fputs(color, stdout);
  
-		if (suspect->commit->object.flags & UNINTERESTING) {
-			if (blank_boundary) {
-				memset(hex, ' ', strlen(hex));
-			} else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
-				length--;
-				putchar('^');
-			}
-		}
-
-		if (mark_unblamable_lines && ent->unblamable) {
-			length--;
-			putchar('*');
-		}
-		if (mark_ignored_lines && ent->ignored) {
-			length--;
-			putchar('?');
-		}
+		if ((suspect->commit->object.flags & UNINTERESTING) &&
+		    blank_boundary)
+			memset(hex, ' ', strlen(hex));
+		length -= print_marks(ent, opt);
  
  		printf("%.*s", (int)(length < GIT_MAX_HEXSZ ? length : GIT_MAX_HEXSZ), hex);
  		if (opt & OUTPUT_ANNOTATE_COMPAT) {
@@ -647,11 +667,15 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)
  	struct blame_entry *e;
  	int compute_auto_abbrev = (abbrev < 0);
  	int auto_abbrev = DEFAULT_ABBREV;
+	size_t max_marks_count = 0;
  
  	for (e = sb->ent; e; e = e->next) {
  		struct blame_origin *suspect = e->suspect;
  		int num;
+		size_t marks_count = count_marks(e, *option);
  
+		if (max_marks_count < marks_count)
+			max_marks_count = marks_count;
  		if (compute_auto_abbrev)
  			auto_abbrev = update_auto_abbrev(auto_abbrev, suspect);
  		if (strcmp(suspect->path, sb->path))
@@ -685,8 +709,7 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)
  	max_score_digits = decimal_width(largest_score);
  
  	if (compute_auto_abbrev)
-		/* one more abbrev length is needed for the boundary commit */
-		abbrev = auto_abbrev + 1;
+		abbrev = auto_abbrev + max_marks_count;
  }
  
  static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa)
This way, I didn't even need to pass "-b". :)

I didn't try to test the patch exhaustively, but it definitely does what 
I'm after.

Tested-by: Laszlo Ersek <redacted>

Thank you!
Laszlo
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help