Subject: [PATCH] Make rev-list --objects work together with pathspecs

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Fri, 17 Dec 2010 20:26:47 +0700

From: Elijah Newren <[email-masked]>

When traversing commits, the selection of commits would heed the list of
pathspecs passed, but subsequent walking of the trees of those commits
would not.  This resulted in 'rev-list --objects HEAD -- <paths>'
displaying objects at unwanted paths.

Have process_tree() call tree_entry_interesting() to determine which paths
are interesting and should be walked.

Naturally, this change can provide a large speedup when paths are specified
together with --objects, since many tree entries are now correctly ignored.
Interestingly, though, this change also gives me a small (~1%) but
repeatable speedup even when no paths are specified with --objects.

Signed-off-by: Elijah Newren <[email-masked]>
Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
Signed-off-by: Junio C Hamano <[email-masked]>
---
 Replacement for 1/2 of en/object-list-with-pathspec, on top of
 nd/struct-pathspec v2 and followup patches.

 Note that I dropped path_name() changes.

 list-objects.c |   30 ++++++++++++++++++++++++++++--
 1 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/list-objects.c b/list-objects.c
index 8953548..61f6cc9 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -61,12 +61,15 @@ static void process_tree(struct rev_info *revs,
 			 struct tree *tree,
 			 show_object_fn show,
 			 struct name_path *path,
+			 struct strbuf *base,
 			 const char *name)
 {
 	struct object *obj = &tree->object;
 	struct tree_desc desc;
 	struct name_entry entry;
 	struct name_path me;
+	int all_interesting = (revs->diffopt.pathspec.nr == 0);
+	int baselen = base->len;
 
 	if (!revs->tree_objects)
 		return;
@@ -82,13 +85,32 @@ static void process_tree(struct rev_info *revs,
 	me.elem = name;
 	me.elem_len = strlen(name);
 
+	if (!all_interesting) {
+		strbuf_addstr(base, name);
+		if (base->len)
+			strbuf_addch(base, '/');
+	}
+
 	init_tree_desc(&desc, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
+		if (!all_interesting) {
+			int showit = tree_entry_interesting(&entry,
+							    base, 0,
+							    &revs->diffopt.pathspec);
+
+			if (showit < 0)
+				break;
+			else if (!showit)
+				continue;
+			else if (showit == 2)
+				all_interesting = 1;
+		}
+
 		if (S_ISDIR(entry.mode))
 			process_tree(revs,
 				     lookup_tree(entry.sha1),
-				     show, &me, entry.path);
+				     show, &me, base, entry.path);
 		else if (S_ISGITLINK(entry.mode))
 			process_gitlink(revs, entry.sha1,
 					show, &me, entry.path);
@@ -97,6 +119,7 @@ static void process_tree(struct rev_info *revs,
 				     lookup_blob(entry.sha1),
 				     show, &me, entry.path);
 	}
+	strbuf_setlen(base, baselen);
 	free(tree->buffer);
 	tree->buffer = NULL;
 }
@@ -146,7 +169,9 @@ void traverse_commit_list(struct rev_info *revs,
 {
 	int i;
 	struct commit *commit;
+	struct strbuf base;
 
+	strbuf_init(&base, PATH_MAX);
 	while ((commit = get_revision(revs)) != NULL) {
 		add_pending_tree(revs, commit->tree);
 		show_commit(commit, data);
@@ -164,7 +189,7 @@ void traverse_commit_list(struct rev_info *revs,
 		}
 		if (obj->type == OBJ_TREE) {
 			process_tree(revs, (struct tree *)obj, show_object,
-				     NULL, name);
+				     NULL, &base, name);
 			continue;
 		}
 		if (obj->type == OBJ_BLOB) {
@@ -181,4 +206,5 @@ void traverse_commit_list(struct rev_info *revs,
 		revs->pending.alloc = 0;
 		revs->pending.objects = NULL;
 	}
+	strbuf_release(&base);
 }
-- 
1.7.3.3.476.g10a82