In a similar way to the previous commit, this extracts new helper
functions excluded_1() and path_excluded_1() which return the last
exclude_list element which matched, or NULL if no match was found.
excluded() and path_excluded() become wrappers around these, and just
return 0 or 1 depending on whether any matching exclude_list element
was found.

This allows callers to find out _why_ a given path was excluded,
rather than just whether it was or not, paving the way for a new git
sub-command which allows users to test their exclude lists from the
command line.

Signed-off-by: Adam Spiers <g...@adamspiers.org>
---
 dir.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
 dir.h |  3 +++
 2 files changed, 67 insertions(+), 17 deletions(-)

diff --git a/dir.c b/dir.c
index 3a532d5..3d438c3 100644
--- a/dir.c
+++ b/dir.c
@@ -603,23 +603,42 @@ int excluded_from_list(const char *pathname,
        return -1; /* undecided */
 }
 
-static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
+/*
+ * Loads the exclude lists for the directory containing pathname, then
+ * scans all exclude lists to determine whether pathname is excluded.
+ * Returns the exclude_list element which matched, or NULL for
+ * undecided.
+ */
+static struct exclude *excluded_1(struct dir_struct *dir, const char 
*pathname, int *dtype_p)
 {
        int pathlen = strlen(pathname);
        int st;
+       struct exclude *exclude;
        const char *basename = strrchr(pathname, '/');
        basename = (basename) ? basename+1 : pathname;
 
        prep_exclude(dir, pathname, basename-pathname);
        for (st = EXC_CMDL; st <= EXC_FILE; st++) {
-               switch (excluded_from_list(pathname, pathlen, basename,
-                                          dtype_p, &dir->exclude_list[st])) {
-               case 0:
-                       return 0;
-               case 1:
-                       return 1;
+               exclude = excluded_from_list_1(pathname, pathlen, basename,
+                                              dtype_p, &dir->exclude_list[st]);
+               if (exclude) {
+                       return exclude;
                }
        }
+       return NULL;
+}
+
+/*
+ * Loads the exclude lists for the directory containing pathname, then
+ * scans all exclude lists to determine whether pathname is excluded.
+ * Returns 1 if true, otherwise 0.
+ */
+static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
+{
+       struct exclude *exclude = excluded_1(dir, pathname, dtype_p);
+       if (exclude) {
+               return exclude->to_exclude;
+       }
        return 0;
 }
 
@@ -627,6 +646,7 @@ void path_exclude_check_init(struct path_exclude_check 
*check,
                             struct dir_struct *dir)
 {
        check->dir = dir;
+       check->exclude = NULL;
        strbuf_init(&check->path, 256);
 }
 
@@ -636,18 +656,20 @@ void path_exclude_check_clear(struct path_exclude_check 
*check)
 }
 
 /*
- * Is this name excluded?  This is for a caller like show_files() that
- * do not honor directory hierarchy and iterate through paths that are
- * possibly in an ignored directory.
+ * For each subdirectory in name, starting with the top-most, checks
+ * to see if that subdirectory is excluded, and if so, returns the
+ * corresponding exclude structure.  Otherwise, checks whether name
+ * itself (which is presumably a file) is excluded.
  *
  * A path to a directory known to be excluded is left in check->path to
  * optimize for repeated checks for files in the same excluded directory.
  */
-int path_excluded(struct path_exclude_check *check,
-                 const char *name, int namelen, int *dtype)
+struct exclude *path_excluded_1(struct path_exclude_check *check,
+                               const char *name, int namelen, int *dtype)
 {
        int i;
        struct strbuf *path = &check->path;
+       struct exclude *exclude;
 
        /*
         * we allow the caller to pass namelen as an optimization; it
@@ -657,11 +679,18 @@ int path_excluded(struct path_exclude_check *check,
        if (namelen < 0)
                namelen = strlen(name);
 
+       /*
+        * If path is non-empty, and name is equal to path or a
+        * subdirectory of path, name should be excluded, because
+        * it's inside a directory which is already known to be
+        * excluded and was previously left in check->path.
+        */
        if (path->len &&
            path->len <= namelen &&
            !memcmp(name, path->buf, path->len) &&
-           (!name[path->len] || name[path->len] == '/'))
-               return 1;
+           (!name[path->len] || name[path->len] == '/')) {
+               return check->exclude;
+       }
 
        strbuf_setlen(path, 0);
        for (i = 0; name[i]; i++) {
@@ -669,8 +698,11 @@ int path_excluded(struct path_exclude_check *check,
 
                if (ch == '/') {
                        int dt = DT_DIR;
-                       if (excluded(check->dir, path->buf, &dt))
-                               return 1;
+                       exclude = excluded_1(check->dir, path->buf, &dt);
+                       if (exclude) {
+                               check->exclude = exclude;
+                               return exclude;
+                       }
                }
                strbuf_addch(path, ch);
        }
@@ -678,7 +710,22 @@ int path_excluded(struct path_exclude_check *check,
        /* An entry in the index; cannot be a directory with subentries */
        strbuf_setlen(path, 0);
 
-       return excluded(check->dir, name, dtype);
+       return excluded_1(check->dir, name, dtype);
+}
+
+/*
+ * Is this name excluded?  This is for a caller like show_files() that
+ * do not honor directory hierarchy and iterate through paths that are
+ * possibly in an ignored directory.
+ */
+int path_excluded(struct path_exclude_check *check,
+                 const char *name, int namelen, int *dtype)
+{
+       struct exclude *exclude = path_excluded_1(check, name, namelen, dtype);
+       if (exclude) {
+               return exclude->to_exclude;
+       }
+       return 0;
 }
 
 static struct dir_entry *dir_entry_new(const char *pathname, int len)
diff --git a/dir.h b/dir.h
index 549a187..1b4f9dc 100644
--- a/dir.h
+++ b/dir.h
@@ -109,10 +109,13 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir, 
const char *pathname,
  */
 struct path_exclude_check {
        struct dir_struct *dir;
+       struct exclude *exclude;
        struct strbuf path;
 };
 extern void path_exclude_check_init(struct path_exclude_check *, struct 
dir_struct *);
 extern void path_exclude_check_clear(struct path_exclude_check *);
+extern struct exclude *path_excluded_1(struct path_exclude_check *, const char 
*,
+                                      int namelen, int *dtype);
 extern int path_excluded(struct path_exclude_check *, const char *, int 
namelen, int *dtype);
 
 
-- 
1.7.12.155.ge5750d5.dirty

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to