Re: [PATCH] git-pickaxe: blame rewritten.

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

 



Linus Torvalds <torvalds@xxxxxxxx> writes:

> On Thu, 12 Oct 2006, Junio C Hamano wrote:
>> +
>> +SYNOPSIS
>> +--------
>> +'git-pickaxe' [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [--] <file> [<rev>]
>
> Btw, could we please get rid of this horrible command line syntax.
>
> Pretty much _every_ other git command takes the form
>
> 	git cmd [<rev>] [--] <path>
>
> but for some reason annotate and blame (and now pickaxe) do it the wrong 
> way around, and do
>
> 	git cmd [--] <path> [<rev>]
>
> which is just irritating to somebody who has grown very used to being able 
> to specify revisions first.
>
> (I'd actually also like to have a range-modifier, so that I could do
>
> 	git annotate --since=2.weeks.ago v2.6.18.. <path>
>
> that didn't go back beyond a certain point, so the command line syntax 
> actually _does_ matter - even if we don't support that now, having the 
> regular command line syntax means that we -could- support it some day).
>
> 		Linus

So here it is.  I did not think "-n 20" would make sense for
this application so the code does not do anything about it.

This will be parked on "pu" for now.

-- >8 --
[PATCH] git-pickaxe: minimally use revision machinery.

This rewrites pickaxe argument parser to use revision machinery,
so that you can say something like:

	git pickaxe --since=2.weeks master commit.c
	git pickaxe -L 20,40 v1.4.0..master -- commit.c

The traditional parameter order (path first then optional rev)
is supported but cannot be mixed with revision limiting
parameters.

Signed-off-by: Junio C Hamano <junkio@xxxxxxx>
---
 Documentation/git-pickaxe.txt |    2 +-
 builtin-pickaxe.c             |  221 ++++++++++++++++++++++++++++++++---------
 2 files changed, 173 insertions(+), 50 deletions(-)

diff --git a/Documentation/git-pickaxe.txt b/Documentation/git-pickaxe.txt
index 7f30cdf..7685bd0 100644
--- a/Documentation/git-pickaxe.txt
+++ b/Documentation/git-pickaxe.txt
@@ -7,7 +7,7 @@ git-pickaxe - Show what revision and aut
 
 SYNOPSIS
 --------
-'git-pickaxe' [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [--] <file> [<rev>]
+'git-pickaxe' [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [<rev>] [--] <file>
 
 DESCRIPTION
 -----------
diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c
index 0e26308..0aa4565 100644
--- a/builtin-pickaxe.c
+++ b/builtin-pickaxe.c
@@ -11,13 +11,14 @@ #include "commit.h"
 #include "tree-walk.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "revision.h"
 #include "xdiff-interface.h"
 
 #include <time.h>
 #include <sys/time.h>
 
 static char pickaxe_usage[] =
-"git-pickaxe [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [--] file [commit]\n"
+"git-pickaxe [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [commit] [--] file\n"
 "  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
 "  -l, --long          Show long commit SHA1 (Default: off)\n"
 "  -t, --time          Show raw timestamp (Default: off)\n"
@@ -109,7 +110,8 @@ static void coalesce(struct scoreboard *
 	for (o = 0, i = 0; i < num; i++) {
 		if (o &&
 		    ent[o-1]->suspect == ent[i]->suspect &&
-		    ent[o-1]->s_lno + ent[o-1]->num_lines == ent[i]->s_lno) {
+		    ent[o-1]->s_lno + ent[o-1]->num_lines == ent[i]->s_lno &&
+		    ent[o-1]->guilty == ent[i]->guilty) {
 			ent[o-1]->num_lines += ent[i]->num_lines;
 			free(ent[i]);
 			continue;
@@ -220,7 +222,8 @@ static void process_u0_diff(void *state_
 	struct chunk *chunk;
 	int off1, off2, len1, len2;
 
-if(DEBUG) fprintf(stderr, "%.*s", (int) len, line);
+	if (DEBUG)
+		fprintf(stderr, "%.*s", (int) len, line);
 
 	if (len < 4 || line[0] != '@' || line[1] != '@')
 		return;
@@ -256,17 +259,23 @@ static struct patch *get_patch(struct or
 	mmfile_t file_p, file_o;
 	xdemitcb_t ecb;
 	char type[10];
-
-if(DEBUG) fprintf(stderr, "get patch %.8s %.8s\n",
-		sha1_to_hex(parent->commit->object.sha1),
-		sha1_to_hex(origin->commit->object.sha1));
-
-	file_p.ptr = read_sha1_file(parent->blob_sha1, type,
-				    (unsigned long *) &file_p.size);
-	file_o.ptr = read_sha1_file(origin->blob_sha1, type,
-				    (unsigned long *) &file_o.size);
-	if (!file_p.ptr || !file_o.ptr)
+	char *blob_p, *blob_o;
+
+	if (DEBUG) fprintf(stderr, "get patch %.8s %.8s\n",
+			   sha1_to_hex(parent->commit->object.sha1),
+			   sha1_to_hex(origin->commit->object.sha1));
+
+	blob_p = read_sha1_file(parent->blob_sha1, type,
+				(unsigned long *) &file_p.size);
+	blob_o = read_sha1_file(origin->blob_sha1, type,
+				(unsigned long *) &file_o.size);
+	file_p.ptr = blob_p;
+	file_o.ptr = blob_o;
+	if (!file_p.ptr || !file_o.ptr) {
+		free(blob_p);
+		free(blob_o);
 		return NULL;
+	}
 
 	xpp.flags = XDF_NEED_MINIMAL;
 	xecfg.ctxlen = 0;
@@ -280,6 +289,8 @@ if(DEBUG) fprintf(stderr, "get patch %.8
 	state.ret->num = 0;
 
 	xdl_diff(&file_p, &file_o, &xpp, &xecfg, &ecb);
+	free(blob_p);
+	free(blob_o);
 	return state.ret;
 }
 
@@ -417,11 +428,12 @@ static int pass_blame_to_parent(struct s
 	for (i = 0; i < patch->num; i++) {
 		struct chunk *chunk = &patch->chunks[i];
 
-if(DEBUG)	fprintf(stderr,
-			"plno = %d, tlno = %d, "
-			"same as parent up to %d, resync %d and %d\n",
-			plno, tlno,
-			chunk->same, chunk->p_next, chunk->t_next);
+		if (DEBUG)
+			fprintf(stderr,
+				"plno = %d, tlno = %d, "
+				"same as parent up to %d, resync %d and %d\n",
+				plno, tlno,
+				chunk->same, chunk->p_next, chunk->t_next);
 		blame_chunk(sb, tlno, plno, chunk->same, target, parent);
 		plno = chunk->p_next;
 		tlno = chunk->t_next;
@@ -434,7 +446,8 @@ if(DEBUG)	fprintf(stderr,
 }
 
 #define MAXPARENT 16
-static void pass_blame(struct scoreboard *sb, struct origin *origin)
+static void pass_blame(struct scoreboard *sb, struct origin *origin,
+		       struct rev_info *revs)
 {
 	int i, parent_ix;
 	struct commit *commit = origin->commit;
@@ -448,8 +461,15 @@ static void pass_blame(struct scoreboard
 	for (parent_ix = 0, parent = commit->parents;
 	     parent_ix < MAXPARENT && parent;
 	     parent = parent->next, parent_ix++) {
-		if (parse_commit(parent->item))
+		struct commit *p = parent->item;
+
+		if (parse_commit(p))
+			continue;
+		if (p->object.flags & UNINTERESTING)
 			continue;
+		if (revs->max_age != -1 && p->date  < revs->max_age)
+			continue;
+
 		porigin = find_origin(sb, parent->item, origin->path);
 		if (!porigin)
 			porigin = find_rename(sb, parent->item, origin);
@@ -485,14 +505,9 @@ static void pass_blame(struct scoreboard
 	 * Optionally run "ciff" to find copies from parents' files here
 	 */
 
-	/* Take responsibility for the remaining entries */
-	for (i = 0; i < sb->num_entries; i++)
-		if (!sb->entries[i]->guilty &&
-		    sb->entries[i]->suspect == origin)
-			sb->entries[i]->guilty = 1;
 }
 
-static void assign_blame(struct scoreboard *sb)
+static void assign_blame(struct scoreboard *sb, struct rev_info *revs)
 {
 	while (1) {
 		int i;
@@ -506,7 +521,13 @@ static void assign_blame(struct scoreboa
 		if (!suspect)
 			return; /* all done */
 
-		pass_blame(sb, suspect);
+		pass_blame(sb, suspect, revs);
+
+		/* Take responsibility for the remaining entries */
+		for (i = 0; i < sb->num_entries; i++)
+			if (!sb->entries[i]->guilty &&
+			    sb->entries[i]->suspect == suspect)
+				sb->entries[i]->guilty = 1;
 	}
 }
 
@@ -829,23 +850,29 @@ static void find_alignment(struct scoreb
 	max_digits = lineno_width(longest_dst_lines);
 }
 
+static int has_path_in_work_tree(const char *path)
+{
+	struct stat st;
+	return !lstat(path, &st);
+}
+
 int cmd_pickaxe(int argc, const char **argv, const char *prefix)
 {
-	const char *path = argv[1];
-	unsigned char sha1[20];
+	struct rev_info revs;
+	const char *path;
 	struct scoreboard sb;
 	struct origin *o;
 	struct blame_entry *ent;
-	int i, seen_dashdash;
+	int i, seen_dashdash, unk;
 	long bottom, top, lno;
 	int output_option = 0;
 	const char *revs_file = NULL;
-	const char *final_commit_name = "HEAD";
+	const char *final_commit_name = NULL;
 	char type[10];
 
 	bottom = top = 0;
 	seen_dashdash = 0;
-	for (i = 1; i < argc; i++) {
+	for (unk = i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		if (*arg != '-')
 			break;
@@ -887,28 +914,121 @@ int cmd_pickaxe(int argc, const char **a
 			break;
 		}
 		else
+			argv[unk++] = arg;
+	}
+
+	/* We have collected options unknown to us in argv[1..unk]
+	 * which are to be passed to revision machinery if we are
+	 * going to do the "bottom" procesing.
+	 *
+	 * The remaining are:
+	 *
+	 * (1) if seen_dashdash, its either
+	 *     "-options -- <path>" or
+	 *     "-options -- <path> <rev>".
+	 *     but the latter is allowed only if there is no
+	 *     options that we passed to revision machinery.
+	 *
+	 * (2) otherwise, we may have "--" somewhere later and
+	 *     might be looking at the first one of multiple 'rev'
+	 *     parameters (e.g. " master ^next ^maint -- path").
+	 *     See if there is a dashdash first, and give the
+	 *     arguments before that to revision machinery.
+	 *     After that there must be one 'path'.
+	 *
+	 * (3) otherwise, its one of the three:
+	 *     "-options <path> <rev>"
+	 *     "-options <rev> <path>"
+	 *     "-options <path>"
+	 *     but again the first one is allowed only if
+	 *     there is no options that we passed to revision
+	 *     machinery.
+	 */
+
+	if (seen_dashdash) {
+		/* (1) */
+		if (argc <= i)
+			usage(pickaxe_usage);
+		path = argv[i];
+		if (i + 1 == argc - 1) {
+			if (unk != 1)
+				usage(pickaxe_usage);
+			argv[unk++] = argv[i + 1];
+		}
+		else if (i + 1 != argc)
+			/* garbage at end */
 			usage(pickaxe_usage);
 	}
+	else {
+		int j;
+		for (j = i; !seen_dashdash && j < argc; j++)
+			if (!strcmp(argv[j], "--"))
+				seen_dashdash = j;
+		if (seen_dashdash) {
+			if (seen_dashdash + 1 != argc - 1)
+				usage(pickaxe_usage);
+			path = argv[seen_dashdash + 1];
+			for (j = i; j < seen_dashdash; j++)
+				argv[unk++] = argv[j];
+		}
+		else {
+			/* (3) */
+			path = argv[i];
+			if (i + 1 == argc - 1) {
+				final_commit_name = argv[i + 1];
+
+				/* if (unk == 1) we could be getting
+				 * old-style
+				 */
+				if (unk == 1 && !has_path_in_work_tree(path)) {
+					path = argv[i + 1];
+					final_commit_name = argv[i];
+				}
+			}
+			else if (i != argc - 1)
+				usage(pickaxe_usage); /* garbage at end */
 
-	/* argv[i] is filename, argv[i+1] if exists is the commit */
-	if (i >= argc)
-		usage(pickaxe_usage);
-	path = argv[i++];
-	if (!seen_dashdash) {
-		struct stat st;
-		if (lstat(path, &st))
-			die("cannot stat path %s: %s", path, strerror(errno));
+			if (!has_path_in_work_tree(path))
+				die("cannot stat path %s: %s",
+				    path, strerror(errno));
+		}
 	}
 
-	if (i == argc - 1)
-		final_commit_name = argv[i];
-	else if (i != argc)
-		usage(pickaxe_usage);
+	if (final_commit_name)
+		argv[unk++] = final_commit_name;
 
+	/* Now we got rev and path.  We do not want the path pruning
+	 * but we may want "bottom" processing.
+	 */
+	argv[unk] = NULL;
+
+	init_revisions(&revs, NULL);
+	setup_revisions(unk, argv, &revs, "HEAD");
 	memset(&sb, 0, sizeof(sb));
-	if (get_sha1(final_commit_name, sha1) ||
-	    !(sb.final = lookup_commit_reference(sha1)))
-		die("no such commit %s", final_commit_name);
+
+	/* There must be one and only one positive commit in the
+	 * revs->pending array.
+	 */
+	for (i = 0; i < revs.pending.nr; i++) {
+		struct object *obj = revs.pending.objects[i].item;
+		if (obj->flags & UNINTERESTING)
+			continue;
+		if (obj->type != OBJ_COMMIT)
+			die("Non commit %s?",
+			    revs.pending.objects[i].name);
+		if (sb.final)
+			die("More than one commit to dig from %s and %s?",
+			    revs.pending.objects[i].name,
+			    final_commit_name);
+		sb.final = (struct commit *) obj;
+		final_commit_name = revs.pending.objects[i].name;
+	}
+	/* If we have bottom, this will mark the ancestors of the
+	 * bottom commits we would reach while traversing as
+	 * uninteresting.
+	 */
+	prepare_revision_walk(&revs);
+
 	o = find_origin(&sb, sb.final, path);
 	if (!o)
 		die("no such path %s in %s", path, final_commit_name);
@@ -939,7 +1059,7 @@ int cmd_pickaxe(int argc, const char **a
 		die("reading graft file %s failed: %s",
 		    revs_file, strerror(errno));
 
-	assign_blame(&sb);
+	assign_blame(&sb, &revs);
 
 	coalesce(&sb);
 
@@ -947,6 +1067,9 @@ int cmd_pickaxe(int argc, const char **a
 		find_alignment(&sb, &output_option);
 
 	output(&sb, output_option);
-
+	free((void *)sb.final_buf);
+	for (i = 0; i < sb.num_entries; i++)
+		free(sb.entries[i]);
+	free(sb.entries);
 	return 0;
 }
-- 
1.4.3.rc2.g51ca




-
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]