Subject: [PATCH 1/5] tree-walk: support negative pathspec

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Thu, 10 Mar 2011 10:13:35 +0700

While the UI will be simple, where negative pathspecs are always
appended in the end, this could should be able to handle a mixed set
of negative and positive pathspecs (ie. overlapping must be handled
correctly).

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 cache.h     |    1 +
 tree-walk.c |   37 ++++++++++++++++++++++++++++---------
 2 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/cache.h b/cache.h
index 08a9022..2189366 100644
--- a/cache.h
+++ b/cache.h
@@ -510,6 +510,7 @@ struct pathspec {
 		const char *match;
 		int len;
 		int has_wildcard:1;
+		int to_exclude:1;
 	} *items;
 };
 
diff --git a/tree-walk.c b/tree-walk.c
index 322becc..acbee1b 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -560,6 +560,7 @@ int tree_entry_interesting(const struct name_entry *entry,
 	int i;
 	int pathlen, baselen = base->len - base_offset;
 	int never_interesting = ps->has_wildcard ? 0 : -1;
+	int all_interesting_ok = !ps->recursive || ps->max_depth == -1;
 
 	if (!ps->nr) {
 		if (!ps->recursive || ps->max_depth == -1)
@@ -569,6 +570,19 @@ int tree_entry_interesting(const struct name_entry *entry,
 				      ps->max_depth);
 	}
 
+	/*
+	 * TODO: add prepare_pathspec() to scan for pathspecs
+	 * overlapped with negative ones. The ones that do not overlap
+	 * can still return 2.
+	 */
+	for (i = 0; i < ps->nr; i++) {
+		const struct pathspec_item *item = ps->items+i;
+		if (item->to_exclude) {
+			all_interesting_ok = 0;
+			break;
+		}
+	}
+
 	pathlen = tree_entry_len(entry->path, entry->sha1);
 
 	for (i = ps->nr - 1; i >= 0; i--) {
@@ -578,17 +592,22 @@ int tree_entry_interesting(const struct name_entry *entry,
 		int matchlen = item->len;
 
 		if (baselen >= matchlen) {
+			int ret;
+
 			/* If it doesn't match, move along... */
 			if (!match_dir_prefix(base_str, baselen, match, matchlen))
 				goto match_wildcards;
 
-			if (!ps->recursive || ps->max_depth == -1)
+			if (all_interesting_ok)
 				return 2;
 
-			return !!within_depth(base_str + matchlen + 1,
-					      baselen - matchlen - 1,
-					      !!S_ISDIR(entry->mode),
-					      ps->max_depth);
+			ret = !!within_depth(base_str + matchlen + 1,
+					     baselen - matchlen - 1,
+					     !!S_ISDIR(entry->mode),
+					     ps->max_depth);
+			if (item->to_exclude)
+				ret = !ret;
+			return ret;
 		}
 
 		/* Does the base match? */
@@ -596,18 +615,18 @@ int tree_entry_interesting(const struct name_entry *entry,
 			if (match_entry(entry, pathlen,
 					match + baselen, matchlen - baselen,
 					&never_interesting))
-				return 1;
+				return item->to_exclude ? 0 : 1;
 
 			if (ps->items[i].has_wildcard) {
 				if (!fnmatch(match + baselen, entry->path, 0))
-					return 1;
+					return item->to_exclude ? 0 : 1;
 
 				/*
 				 * Match all directories. We'll try to
 				 * match files later on.
 				 */
 				if (ps->recursive && S_ISDIR(entry->mode))
-					return 1;
+					return item->to_exclude ? 0 : 1;
 			}
 
 			continue;
@@ -626,7 +645,7 @@ match_wildcards:
 
 		if (!fnmatch(match, base->buf + base_offset, 0)) {
 			strbuf_setlen(base, base_offset + baselen);
-			return 1;
+			return item->to_exclude ? 0 : 1;
 		}
 		strbuf_setlen(base, base_offset + baselen);
 
-- 
1.7.3.1.256.g2539c.dirty