git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] git-blame: Make the output human readable
@ 2006-03-05 11:03 Fredrik Kuivinen
  2006-03-05 12:10 ` Junio C Hamano
  0 siblings, 1 reply; 15+ messages in thread
From: Fredrik Kuivinen @ 2006-03-05 11:03 UTC (permalink / raw)
  To: git; +Cc: junkio

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

^ permalink raw reply related	[flat|nested] 15+ messages in thread
* Re: [PATCH] git-blame: Make the output human readable
@ 2006-03-06 19:33 linux
  2006-03-08 14:32 ` Sergey Vlasov
  0 siblings, 1 reply; 15+ messages in thread
From: linux @ 2006-03-06 19:33 UTC (permalink / raw)
  To: junkio; +Cc: git

Well, getting 15 characters in UTF-8 is easy (just stop before the 16th
byte for which ((b & 0xc0) != 0x80)), but what about combining characters?

You've got accents and stuff to worry about.  And the annoying fact that
Unicode defined accents as suffixes, so you have to go past the 15th
column to include all of the 

And then there's that fact that many characters are traditionally
represented as double-wide forms, even on character terminals.

See http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c for details
an an example implementation of wcwidth().

Using that, it would be something like (compiles but untested):

/*
 * Return the number of bytes from the nul-terminated utf8 string
 * that can be printed in at most max columns using a monospaced
 * font.  *actual returns the number of columns actually occupied,
 * which may be less than max.
 *
 * Output is truncated before any control characters or illegal
 * UTF-8 sequences.
 */
unsigned
fit_columns(char const *utf8, unsigned max, unsigned *actual)
{
	char const * const origin = utf8;
	unsigned width = 0;
	unsigned pos = 0;
	unsigned c;

	for (;;) {
		unsigned w;
		unsigned c = *utf8++;

		/* Part 1: Parse the next Unicode code point */
		if (c < 0x20) {
			break;	/* Control character - stop */
		} else if (c < 0x7F) {
			w = 1;	/* Standard ASCII */
		} else if (c < 0xC2 || c > 0xF4) {
			break;	/* DEL or illegal Unicode */
		} else {
			/* Multi-byte UTF-8 sequence */
			unsigned n;
			unsigned char byte = *utf8++;

			if (c < 0xE0) {
				/* 2-byte sequence: U+0080..U+07FF */
				n = 1;
				c &= 0x1F;
			} else if (c < 0xF0) {
				/* 3-byte sequence: U+0800..U+FFFF */
				if (c == 0xE0 && byte < 0xA0)
					break;	/* < /U+0800 */
				n = 2;
				c &= 0x0F;
			} else {
				/* 4-byte sequence: U+10000..U+10FFFF */
				if (byte < 0x90 ? c == 0xF0 : c == 0xF4)
					break; /* < 10000 or > 10FFFF */
				n = 3;
				c &= 0x07;
			}

			for (; n--; byte = *utf8++) {
				if (byte & 0xc0 != 0x80)
					goto done;	/* Double break */
				c = (c << 6) | (byte & 0x3f);
			}
			/* Now find the width of it */
			w = wcwidth(c);
			if (w == -1)
				break;
		}

		/* Part 2: Figure out if it will fit */
		if (width + w > max)
			break;	/* Would exceed space - stop */
		/* Part 3: It fits; update our statistics */
		width += w;
		pos = (unsigned)(utf8 - origin);
	}

done:
	if (actual)
		*actual = width;
	return pos;
}

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2006-03-08 19:06 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-03-05 11:03 [PATCH] git-blame: Make the output human readable Fredrik Kuivinen
2006-03-05 12:10 ` Junio C Hamano
2006-03-05 12:38   ` Fredrik Kuivinen
2006-03-05 14:23     ` Johannes Schindelin
2006-03-05 21:28     ` Junio C Hamano
2006-03-07 16:34       ` Fredrik Kuivinen
2006-03-05 22:46     ` [PATCH] blame: avoid -lm by not using log() Junio C Hamano
2006-03-05 23:36       ` Johannes Schindelin
2006-03-05 22:48     ` [PATCH] blame and annotate: show localtime with timezone Junio C Hamano
2006-03-05 14:11   ` [PATCH] git-blame: Make the output human readable Johannes Schindelin
  -- strict thread matches above, loose matches on Subject: below --
2006-03-06 19:33 linux
2006-03-08 14:32 ` Sergey Vlasov
2006-03-08 18:04   ` linux
2006-03-08 18:30     ` Sergey Vlasov
2006-03-08 19:06       ` linux

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).