[PATCH] Build in shortlog

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Johannes Schindelin <Johannes.Schindelin@xxxxxx>
---
 Documentation/git-shortlog.txt |    1 +
 Makefile                       |    5 +-
 builtin-shortlog.c             |  302 ++++++++++++++++++++++++++++++++++++++++
 builtin.h                      |    1 +
 git-shortlog.perl              |  234 -------------------------------
 git.c                          |    1 +
 path-list.c                    |    2 +-
 7 files changed, 309 insertions(+), 237 deletions(-)

diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index d54fc3e..95fa901 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -8,6 +8,7 @@ git-shortlog - Summarize 'git log' outpu
 SYNOPSIS
 --------
 git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
+git-shortlog [-n|--number] [-s|--summary] [<committish>...]
 
 DESCRIPTION
 -----------
diff --git a/Makefile b/Makefile
index 018dad2..0beda57 100644
--- a/Makefile
+++ b/Makefile
@@ -106,7 +106,7 @@ uname_P := $(shell sh -c 'uname -p 2>/de
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
-CFLAGS = -g -O2 -Wall
+CFLAGS = -g -O0 -Wall
 LDFLAGS =
 ALL_CFLAGS = $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
@@ -178,7 +178,7 @@ SCRIPT_SH = \
 
 SCRIPT_PERL = \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
-	git-shortlog.perl git-rerere.perl \
+	git-rerere.perl \
 	git-cvsserver.perl \
 	git-svnimport.perl git-cvsexportcommit.perl \
 	git-send-email.perl git-svn.perl
@@ -306,6 +306,7 @@ BUILTIN_OBJS = \
 	builtin-rev-parse.o \
 	builtin-rm.o \
 	builtin-runstatus.o \
+	builtin-shortlog.o \
 	builtin-show-branch.o \
 	builtin-stripspace.o \
 	builtin-symbolic-ref.o \
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
new file mode 100644
index 0000000..df60bd2
--- /dev/null
+++ b/builtin-shortlog.c
@@ -0,0 +1,302 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "path-list.h"
+#include "revision.h"
+#include <string.h>
+
+static const char shortlog_usage[] =
+"git-shortlog [-n] [-s] [<commit-id>... ]\n";
+
+static int compare_by_number(const void *a1, const void *a2)
+{
+	const struct path_list_item *i1 = a1, *i2 = a2;
+	const struct path_list *l1 = i1->util, *l2 = i2->util;
+
+	if (l1->nr < l2->nr)
+		return -1;
+	else if (l1->nr == l2->nr)
+		return 0;
+	else
+		return +1;
+}
+
+static struct path_list_item mailmap_list[] = {
+	{ "R.Marek@xxxxxxxxxx", (void*)"Rudolf Marek" },
+	{ "Ralf.Wildenhues@xxxxxx", (void*)"Ralf Wildenhues" },
+	{ "aherrman@xxxxxxxxxx", (void*)"Andreas Herrmann" },
+	{ "akpm@xxxxxxxx", (void*)"Andrew Morton" },
+	{ "andrew.vasquez@xxxxxxxxxx", (void*)"Andrew Vasquez" },
+	{ "aquynh@xxxxxxxxx", (void*)"Nguyen Anh Quynh" },
+	{ "axboe@xxxxxxx", (void*)"Jens Axboe" },
+	{ "blaisorblade@xxxxxxxx", (void*)"Paolo 'Blaisorblade' Giarrusso" },
+	{ "bunk@xxxxxxxxx", (void*)"Adrian Bunk" },
+	{ "domen@xxxxxxxxxxxx", (void*)"Domen Puncer" },
+	{ "dougg@xxxxxxxxxx", (void*)"Douglas Gilbert" },
+	{ "dwmw2@xxxxxxxxxxxxxxxxxxxxxxx", (void*)"David Woodhouse" },
+	{ "ecashin@xxxxxxxxxx", (void*)"Ed L Cashin" },
+	{ "felix@xxxxxxxxxxxx", (void*)"Felix Moeller" },
+	{ "fzago@xxxxxxxxxxxxxxxxxxxxx", (void*)"Frank Zago" },
+	{ "gregkh@xxxxxxx", (void*)"Greg Kroah-Hartman" },
+	{ "hch@xxxxxx", (void*)"Christoph Hellwig" },
+	{ "htejun@xxxxxxxxx", (void*)"Tejun Heo" },
+	{ "jejb@mulgrave.(none)", (void*)"James Bottomley" },
+	{ "jejb@xxxxxxxxxxxxxxxxxxxxxxx", (void*)"James Bottomley" },
+	{ "jgarzik@xxxxxxxxxxxxxx", (void*)"Jeff Garzik" },
+	{ "johnpol@xxxxxxxxxxx", (void*)"Evgeniy Polyakov" },
+	{ "kay.sievers@xxxxxxxx", (void*)"Kay Sievers" },
+	{ "minyard@xxxxxxx", (void*)"Corey Minyard" },
+	{ "mshah@xxxxxxxx", (void*)"Mitesh shah" },
+	{ "pj@xxxxxxxxxxx", (void*)"Peter A Jonsson" },
+	{ "rmps@xxxxxxxxxxxxxxx", (void*)"Rui Saraiva" },
+	{ "santtu.hyrkko@xxxxxxxxx", (void*)"Santtu Hyrkk,Av(B" },
+	{ "simon@xxxxxxxxxxxxxxxxx", (void*)"Simon Kelley" },
+	{ "ssant@xxxxxxxxxx", (void*)"Sachin P Sant" },
+	{ "terra@xxxxxxxxx", (void*)"Morten Welinder" },
+	{ "tony.luck@xxxxxxxxx", (void*)"Tony Luck" },
+	{ "welinder@xxxxxxxxxxxxxxxxxx", (void*)"Morten Welinder" },
+	{ "welinder@xxxxxxxxxxxxxxxxx", (void*)"Morten Welinder" },
+	{ "welinder@xxxxxxxxx", (void*)"Morten Welinder" }
+};
+
+static struct path_list mailmap = {
+	mailmap_list,
+	sizeof(mailmap_list) / sizeof(struct path_list_item), 0, 0
+};
+
+static int map_email(char *email, char *name, int maxlen)
+{
+	char *p;
+	struct path_list_item *item;
+
+	/* autocomplete common developers */
+	p = strchr(email, '>');
+	if (!p)
+		return 0;
+
+	*p = '\0';
+	item = path_list_lookup(email, &mailmap);
+	if (item != NULL) {
+		const char *realname = (const char *)item->util;
+		strncpy(name, realname, maxlen);
+		return 1;
+	}
+	return 0;
+}
+
+static void insert_author_oneline(struct path_list *list,
+		const char *author, int authorlen,
+		const char *oneline, int onelinelen)
+{
+	const char *dot3 = "/pub/scm/linux/kernel/git/";
+	char *buffer, *p;
+	struct path_list_item *item;
+	struct path_list *onelines;
+
+	while (authorlen > 0 && isspace(author[authorlen - 1]))
+		authorlen--;
+
+	buffer = xmalloc(authorlen + 1);
+	memcpy(buffer, author, authorlen);
+	buffer[authorlen] = '\0';
+
+	item = path_list_insert(buffer, list);
+	if (item->util == NULL)
+		item->util = xcalloc(1, sizeof(struct path_list));
+	else
+		free(buffer);
+
+	if (!strncmp(oneline, "[PATCH", 6)) {
+		char *eob = strchr(buffer, ']');
+
+		while (isspace(eob[1]) && eob[1] != '\n')
+			eob++;
+		if (eob - oneline < onelinelen) {
+			onelinelen -= eob - oneline;
+			oneline = eob;
+		}
+	}
+
+	while (onelinelen > 0 && isspace(oneline[0])) {
+		oneline++;
+		onelinelen--;
+	}
+
+	while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
+		onelinelen--;
+
+	buffer = xmalloc(onelinelen + 1);
+	memcpy(buffer, oneline, onelinelen);
+	buffer[onelinelen] = '\0';
+
+	while ((p = strstr(buffer, dot3)) != NULL) {
+		memcpy(p, "...", 3);
+		strcpy(p + 2, p + sizeof(dot3) - 1);
+	}
+
+
+	onelines = item->util;
+	if (onelines->nr >= onelines->alloc) {
+		onelines->alloc = alloc_nr(onelines->nr);
+		onelines->items = xrealloc(onelines->items,
+				onelines->alloc
+				* sizeof(struct path_list_item));
+	}
+
+	onelines->items[onelines->nr].util = NULL;
+	onelines->items[onelines->nr++].path = buffer;
+}
+
+static void read_from_stdin(struct path_list *list)
+{
+	char buffer[1024];
+
+	while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
+		char *bob;
+		if ((buffer[0] == 'A' || buffer[0] == 'a') &&
+				!strncmp(buffer + 1, "uthor: ", 7) &&
+				(bob = strchr(buffer + 7, '<')) != NULL) {
+			char buffer2[1024], offset = 0;
+
+			if (map_email(bob + 1, buffer, sizeof(buffer)))
+				bob = buffer + strlen(buffer);
+			else {
+				offset = 8;
+				while (isspace(bob[-1]))
+					bob--;
+			}
+
+			while (fgets(buffer2, sizeof(buffer2), stdin) &&
+					buffer2[0] != '\n')
+				; /* chomp input */
+			if (fgets(buffer2, sizeof(buffer2), stdin))
+				insert_author_oneline(list,
+						buffer + offset,
+						bob - buffer - offset,
+						buffer2, strlen(buffer2));
+		}
+	}
+}
+
+static void get_from_rev(struct rev_info *rev, struct path_list *list)
+{
+	char scratch[1024];
+	struct commit *commit;
+
+	prepare_revision_walk(rev);
+	while ((commit = get_revision(rev)) != NULL) {
+		char *author = NULL, *oneline, *buffer;
+		int authorlen, onelinelen;
+
+		/* get author and oneline */
+		for (buffer = commit->buffer; buffer && *buffer != '\0' &&
+				*buffer != '\n'; ) {
+			char *eol = strchr(buffer, '\n');
+
+			if (eol == NULL)
+				eol = buffer + strlen(buffer);
+			else
+				eol++;
+
+			if (!strncmp(buffer, "author ", 7)) {
+				char *bracket = strchr(buffer, '<');
+
+				if (bracket == NULL || bracket > eol)
+					die("Invalid commit buffer: %s",
+					    sha1_to_hex(commit->object.sha1));
+
+				if (map_email(bracket + 1, scratch,
+							sizeof(scratch))) {
+					author = scratch;
+					authorlen = strlen(scratch);
+				} else {
+					while (bracket[-1] == ' ')
+						bracket--;
+
+					author = buffer + 7;
+					authorlen = bracket - buffer - 7;
+				}
+			}
+			buffer = eol;
+		}
+
+		if (author == NULL)
+			die ("Missing author: %s",
+					sha1_to_hex(commit->object.sha1));
+
+		if (buffer == NULL || *buffer == '\0') {
+			oneline = "<none>";
+			onelinelen = sizeof(oneline) + 1;
+		} else {
+			char *eol;
+
+			oneline = buffer + 1;
+			eol = strchr(oneline, '\n');
+			if (eol == NULL)
+				onelinelen = strlen(oneline);
+			else
+				onelinelen = eol - oneline;
+		}
+
+		insert_author_oneline(list,
+				author, authorlen, oneline, onelinelen);
+	}
+
+}
+
+int cmd_shortlog(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct path_list list = { NULL, 0, 0, 1 };
+	int i, j, sort_by_number = 0, summary = 0;
+
+	init_revisions(&rev, prefix);
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	while (argc > 1) {
+		if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
+			sort_by_number = 1;
+		else if (!strcmp(argv[1], "-s") ||
+				!strcmp(argv[1], "--summary"))
+			summary = 1;
+		else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+			usage(shortlog_usage);
+		else
+			die ("unrecognized argument: %s", argv[1]);
+		argv++;
+		argc--;
+	}
+
+	if (rev.pending.nr == 1)
+		die ("Need a range!");
+	else if (rev.pending.nr == 0)
+		read_from_stdin(&list);
+	else
+		get_from_rev(&rev, &list);
+
+	if (sort_by_number)
+		qsort(list.items, sizeof(struct path_list_item), list.nr,
+			compare_by_number);
+
+	for (i = 0; i < list.nr; i++) {
+		struct path_list *onelines = list.items[i].util;
+
+		printf("%s (%d):\n", list.items[i].path, onelines->nr);
+		if (!summary) {
+			for (j = onelines->nr - 1; j >= 0; j--)
+				printf("      %s\n", onelines->items[j].path);
+			printf("\n");
+		}
+
+		onelines->strdup_paths = 1;
+		path_list_clear(onelines, 1);
+		free(onelines);
+		list.items[i].util = NULL;
+	}
+
+	list.strdup_paths = 1;
+	path_list_clear(&list, 1);
+
+	return 0;
+}
+
diff --git a/builtin.h b/builtin.h
index 9683a7c..0ce8f8b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -51,6 +51,7 @@ extern int cmd_rev_list(int argc, const 
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
 extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
+extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
diff --git a/git-shortlog.perl b/git-shortlog.perl
deleted file mode 100755
index 334fec7..0000000
--- a/git-shortlog.perl
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use Getopt::Std;
-use File::Basename qw(basename dirname);
-
-our ($opt_h, $opt_n, $opt_s);
-getopts('hns');
-
-$opt_h && usage();
-
-sub usage {
-	print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < <log_data>\n";
-        exit(1);
-}
-
-my (%mailmap);
-my (%email);
-my (%map);
-my $pstate = 1;
-my $n_records = 0;
-my $n_output = 0;
-
-sub shortlog_entry($$) {
-	my ($name, $desc) = @_;
-	my $key = $name;
-
-	$desc =~ s#/pub/scm/linux/kernel/git/#/.../#g;
-	$desc =~ s#\[PATCH\] ##g;
-
-	# store description in array, in email->{desc list} map
-	if (exists $map{$key}) {
-		# grab ref
-		my $obj = $map{$key};
-
-		# add desc to array
-		push(@$obj, $desc);
-	} else {
-		# create new array, containing 1 item
-		my @arr = ($desc);
-
-		# store ref to array
-		$map{$key} = \@arr;
-	}
-}
-
-# sort comparison function
-sub by_name($$) {
-	my ($a, $b) = @_;
-
-	uc($a) cmp uc($b);
-}
-sub by_nbentries($$) {
-	my ($a, $b) = @_;
-	my $a_entries = $map{$a};
-	my $b_entries = $map{$b};
-
-	@$b_entries - @$a_entries || by_name $a, $b;
-}
-
-my $sort_method = $opt_n ? \&by_nbentries : \&by_name;
-
-sub summary_output {
-	my ($obj, $num, $key);
-
-	foreach $key (sort $sort_method keys %map) {
-		$obj = $map{$key};
-		$num = @$obj;
-		printf "%s: %u\n", $key, $num;
-		$n_output += $num;
-	}
-}
-
-sub shortlog_output {
-	my ($obj, $num, $key, $desc);
-
-	foreach $key (sort $sort_method keys %map) {
-		$obj = $map{$key};
-		$num = @$obj;
-
-		# output author
-		printf "%s (%u):\n", $key, $num;
-
-		# output author's 1-line summaries
-		foreach $desc (reverse @$obj) {
-			print "  $desc\n";
-			$n_output++;
-		}
-
-		# blank line separating author from next author
-		print "\n";
-	}
-}
-
-sub changelog_input {
-	my ($author, $desc);
-
-	while (<>) {
-		# get author and email
-		if ($pstate == 1) {
-			my ($email);
-
-			next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/;
-
-			$n_records++;
-
-			$author = $1;
-			$email = $2;
-			$desc = undef;
-
-			# cset author fixups
-			if (exists $mailmap{$email}) {
-				$author = $mailmap{$email};
-			} elsif (exists $mailmap{$author}) {
-				$author = $mailmap{$author};
-			} elsif (!$author) {
-				$author = $email;
-			}
-			$email{$author}{$email}++;
-			$pstate++;
-		}
-
-		# skip to blank line
-		elsif ($pstate == 2) {
-			next unless /^\s*$/;
-			$pstate++;
-		}
-
-		# skip to non-blank line
-		elsif ($pstate == 3) {
-			next unless /^\s*?(.*)/;
-
-			# skip lines that are obviously not
-			# a 1-line cset description
-			next if /^\s*From: /;
-
-			chomp;
-			$desc = $1;
-
-			&shortlog_entry($author, $desc);
-
-			$pstate = 1;
-		}
-	
-		else {
-			die "invalid parse state $pstate";
-		}
-	}
-}
-
-sub read_mailmap {
-	my ($fh, $mailmap) = @_;
-	while (<$fh>) {
-		chomp;
-		if (/^([^#].*?)\s*<(.*)>/) {
-			$mailmap->{$2} = $1;
-		}
-	}
-}
-
-sub setup_mailmap {
-	read_mailmap(\*DATA, \%mailmap);
-	if (-f '.mailmap') {
-		my $fh = undef;
-		open $fh, '<', '.mailmap';
-		read_mailmap($fh, \%mailmap);
-		close $fh;
-	}
-}
-
-sub finalize {
-	#print "\n$n_records records parsed.\n";
-
-	if ($n_records != $n_output) {
-		die "parse error: input records != output records\n";
-	}
-	if (0) {
-		for my $author (sort keys %email) {
-			my $e = $email{$author};
-			for my $email (sort keys %$e) {
-				print STDERR "$author <$email>\n";
-			}
-		}
-	}
-}
-
-&setup_mailmap;
-&changelog_input;
-$opt_s ? &summary_output : &shortlog_output;
-&finalize;
-exit(0);
-
-
-__DATA__
-#
-# Even with git, we don't always have name translations.
-# So have an email->real name table to translate the
-# (hopefully few) missing names
-#
-Adrian Bunk <bunk@xxxxxxxxx>
-Andreas Herrmann <aherrman@xxxxxxxxxx>
-Andrew Morton <akpm@xxxxxxxx>
-Andrew Vasquez <andrew.vasquez@xxxxxxxxxx>
-Christoph Hellwig <hch@xxxxxx>
-Corey Minyard <minyard@xxxxxxx>
-David Woodhouse <dwmw2@xxxxxxxxxxxxxxxxxxxxxxx>
-Domen Puncer <domen@xxxxxxxxxxxx>
-Douglas Gilbert <dougg@xxxxxxxxxx>
-Ed L Cashin <ecashin@xxxxxxxxxx>
-Evgeniy Polyakov <johnpol@xxxxxxxxxxx>
-Felix Moeller <felix@xxxxxxxxxxxx>
-Frank Zago <fzago@xxxxxxxxxxxxxxxxxxxxx>
-Greg Kroah-Hartman <gregkh@xxxxxxx>
-James Bottomley <jejb@mulgrave.(none)>
-James Bottomley <jejb@xxxxxxxxxxxxxxxxxxxxxxx>
-Jeff Garzik <jgarzik@xxxxxxxxxxxxxx>
-Jens Axboe <axboe@xxxxxxx>
-Kay Sievers <kay.sievers@xxxxxxxx>
-Mitesh shah <mshah@xxxxxxxx>
-Morten Welinder <terra@xxxxxxxxx>
-Morten Welinder <welinder@xxxxxxxxxxxxxxxxxx>
-Morten Welinder <welinder@xxxxxxxxxxxxxxxxx>
-Morten Welinder <welinder@xxxxxxxxx>
-Nguyen Anh Quynh <aquynh@xxxxxxxxx>
-Paolo 'Blaisorblade' Giarrusso <blaisorblade@xxxxxxxx>
-Peter A Jonsson <pj@xxxxxxxxxxx>
-Ralf Wildenhues <Ralf.Wildenhues@xxxxxx>
-Rudolf Marek <R.Marek@xxxxxxxxxx>
-Rui Saraiva <rmps@xxxxxxxxxxxxxxx>
-Sachin P Sant <ssant@xxxxxxxxxx>
-Santtu Hyrkk,Av(B <santtu.hyrkko@xxxxxxxxx>
-Simon Kelley <simon@xxxxxxxxxxxxxxxxx>
-Tejun Heo <htejun@xxxxxxxxx>
-Tony Luck <tony.luck@xxxxxxxxx>
diff --git a/git.c b/git.c
index 8044667..771e8ee 100644
--- a/git.c
+++ b/git.c
@@ -262,6 +262,7 @@ static void handle_internal_command(int 
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "rm", cmd_rm, RUN_SETUP },
 		{ "runstatus", cmd_runstatus, RUN_SETUP },
+		{ "shortlog", cmd_shortlog, RUN_SETUP },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
 		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
 		{ "stripspace", cmd_stripspace },
diff --git a/path-list.c b/path-list.c
index 0c332dc..f8800f8 100644
--- a/path-list.c
+++ b/path-list.c
@@ -57,7 +57,7 @@ struct path_list_item *path_list_insert(
 	int index = add_entry(list, path);
 
 	if (index < 0)
-		index = 1 - index;
+		index = -1 - index;
 
 	return list->items + index;
 }
-- 
1.4.3.1.ge8ca-dirty

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]