# [PATCH] git-blame: Make the output human readable

10 messages from 2006-03-05 to 2006-03-07. Participants: Fredrik Kuivinen, Junio C Hamano, Johannes Schindelin.
Thread: https://gitlist.dev/t/3565

## Fredrik Kuivinen, 2006-03-05 11:03

Subject: [PATCH] git-blame: Make the output human readable
Message-ID: <20060305110351.GA23448@c165.ib.student.liu.se>
URL: https://gitlist.dev/e/20060305110351.GA23448%40c165.ib.student.liu.se

```
The default output mode is slightly different from git-annotate's.
However, git-annotate's output mode can be obtained by using the
'-c' flag.

Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>


---

 Makefile |    4 ++
 blame.c  |  164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 149 insertions(+), 19 deletions(-)

3459b7c5032d1462d5857cf6afc2d3e3ef61b93b
diff --git a/Makefile b/Makefile
index b6d8804..eb1887d 100644
--- a/Makefile
+++ b/Makefile
@@ -534,6 +534,10 @@ git-rev-list$X: rev-list.o $(LIB_FILE)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(OPENSSL_LIBSSL)
 
+git-blame$X: blame.o $(LIB_FILE)
+	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+		$(LIBS) -lm
+
 init-db.o: init-db.c
 	$(CC) -c $(ALL_CFLAGS) \
 		-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
diff --git a/blame.c b/blame.c
index 7308c36..6dccae5 100644
--- a/blame.c
+++ b/blame.c
@@ -5,6 +5,7 @@
 #include <assert.h>
 #include <time.h>
 #include <sys/time.h>
+#include <math.h>
 
 #include "cache.h"
 #include "refs.h"
@@ -17,8 +18,15 @@
 
 #define DEBUG 0
 
-struct commit **blame_lines;
-int num_blame_lines;
+static const char blame_usage[] = "[-c] [-l] [--] file [commit]\n"
+	"  -c, --compability Use the same output mode as git-annotate (Default: off)\n"
+	"  -l, --long        Show long commit SHA1 (Default: off)\n"
+	"  -h, --help        This message";
+
+static struct commit **blame_lines;
+static int num_blame_lines;
+static char* blame_contents;
+static int blame_len;
 
 struct util_info {
 	int *line_map;
@@ -388,9 +396,8 @@ static void init_first_commit(struct com
 	alloc_line_map(commit);
 
 	util = commit->object.util;
-	num_blame_lines = util->num_lines;
 
-	for (i = 0; i < num_blame_lines; i++)
+	for (i = 0; i < util->num_lines; i++)
 		util->line_map[i] = i;
 }
 
@@ -412,6 +419,9 @@ static void process_commits(struct rev_i
 	util = commit->object.util;
 	num_blame_lines = util->num_lines;
 	blame_lines = xmalloc(sizeof(struct commit *) * num_blame_lines);
+	blame_contents = util->buf;
+	blame_len = util->size;
+
 	for (i = 0; i < num_blame_lines; i++)
 		blame_lines[i] = NULL;
 
@@ -499,32 +509,128 @@ static void process_commits(struct rev_i
 	} while ((commit = get_revision(rev)) != NULL);
 }
 
+struct commit_info
+{
+	char* author;
+	char* author_mail;
+	unsigned long author_time;
+	char* author_tz;
+};
+
+static void get_commit_info(struct commit* commit, struct commit_info* ret)
+{
+	int len;
+	char* tmp;
+	static char author_buf[1024];
+
+	tmp = strstr(commit->buffer, "\nauthor ") + 8;
+	len = index(tmp, '\n') - tmp;
+	ret->author = author_buf;
+	memcpy(ret->author, tmp, len);
+
+	tmp = ret->author;
+	tmp += len;
+	*tmp = 0;
+	while(*tmp != ' ')
+		tmp--;
+	ret->author_tz = tmp+1;
+
+	*tmp = 0;
+	while(*tmp != ' ')
+		tmp--;
+	ret->author_time = strtoul(tmp, NULL, 10);
+
+	*tmp = 0;
+	while(*tmp != ' ')
+		tmp--;
+	ret->author_mail = tmp + 1;
+
+	*tmp = 0;
+}
+
+char* format_time(unsigned long time, const char* tz)
+{
+	static char time_buf[128];
+	time_t t = time;
+
+	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", gmtime(&t));
+	strcat(time_buf, tz);
+	return time_buf;
+}
+
 int main(int argc, const char **argv)
 {
 	int i;
 	struct commit *initial = NULL;
 	unsigned char sha1[20];
-	const char* filename;
+
+	const char *filename = NULL, *commit = NULL;
+	char filename_buf[256];
+	int sha1_len = 8;
+	int compability = 0;
+	int options = 1;
+
 	int num_args;
 	const char* args[10];
 	struct rev_info rev;
 
-	setup_git_directory();
-
-	if (argc != 3)
-		die("Usage: blame commit-ish file");
+	struct commit_info ci;
+	const char *buf;
+	int max_digits;
+
+	const char* prefix = setup_git_directory();
+
+	for(i = 1; i < argc; i++) {
+		if(options) {
+			if(!strcmp(argv[i], "-h") ||
+			   !strcmp(argv[i], "--help"))
+				usage(blame_usage);
+			else if(!strcmp(argv[i], "-l") ||
+				!strcmp(argv[i], "--long")) {
+				sha1_len = 20;
+				continue;
+			} else if(!strcmp(argv[i], "-c") ||
+				  !strcmp(argv[i], "--compability")) {
+				compability = 1;
+				continue;
+			} else if(!strcmp(argv[i], "--")) {
+				options = 0;
+				continue;
+			} else if(argv[i][0] == '-')
+				usage(blame_usage);
+			else
+				options = 0;
+		}
 
+		if(!options) {
+			if(!filename)
+				filename = argv[i];
+			else if(!commit)
+				commit = argv[i];
+			else
+				usage(blame_usage);
+		}
+	}
 
-	filename = argv[2];
+	if(!filename)
+		usage(blame_usage);
+	if(!commit)
+		commit = "HEAD";
+
+	if(prefix)
+		sprintf(filename_buf, "%s%s", prefix, filename);
+	else
+		strcpy(filename_buf, filename);
+	filename = filename_buf;
 
 	{
-		struct commit* commit;
-		if (get_sha1(argv[1], sha1))
-			die("get_sha1 failed");
-		commit = lookup_commit_reference(sha1);
+		struct commit* c;
+		if (get_sha1(commit, sha1))
+			die("get_sha1 failed, commit '%s' not found", commit);
+		c = lookup_commit_reference(sha1);
 
-		if (fill_util_info(commit, filename)) {
-			printf("%s not found in %s\n", filename, argv[1]);
+		if (fill_util_info(c, filename)) {
+			printf("%s not found in %s\n", filename, commit);
 			return 1;
 		}
 	}
@@ -533,7 +639,7 @@ int main(int argc, const char **argv)
 	args[num_args++] = NULL;
 	args[num_args++] = "--topo-order";
 	args[num_args++] = "--remove-empty";
-	args[num_args++] = argv[1];
+	args[num_args++] = commit;
 	args[num_args++] = "--";
 	args[num_args++] = filename;
 	args[num_args] = NULL;
@@ -542,13 +648,33 @@ int main(int argc, const char **argv)
 	prepare_revision_walk(&rev);
 	process_commits(&rev, filename, &initial);
 
+	buf = blame_contents;
+	max_digits = 1 + log(num_blame_lines+1)/log(10);
 	for (i = 0; i < num_blame_lines; i++) {
 		struct commit *c = blame_lines[i];
 		if (!c)
 			c = initial;
 
-		printf("%d %.8s\n", i, sha1_to_hex(c->object.sha1));
-// printf("%d %s\n", i, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
+		get_commit_info(c, &ci);
+		fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
+		if(compability)
+			printf("\t(%10s\t%10s\t%d)", ci.author,
+			       format_time(ci.author_time, ci.author_tz), i+1);
+		else
+			printf(" (%-15.15s %10s %*d) ", ci.author,
+			       format_time(ci.author_time, ci.author_tz),
+			       max_digits, i+1);
+
+		if(i == num_blame_lines - 1) {
+			fwrite(buf, blame_len - (buf - blame_contents),
+			       1, stdout);
+			if(blame_contents[blame_len-1] != '\n')
+				putc('\n', stdout);
+		} else {
+			char* next_buf = index(buf, '\n') + 1;
+			fwrite(buf, next_buf - buf, 1, stdout);
+			buf = next_buf;
+		}
 	}
 
 	if (DEBUG) {
-- 
1.2.4.g4644-dirty

```

