Subject: [PATCH 1/3] Reimplement read_tree_recursive() using tree_entry_interesting()

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Fri, 25 Mar 2011 16:34:18 +0700

read_tree_recursive() uses a very similar function, match_tree_entry, to
tree_entry_interesting() to do its path matching. This patch kills
match_tree_entry() in favor of tree_entry_interesting().

match_tree_entry(), like older version of tree_entry_interesting(), does
not support wildcard matching. New read_tree_recursive() retains this
behavior by forcing all pathspecs literal.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 2011/3/25 Junio C Hamano <[email-masked]>:
 > Nguyễn Thái Ngọc Duy  <[email-masked]> writes:
 >
 >> +static int read_tree_1(struct tree *tree, struct strbuf *base,
 >> +                    int stage, struct pathspec *pathspecs,
 >> +                    read_tree_fn_t fn, void *context)
 >
 > Micronit: call the variable pathspec, not pathspecs.

 Fixed (also in the second patch)

 > This "if all-interesting then avoid calling into an expensive matcher"
 > logic looks familiar but is not something that came from the parts deleted
 > by this patch.  Where did I see it? Is that something we can share in a
 > common helper function? Is such a sharing worth pursuing, considering that
 > the above is a reasonably trivial logic in a tight loop?
 >
 > "That's how the return value of tree-entry-interesting is designed to be
 > used, and it is unsurprising that all the callers will fall into that
 > pattern" is a perfectly acceptable answer, I guess.

 Actually the surrounding code could be improved so that helper function
 is not needed. I also update other t_e_i() callsites in the third patch.

 tree.c |  152 ++++++++++++++++++++++++----------------------------------------
 1 files changed, 57 insertions(+), 95 deletions(-)

diff --git a/tree.c b/tree.c
index 5ab90af..db3a5c3 100644
--- a/tree.c
+++ b/tree.c
@@ -45,62 +45,14 @@ static int read_one_entry_quick(const unsigned char *sha1, const char *base, int
 				  ADD_CACHE_JUST_APPEND);
 }
 
