summaryrefslogtreecommitdiff
path: root/tree-walk.c
diff options
context:
space:
mode:
Diffstat (limited to 'tree-walk.c')
-rw-r--r--tree-walk.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/tree-walk.c b/tree-walk.c
index a9bbf4e235..522bb6b8f6 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -455,3 +455,117 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
free(tree);
return retval;
}
+
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Pre-condition: baselen == 0 || base[baselen-1] == '/'
+ *
+ * Return:
+ * - 2 for "yes, and all subsequent entries will be"
+ * - 1 for yes
+ * - zero for no
+ * - negative for "no, and no subsequent entries will be either"
+ */
+int tree_entry_interesting(const struct name_entry *entry,
+ const char *base, int baselen,
+ const struct pathspec *ps)
+{
+ int i;
+ int pathlen;
+ int never_interesting = -1;
+
+ if (!ps || !ps->nr)
+ return 2;
+
+ pathlen = tree_entry_len(entry->path, entry->sha1);
+
+ for (i = 0; i < ps->nr; i++) {
+ const struct pathspec_item *item = ps->items+i;
+ const char *match = item->match;
+ int matchlen = item->len;
+ int m = -1; /* signals that we haven't called strncmp() */
+
+ if (baselen >= matchlen) {
+ /* If it doesn't match, move along... */
+ if (strncmp(base, match, matchlen))
+ continue;
+
+ /*
+ * If the base is a subdirectory of a path which
+ * was specified, all of them are interesting.
+ */
+ if (!matchlen ||
+ base[matchlen] == '/' ||
+ match[matchlen - 1] == '/')
+ return 2;
+
+ /* Just a random prefix match */
+ continue;
+ }
+
+ /* Does the base match? */
+ if (strncmp(base, match, baselen))
+ continue;
+
+ match += baselen;
+ matchlen -= baselen;
+
+ if (never_interesting) {
+ /*
+ * We have not seen any match that sorts later
+ * than the current path.
+ */
+
+ /*
+ * Does match sort strictly earlier than path
+ * with their common parts?
+ */
+ m = strncmp(match, entry->path,
+ (matchlen < pathlen) ? matchlen : pathlen);
+ if (m < 0)
+ continue;
+
+ /*
+ * If we come here even once, that means there is at
+ * least one pathspec that would sort equal to or
+ * later than the path we are currently looking at.
+ * In other words, if we have never reached this point
+ * after iterating all pathspecs, it means all
+ * pathspecs are either outside of base, or inside the
+ * base but sorts strictly earlier than the current
+ * one. In either case, they will never match the
+ * subsequent entries. In such a case, we initialized
+ * the variable to -1 and that is what will be
+ * returned, allowing the caller to terminate early.
+ */
+ never_interesting = 0;
+ }
+
+ if (pathlen > matchlen)
+ continue;
+
+ if (matchlen > pathlen) {
+ if (match[pathlen] != '/')
+ continue;
+ if (!S_ISDIR(entry->mode))
+ continue;
+ }
+
+ if (m == -1)
+ /*
+ * we cheated and did not do strncmp(), so we do
+ * that here.
+ */
+ m = strncmp(match, entry->path, pathlen);
+
+ /*
+ * If common part matched earlier then it is a hit,
+ * because we rejected the case where path is not a
+ * leading directory and is shorter than match.
+ */
+ if (!m)
+ return 1;
+ }
+ return never_interesting; /* No matches */
+}