## Junio C Hamano, 2006-03-05 12:10

Subject: Re: [PATCH] git-blame: Make the output human readable
Message-ID: <7vbqwlgkhk.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vbqwlgkhk.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <20060305110351.GA23448@c165.ib.student.liu.se>

```
Fredrik Kuivinen <freku045@student.liu.se> writes:

> The default output mode is slightly different from git-annotate's.
> However, git-annotate's output mode can be obtained by using the
> '-c' flag.

It might be better to default to human readable and make the
script consumption format an option, if only to reduce typing.

> diff --git a/Makefile b/Makefile
> index b6d8804..eb1887d 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -534,6 +534,10 @@ git-rev-list$X: rev-list.o $(LIB_FILE)
>  	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
>  		$(LIBS) $(OPENSSL_LIBSSL)
>  
> +git-blame$X: blame.o $(LIB_FILE)
> +	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
> +		$(LIBS) -lm
> +

I wonder what it is about to link this binary different from others...

> +char* format_time(unsigned long time, const char* tz)
> +{
> +	static char time_buf[128];
> +	time_t t = time;
> +
> +	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", gmtime(&t));
> +	strcat(time_buf, tz);
> +	return time_buf;
> +}

I think this shows GMT with time offset, which is compatible
with the human readable time Johannes did to git-annotate.  I do
not know what timezone CVS annotate shows its dates offhand (it
seems to only show dates).  Johannes, is this an attempt to
match what CVS does?

I am wondering if we want to be in line with the date formatting
convention used for our commits and tags, that is, to show local
timestamp with timezone.  The code to use would be show_date()
from date.c if we go that route.

```