-static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths)
-{
-	const char *match;
-	int pathlen;
-
-	if (!paths)
-		return 1;
-	pathlen = strlen(path);
-	while ((match = *paths++) != NULL) {
-		int matchlen = strlen(match);
-
-		if (baselen >= matchlen) {
-			/* If it doesn't match, move along... */
-			if (strncmp(base, match, matchlen))
-				continue;
-			/* pathspecs match only at the directory boundaries */
-			if (!matchlen ||
-			    baselen == matchlen ||
-			    base[matchlen] == '/' ||
-			    match[matchlen - 1] == '/')
-				return 1;
-			continue;
-		}
-
-		/* Does the base match? */
-		if (strncmp(base, match, baselen))
-			continue;
-
-		match += baselen;
-		matchlen -= baselen;
-
-		if (pathlen > matchlen)
-			continue;
-
-		if (matchlen > pathlen) {
-			if (match[pathlen] != '/')
-				continue;
-			if (!S_ISDIR(mode))
-				continue;
-		}
-
-		if (strncmp(path, match, pathlen))
-			continue;
-
-		return 1;
-	}
-	return 0;
-}
-
-int read_tree_recursive(struct tree *tree,
-			const char *base, int baselen,
-			int stage, const char **match,
-			read_tree_fn_t fn, void *context)
+static int read_tree_1(struct tree *tree, struct strbuf *base,
+		       int stage, struct pathspec *pathspec,
+		       read_tree_fn_t fn, void *context)
 {
 	struct tree_desc desc;
 	struct name_entry entry;
+	unsigned char sha1[20];
+	int len, retval = 0, oldlen = base->len;
 
 	if (parse_tree(tree))
 		return -1;
@@ -108,10 +60,16 @@ int read_tree_recursive(struct tree *tree,
 	init_tree_desc(&desc, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
-		if (!match_tree_entry(base, baselen, entry.path, entry.mode, match))
-			continue;
+		if (retval != 2) {
+			retval = tree_entry_interesting(&entry, base, 0, pathspec);
+			if (retval < 0)
+				break;
+			if (retval == 0)
+				continue;
+		}
 
-		switch (fn(entry.sha1, base, baselen, entry.path, entry.mode, stage, context)) {
+		switch (fn(entry.sha1, base->buf, base->len,
+			   entry.path, entry.mode, stage, context)) {
 		case 0:
 			continue;
 		case READ_TREE_RECURSIVE:
@@ -119,56 +77,60 @@ int read_tree_recursive(struct tree *tree,
 		default:
 			return -1;
 		}
-		if (S_ISDIR(entry.mode)) {
-			int retval;
-			char *newbase;
-			unsigned int pathlen = tree_entry_len(entry.path, entry.sha1);
-
-			newbase = xmalloc(baselen + 1 + pathlen);
-			memcpy(newbase, base, baselen);
-			memcpy(newbase + baselen, entry.path, pathlen);
-			newbase[baselen + pathlen] = '/';
-			retval = read_tree_recursive(lookup_tree(entry.sha1),
-						     newbase,
-						     baselen + pathlen + 1,
-						     stage, match, fn, context);
-			free(newbase);
-			if (retval)
-				return -1;
-			continue;
-		} else if (S_ISGITLINK(entry.mode)) {
-			int retval;
-			struct strbuf path;
-			unsigned int entrylen;
-			struct commit *commit;
 
-			entrylen = tree_entry_len(entry.path, entry.sha1);
-			strbuf_init(&path, baselen + entrylen + 1);
-			strbuf_add(&path, base, baselen);
-			strbuf_add(&path, entry.path, entrylen);
-			strbuf_addch(&path, '/');
+		if (S_ISDIR(entry.mode))
+			hashcpy(sha1, entry.sha1);
+		else if (S_ISGITLINK(entry.mode)) {
+			struct commit *commit;
 
 			commit = lookup_commit(entry.sha1);
 			if (!commit)
-				die("Commit %s in submodule path %s not found",
-				    sha1_to_hex(entry.sha1), path.buf);
+				die("Commit %s in submodule path %s%s not found",
+				    sha1_to_hex(entry.sha1),
+				    base->buf, entry.path);
 
 			if (parse_commit(commit))
-				die("Invalid commit %s in submodule path %s",
-				    sha1_to_hex(entry.sha1), path.buf);
-
-			retval = read_tree_recursive(commit->tree,
-						     path.buf, path.len,
-						     stage, match, fn, context);
-			strbuf_release(&path);
-			if (retval)
-				return -1;
-			continue;
+				die("Invalid commit %s in submodule path %s%s",
+				    sha1_to_hex(entry.sha1),
+				    base->buf, entry.path);
+
+			hashcpy(sha1, commit->tree->object.sha1);
 		}
+		else
+			continue;
+
+		len = tree_entry_len(entry.path, entry.sha1);
+		strbuf_add(base, entry.path, len);
+		strbuf_addch(base, '/');
+		retval = read_tree_1(lookup_tree(sha1),
+				     base, stage, pathspec,
+				     fn, context);
+		strbuf_setlen(base, oldlen);
+		if (retval)
+			return -1;
 	}
 	return 0;
 }
 
+int read_tree_recursive(struct tree *tree,
+			const char *base, int baselen,
+			int stage, const char **match,
+			read_tree_fn_t fn, void *context)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct pathspec pathspec;
+	int i, ret;
+
+	init_pathspec(&pathspec, match);
+	for (i = 0; i < pathspec.nr; i++)
+		pathspec.items[i].has_wildcard = 0;
+	strbuf_add(&sb, base, baselen);
+	ret = read_tree_1(tree, &sb, stage, &pathspec, fn, context);
+	strbuf_release(&sb);
+	free_pathspec(&pathspec);
+	return ret;
+}
+
 static int cmp_cache_name_compare(const void *a_, const void *b_)
 {
 	const struct cache_entry *ce1, *ce2;
-- 
1.7.4.74.g639db