## Fredrik Kuivinen, 2006-03-05 12:38

Subject: Re: [PATCH] git-blame: Make the output human readable
Message-ID: <20060305123800.GD23448@c165.ib.student.liu.se>
URL: https://gitlist.dev/e/20060305123800.GD23448%40c165.ib.student.liu.se
In-Reply-To: <7vbqwlgkhk.fsf@assigned-by-dhcp.cox.net>

```
On Sun, Mar 05, 2006 at 04:10:47AM -0800, Junio C Hamano wrote:
> Fredrik Kuivinen <freku045@student.liu.se> writes:
> 
> > The default output mode is slightly different from git-annotate's.
> > However, git-annotate's output mode can be obtained by using the
> > '-c' flag.
> 
> It might be better to default to human readable and make the
> script consumption format an option, if only to reduce typing.
> 

The default output format is human readable, but it is different from
the output format used by git-annotate. By default, (when the patch is
applied) git-blame outputs lines with on the following form:

   <eight first digits of commit SHA1> (<first 15 letters of committer's name> YYYY-MM-DD HH:MM:SS TZ <line number, right justified>) file contents

where as git-annotate uses

   <eight first digits of commit SHA1>\t(<committer's name, right justified, padded to 10 characters> YYYY-MM-DD HH:MM:SS TZ\t<line number>)file contents

I find the first format easier to read since everything is aligned (we
always output 15 characters for the committer's name padded with
spaces if necessary and the line numbers are padded appropriately). It
also takes up less space on screen since it doesn't use tabs.

However, I wanted to use the tests for git-annotate to test git-blame
too and the tests do, of course, expect the output to be in
git-annotate's format. Hence, the '-c' switch.

We may want to add an output format suitable for scripts too, which
just lists the SHA1. But I don't think it is much more difficult to
parse the format above, at least not if you just want the SHA1s.

> > diff --git a/Makefile b/Makefile
> > index b6d8804..eb1887d 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -534,6 +534,10 @@ git-rev-list$X: rev-list.o $(LIB_FILE)
> >  	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
> >  		$(LIBS) $(OPENSSL_LIBSSL)
> >  
> > +git-blame$X: blame.o $(LIB_FILE)
> > +	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
> > +		$(LIBS) -lm
> > +
> 
> I wonder what it is about to link this binary different from others...
> 

It uses log(3) to compute the number of digits needed to represent the
last line number. It is probably better to this some other way
though...

> > +char* format_time(unsigned long time, const char* tz)
> > +{
> > +	static char time_buf[128];
> > +	time_t t = time;
> > +
> > +	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", gmtime(&t));
> > +	strcat(time_buf, tz);
> > +	return time_buf;
> > +}
> 
> I think this shows GMT with time offset, which is compatible
> with the human readable time Johannes did to git-annotate.  I do
> not know what timezone CVS annotate shows its dates offhand (it
> seems to only show dates).  Johannes, is this an attempt to
> match what CVS does?
> 
> I am wondering if we want to be in line with the date formatting
> convention used for our commits and tags, that is, to show local
> timestamp with timezone.  The code to use would be show_date()
> from date.c if we go that route.
> 

I think it is a good idea. Consistency is good.

- Fredrik

```

## Johannes Schindelin, 2006-03-05 14:11

Subject: Re: [PATCH] git-blame: Make the output human readable
Message-ID: <Pine.LNX.4.63.0603051509480.12799@wbgn013.biozentrum.uni-wuerzburg.de>
URL: https://gitlist.dev/e/Pine.LNX.4.63.0603051509480.12799%40wbgn013.biozentrum.uni-wuerzburg.de
In-Reply-To: <7vbqwlgkhk.fsf@assigned-by-dhcp.cox.net>

```
Hi,

On Sun, 5 Mar 2006, Junio C Hamano wrote:

> Fredrik Kuivinen <freku045@student.liu.se> writes:
> 
> > +char* format_time(unsigned long time, const char* tz)
> > +{
> > +	static char time_buf[128];
> > +	time_t t = time;
> > +
> > +	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", gmtime(&t));
> > +	strcat(time_buf, tz);
> > +	return time_buf;
> > +}
> 
> I think this shows GMT with time offset, which is compatible
> with the human readable time Johannes did to git-annotate.  I do
> not know what timezone CVS annotate shows its dates offhand (it
> seems to only show dates).  Johannes, is this an attempt to
> match what CVS does?

CVS only shows the date, something like

	strftime("%Y-%b-%d", gmtime($timestamp));

> I am wondering if we want to be in line with the date formatting
> convention used for our commits and tags, that is, to show local
> timestamp with timezone.  The code to use would be show_date()
> from date.c if we go that route.

I like that approach. Sometimes imitating CVS can be overdone.

Ciao,
Dscho

```

## Johannes Schindelin, 2006-03-05 14:23

Subject: Re: [PATCH] git-blame: Make the output human readable
Message-ID: <Pine.LNX.4.63.0603051516020.12866@wbgn013.biozentrum.uni-wuerzburg.de>
URL: https://gitlist.dev/e/Pine.LNX.4.63.0603051516020.12866%40wbgn013.biozentrum.uni-wuerzburg.de
In-Reply-To: <20060305123800.GD23448@c165.ib.student.liu.se>

```
Hi,

On Sun, 5 Mar 2006, Fredrik Kuivinen wrote:

> On Sun, Mar 05, 2006 at 04:10:47AM -0800, Junio C Hamano wrote:
> > Fredrik Kuivinen <freku045@student.liu.se> writes:
> > 
> > > +git-blame$X: blame.o $(LIB_FILE)
> > > +	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
> > > +		$(LIBS) -lm
> > > +
> > 
> > I wonder what it is about to link this binary different from others...
> > 
> 
> It uses log(3) to compute the number of digits needed to represent the
> last line number. It is probably better to this some other way
> though...

How about this:

-	max_digits = 1 + log(num_blame_lines+1)/log(10);
+	for (i = 10, max_digits = 1; i <= num_blame_lines + 1;
+		i *= 10, max_digits++);

(Totally untested)

Hth,
Dscho

```

## Junio C Hamano, 2006-03-05 21:28

Subject: Re: [PATCH] git-blame: Make the output human readable
Message-ID: <7vveuseg2u.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vveuseg2u.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <20060305123800.GD23448@c165.ib.student.liu.se>

```
Fredrik Kuivinen <freku045@student.liu.se> writes:

> I find the first format easier to read since everything is aligned (we
> always output 15 characters for the committer's name padded with
> spaces if necessary and the line numbers are padded appropriately). It
> also takes up less space on screen since it doesn't use tabs.

Careful.  The convention for names is to encode them in UTF-8,
so if you mean 15 "characters" that might be OK but if it can
truncate in the middle of a multibyte sequence of UTF-8 encoded
single character it is a no-no.

We are talking about "casual" aligning, so I would not bring up
"proportional fonts", but even on a monospace terminal and
line-printer output, there still is the issue of byte count not
matching columns.  In an i18nized environment, aligning columns
by counting bytes is a lost battle.

In any case, please make sure you do not chop a character in the
middle at least [*1*].

> We may want to add an output format suitable for scripts too, which
> just lists the SHA1. But I don't think it is much more difficult to
> parse the format above, at least not if you just want the SHA1s.

Fair enough.

> It uses log(3) to compute the number of digits needed to represent the
> last line number. It is probably better to this some other way
> though...

Yup.


[Footnote]

*1* I was kind of impressed that Linus was careful about this
issue when I saw the commit log chopping is only done at line
boundaries.  A very careful coder would have chopped the last
line in the middle at character boundary, but if you do not want
to bother that much, chopping at line boundary is better than
chopping the last line in the middle of a character ;-).

```

## Junio C Hamano, 2006-03-05 22:46

Subject: [PATCH] blame: avoid -lm by not using log().
Message-ID: <7vhd6cechf.fsf_-_@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vhd6cechf.fsf_-_%40assigned-by-dhcp.cox.net
In-Reply-To: <20060305123800.GD23448@c165.ib.student.liu.se>

```

... as suggested on the list.

Signed-off-by: Junio C Hamano <junkio@cox.net>

---

 Makefile |    4 ----
 blame.c  |    6 ++++--
 2 files changed, 4 insertions(+), 6 deletions(-)

e0539773df17e5f8410850b8ffcfe8d2fa14ab7a
diff --git a/Makefile b/Makefile
index eb1887d..b6d8804 100644
--- a/Makefile
+++ b/Makefile
@@ -534,10 +534,6 @@ git-rev-list$X: rev-list.o $(LIB_FILE)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(OPENSSL_LIBSSL)
 
-git-blame$X: blame.o $(LIB_FILE)
-	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-		$(LIBS) -lm
-
 init-db.o: init-db.c
 	$(CC) -c $(ALL_CFLAGS) \
 		-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
diff --git a/blame.c b/blame.c
index 168b1f5..59146fa 100644
--- a/blame.c
+++ b/blame.c
@@ -589,7 +589,7 @@ int main(int argc, const char **argv)
 				usage(blame_usage);
 			else if(!strcmp(argv[i], "-l") ||
 				!strcmp(argv[i], "--long")) {
-				sha1_len = 20;
+				sha1_len = 40;
 				continue;
 			} else if(!strcmp(argv[i], "-c") ||
 				  !strcmp(argv[i], "--compability")) {
@@ -651,7 +651,9 @@ int main(int argc, const char **argv)
 	process_commits(&rev, filename, &initial);
 
 	buf = blame_contents;
-	max_digits = 1 + log(num_blame_lines+1)/log(10);
+	for (max_digits = 1, i = 10; i <= num_blame_lines; max_digits++)
+		i *= 10;
+
 	for (i = 0; i < num_blame_lines; i++) {
 		struct commit *c = blame_lines[i];
 		if (!c)
-- 
1.2.4.gee5c7

```

## Junio C Hamano, 2006-03-05 22:48

Subject: [PATCH] blame and annotate: show localtime with timezone.
Message-ID: <7vbqwkecf2.fsf_-_@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vbqwkecf2.fsf_-_%40assigned-by-dhcp.cox.net
In-Reply-To: <20060305123800.GD23448@c165.ib.student.liu.se>

```
Earlier they showed gmtime and timezone, which was inconsistent
with the way our commits and tags are pretty-printed.

Signed-off-by: Junio C Hamano <junkio@cox.net>

---

 blame.c           |   15 ++++++++++++---
 git-annotate.perl |    8 +++++++-
 2 files changed, 19 insertions(+), 4 deletions(-)

e29f298a17ee898fd0d191d448ba4fc202175896
diff --git a/blame.c b/blame.c
index 59146fa..a3a8ddc 100644
--- a/blame.c
+++ b/blame.c
@@ -550,13 +550,22 @@ static void get_commit_info(struct commi
 	*tmp = 0;
 }
 
-char* format_time(unsigned long time, const char* tz)
+static const char* format_time(unsigned long time, const char* tz_str)
 {
 	static char time_buf[128];
 	time_t t = time;
+	int minutes, tz;
+	struct tm *tm;
 
-	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", gmtime(&t));
-	strcat(time_buf, tz);
+	tz = atoi(tz_str);
+	minutes = tz < 0 ? -tz : tz;
+	minutes = (minutes / 100)*60 + (minutes % 100);
+	minutes = tz < 0 ? -minutes : minutes;
+	t = time + minutes * 60;
+	tm = gmtime(&t);
+
+	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
+	strcat(time_buf, tz_str);
 	return time_buf;
 }
 
diff --git a/git-annotate.perl b/git-annotate.perl
index d93ee19..b113def 100755
--- a/git-annotate.perl
+++ b/git-annotate.perl
@@ -418,7 +418,13 @@ sub format_date {
 		return $_[0];
 	}
 	my ($timestamp, $timezone) = split(' ', $_[0]);
-	return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($timestamp));
+	my $minutes = abs($timezone);
+	$minutes = int($minutes / 100) * 60 + ($minutes % 100);
+	if ($timezone < 0) {
+	    $minutes = -$minutes;
+	}
+	my $t = $timestamp + $minutes * 60;
+	return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($t));
 }
 
 # Copied from git-send-email.perl - We need a Git.pm module..
-- 
1.2.4.gee5c7

```

## Johannes Schindelin, 2006-03-05 23:36

Subject: Re: [PATCH] blame: avoid -lm by not using log().
Message-ID: <Pine.LNX.4.63.0603060032410.32270@wbgn013.biozentrum.uni-wuerzburg.de>
URL: https://gitlist.dev/e/Pine.LNX.4.63.0603060032410.32270%40wbgn013.biozentrum.uni-wuerzburg.de
In-Reply-To: <7vhd6cechf.fsf_-_@assigned-by-dhcp.cox.net>

```
Hi,

On Sun, 5 Mar 2006, Junio C Hamano wrote:

> -	max_digits = 1 + log(num_blame_lines+1)/log(10);
> +	for (max_digits = 1, i = 10; i <= num_blame_lines; max_digits++)
> +		i *= 10;

According to my late-night maths skills, these two methods do not do the 
same thing: if num_blame_lines == 9, the log method assigns 2 to 
max_digits, but the for loop stops at max_digits == 1. A trivial fix would 
be to test for i <= num_blame_lines + 1.

Ciao,
Dscho

```

## Fredrik Kuivinen, 2006-03-07 16:34

Subject: Re: [PATCH] git-blame: Make the output human readable
Message-ID: <20060307163427.GB4457@c165.ib.student.liu.se>
URL: https://gitlist.dev/e/20060307163427.GB4457%40c165.ib.student.liu.se
In-Reply-To: <7vveuseg2u.fsf@assigned-by-dhcp.cox.net>

```
On Sun, Mar 05, 2006 at 01:28:57PM -0800, Junio C Hamano wrote:
> Fredrik Kuivinen <freku045@student.liu.se> writes:
> 
> > I find the first format easier to read since everything is aligned (we
> > always output 15 characters for the committer's name padded with
> > spaces if necessary and the line numbers are padded appropriately). It
> > also takes up less space on screen since it doesn't use tabs.
> 
> Careful.  The convention for names is to encode them in UTF-8,
> so if you mean 15 "characters" that might be OK but if it can
> truncate in the middle of a multibyte sequence of UTF-8 encoded
> single character it is a no-no.
> 
> We are talking about "casual" aligning, so I would not bring up
> "proportional fonts", but even on a monospace terminal and
> line-printer output, there still is the issue of byte count not
> matching columns.  In an i18nized environment, aligning columns
> by counting bytes is a lost battle.
> 
> In any case, please make sure you do not chop a character in the
> middle at least [*1*].
> 

I hadn't thought of that. The code as it is now will occasionally do
the wrong thing when encountered with multibyte per code point
encodings. If we can assume that the name is in UTF-8 then the code
posted by "linux@horizon.com" to the list should do the trick.

However, can we assume that the name is in UTF-8? Do
i18n.commitencoding only apply to the commit message, or is it
intended to apply to the author/committer information too?

I am beginning to think that it might be best to, by default, store
the author, committer and commit messages in UTF-8. And then
automatically convert from/to the users locale on input/output. (I
just read the old "Rss produced by git is not valid xml?" thread where
this issue was discussed quite a bit. However, I didn't see the 'store
in UTF-8 by default and convert from/to users locale' method anywhere
in that thread.)

- Fredrik

```
