]> git.ipfire.org Git - thirdparty/git.git/blobdiff - dir.c
Merge branch 'pb/ref-filter-with-crlf'
[thirdparty/git.git] / dir.c
diff --git a/dir.c b/dir.c
index bcd04b7cbfd33c0468f4eb5062662e812e849e2d..ebea5f1f91439a097e48ffb5983fbff2bc0ff918 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -54,6 +54,11 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 static int resolve_dtype(int dtype, struct index_state *istate,
                         const char *path, int len);
 
+void dir_init(struct dir_struct *dir)
+{
+       memset(dir, 0, sizeof(*dir));
+}
+
 int count_slashes(const char *s)
 {
        int cnt = 0;
@@ -193,6 +198,10 @@ int fill_directory(struct dir_struct *dir,
        const char *prefix;
        size_t prefix_len;
 
+       unsigned exclusive_flags = DIR_SHOW_IGNORED | DIR_SHOW_IGNORED_TOO;
+       if ((dir->flags & exclusive_flags) == exclusive_flags)
+               BUG("DIR_SHOW_IGNORED and DIR_SHOW_IGNORED_TOO are exclusive");
+
        /*
         * Calculate common prefix for the pathspec, and
         * use that to optimize the directory walk
@@ -364,7 +373,8 @@ static int match_pathspec_item(const struct index_state *istate,
                return MATCHED_FNMATCH;
 
        /* Perform checks to see if "name" is a leading string of the pathspec */
-       if (flags & DO_MATCH_LEADING_PATHSPEC) {
+       if ( (flags & DO_MATCH_LEADING_PATHSPEC) &&
+           !(flags & DO_MATCH_EXCLUDE)) {
                /* name is a literal prefix of the pathspec */
                int offset = name[namelen-1] == '/' ? 1 : 0;
                if ((namelen < matchlen) &&
@@ -401,6 +411,10 @@ static int match_pathspec_item(const struct index_state *istate,
 }
 
 /*
+ * do_match_pathspec() is meant to ONLY be called by
+ * match_pathspec_with_flags(); calling it directly risks pathspecs
+ * like ':!unwanted_path' being ignored.
+ *
  * Given a name and a list of pathspecs, returns the nature of the
  * closest (i.e. most specific) match of the name to any of the
  * pathspecs.
@@ -486,13 +500,12 @@ static int do_match_pathspec(const struct index_state *istate,
        return retval;
 }
 
-int match_pathspec(const struct index_state *istate,
-                  const struct pathspec *ps,
-                  const char *name, int namelen,
-                  int prefix, char *seen, int is_dir)
+static int match_pathspec_with_flags(const struct index_state *istate,
+                                    const struct pathspec *ps,
+                                    const char *name, int namelen,
+                                    int prefix, char *seen, unsigned flags)
 {
        int positive, negative;
-       unsigned flags = is_dir ? DO_MATCH_DIRECTORY : 0;
        positive = do_match_pathspec(istate, ps, name, namelen,
                                     prefix, seen, flags);
        if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
@@ -503,6 +516,16 @@ int match_pathspec(const struct index_state *istate,
        return negative ? 0 : positive;
 }
 
+int match_pathspec(const struct index_state *istate,
+                  const struct pathspec *ps,
+                  const char *name, int namelen,
+                  int prefix, char *seen, int is_dir)
+{
+       unsigned flags = is_dir ? DO_MATCH_DIRECTORY : 0;
+       return match_pathspec_with_flags(istate, ps, name, namelen,
+                                        prefix, seen, flags);
+}
+
 /**
  * Check if a submodule is a superset of the pathspec
  */
@@ -511,11 +534,11 @@ int submodule_path_match(const struct index_state *istate,
                         const char *submodule_name,
                         char *seen)
 {
-       int matched = do_match_pathspec(istate, ps, submodule_name,
-                                       strlen(submodule_name),
-                                       0, seen,
-                                       DO_MATCH_DIRECTORY |
-                                       DO_MATCH_LEADING_PATHSPEC);
+       int matched = match_pathspec_with_flags(istate, ps, submodule_name,
+                                               strlen(submodule_name),
+                                               0, seen,
+                                               DO_MATCH_DIRECTORY |
+                                               DO_MATCH_LEADING_PATHSPEC);
        return matched;
 }
 
@@ -636,11 +659,42 @@ int pl_hashmap_cmp(const void *unused_cmp_data,
        return strncmp(ee1->pattern, ee2->pattern, min_len);
 }
 
+static char *dup_and_filter_pattern(const char *pattern)
+{
+       char *set, *read;
+       size_t count  = 0;
+       char *result = xstrdup(pattern);
+
+       set = result;
+       read = result;
+
+       while (*read) {
+               /* skip escape characters (once) */
+               if (*read == '\\')
+                       read++;
+
+               *set = *read;
+
+               set++;
+               read++;
+               count++;
+       }
+       *set = 0;
+
+       if (count > 2 &&
+           *(set - 1) == '*' &&
+           *(set - 2) == '/')
+               *(set - 2) = 0;
+
+       return result;
+}
+
 static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
 {
        struct pattern_entry *translated;
        char *truncated;
        char *data = NULL;
+       const char *prev, *cur, *next;
 
        if (!pl->use_cone_patterns)
                return;
@@ -657,17 +711,57 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
                return;
        }
 
+       if (given->patternlen < 2 ||
+           *given->pattern == '*' ||
+           strstr(given->pattern, "**")) {
+               /* Not a cone pattern. */
+               warning(_("unrecognized pattern: '%s'"), given->pattern);
+               goto clear_hashmaps;
+       }
+
+       prev = given->pattern;
+       cur = given->pattern + 1;
+       next = given->pattern + 2;
+
+       while (*cur) {
+               /* Watch for glob characters '*', '\', '[', '?' */
+               if (!is_glob_special(*cur))
+                       goto increment;
+
+               /* But only if *prev != '\\' */
+               if (*prev == '\\')
+                       goto increment;
+
+               /* But allow the initial '\' */
+               if (*cur == '\\' &&
+                   is_glob_special(*next))
+                       goto increment;
+
+               /* But a trailing '/' then '*' is fine */
+               if (*prev == '/' &&
+                   *cur == '*' &&
+                   *next == 0)
+                       goto increment;
+
+               /* Not a cone pattern. */
+               warning(_("unrecognized pattern: '%s'"), given->pattern);
+               goto clear_hashmaps;
+
+       increment:
+               prev++;
+               cur++;
+               next++;
+       }
+
        if (given->patternlen > 2 &&
            !strcmp(given->pattern + given->patternlen - 2, "/*")) {
                if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
                        /* Not a cone pattern. */
-                       pl->use_cone_patterns = 0;
                        warning(_("unrecognized pattern: '%s'"), given->pattern);
                        goto clear_hashmaps;
                }
 
-               truncated = xstrdup(given->pattern);
-               truncated[given->patternlen - 2] = 0;
+               truncated = dup_and_filter_pattern(given->pattern);
 
                translated = xmalloc(sizeof(struct pattern_entry));
                translated->pattern = truncated;
@@ -701,7 +795,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
 
        translated = xmalloc(sizeof(struct pattern_entry));
 
-       translated->pattern = xstrdup(given->pattern);
+       translated->pattern = dup_and_filter_pattern(given->pattern);
        translated->patternlen = given->patternlen;
        hashmap_entry_init(&translated->ent,
                           ignore_case ?
@@ -827,6 +921,8 @@ void clear_pattern_list(struct pattern_list *pl)
                free(pl->patterns[i]);
        free(pl->patterns);
        free(pl->filebuf);
+       hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
+       hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
 
        memset(pl, 0, sizeof(*pl));
 }
@@ -944,9 +1040,9 @@ static int add_patterns_from_buffer(char *buf, size_t size,
  * an index if 'istate' is non-null), parse it and store the
  * exclude rules in "pl".
  *
- * If "ss" is not NULL, compute SHA-1 of the exclude file and fill
+ * If "oid_stat" is not NULL, compute oid of the exclude file and fill
  * stat data from disk (only valid if add_patterns returns zero). If
- * ss_valid is non-zero, "ss" must contain good value as input.
+ * oid_stat.valid is non-zero, "oid_stat" must contain good value as input.
  */
 static int add_patterns(const char *fname, const char *base, int baselen,
                        struct pattern_list *pl, struct index_state *istate,
@@ -994,7 +1090,7 @@ static int add_patterns(const char *fname, const char *base, int baselen,
                        int pos;
                        if (oid_stat->valid &&
                            !match_stat_data_racy(istate, &oid_stat->stat, &st))
-                               ; /* no content change, ss->sha1 still good */
+                               ; /* no content change, oid_stat->oid still good */
                        else if (istate &&
                                 (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
                                 !ce_stage(istate->cache[pos]) &&
@@ -1003,8 +1099,8 @@ static int add_patterns(const char *fname, const char *base, int baselen,
                                oidcpy(&oid_stat->oid,
                                       &istate->cache[pos]->oid);
                        else
-                               hash_object_file(buf, size, "blob",
-                                                &oid_stat->oid);
+                               hash_object_file(the_hash_algo, buf, size,
+                                                "blob", &oid_stat->oid);
                        fill_stat_data(&oid_stat->stat, &st);
                        oid_stat->valid = 1;
                }
@@ -1686,9 +1782,11 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
         * for matching patterns.
         */
        if (pathspec && !excluded) {
-               matches_how = do_match_pathspec(istate, pathspec, dirname, len,
-                                               0 /* prefix */, NULL /* seen */,
-                                               DO_MATCH_LEADING_PATHSPEC);
+               matches_how = match_pathspec_with_flags(istate, pathspec,
+                                                       dirname, len,
+                                                       0 /* prefix */,
+                                                       NULL /* seen */,
+                                                       DO_MATCH_LEADING_PATHSPEC);
                if (!matches_how)
                        return path_none;
        }
@@ -1701,9 +1799,12 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
                nested_repo = is_nonbare_repository_dir(&sb);
                strbuf_release(&sb);
        }
-       if (nested_repo)
-               return ((dir->flags & DIR_SKIP_NESTED_GIT) ? path_none :
-                       (excluded ? path_excluded : path_untracked));
+       if (nested_repo) {
+               if ((dir->flags & DIR_SKIP_NESTED_GIT) ||
+                   (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC))
+                       return path_none;
+               return excluded ? path_excluded : path_untracked;
+       }
 
        if (!(dir->flags & DIR_SHOW_OTHER_DIRECTORIES)) {
                if (excluded &&
@@ -1749,7 +1850,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
         * to recurse into untracked/ignored directories if either of the
         * following bits is set:
         *   - DIR_SHOW_IGNORED_TOO (because then we need to determine if
-        *                           there are ignored directories below)
+        *                           there are ignored entries below)
         *   - DIR_HIDE_EMPTY_DIRECTORIES (because we have to determine if
         *                                 the directory is empty)
         */
@@ -1767,10 +1868,11 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
                return path_excluded;
 
        /*
-        * If we have we don't want to know the all the paths under an
-        * untracked or ignored directory, we still need to go into the
-        * directory to determine if it is empty (because an empty directory
-        * should be path_none instead of path_excluded or path_untracked).
+        * Even if we don't want to know all the paths under an untracked or
+        * ignored directory, we may still need to go into the directory to
+        * determine if it is empty (because with DIR_HIDE_EMPTY_DIRECTORIES,
+        * an empty directory should be path_none instead of path_excluded or
+        * path_untracked).
         */
        check_only = ((dir->flags & DIR_HIDE_EMPTY_DIRECTORIES) &&
                      !(dir->flags & DIR_SHOW_IGNORED_TOO));
@@ -2003,7 +2105,6 @@ static int resolve_dtype(int dtype, struct index_state *istate,
 }
 
 static enum path_treatment treat_path_fast(struct dir_struct *dir,
-                                          struct untracked_cache_dir *untracked,
                                           struct cached_dir *cdir,
                                           struct index_state *istate,
                                           struct strbuf *path,
@@ -2051,7 +2152,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
        int has_path_in_index, dtype, excluded;
 
        if (!cdir->d_name)
-               return treat_path_fast(dir, untracked, cdir, istate, path,
+               return treat_path_fast(dir, cdir, istate, path,
                                       baselen, pathspec);
        if (is_dot_or_dotdot(cdir->d_name) || !fspathcmp(cdir->d_name, ".git"))
                return path_none;
@@ -2118,9 +2219,9 @@ static enum path_treatment treat_path(struct dir_struct *dir,
        case DT_REG:
        case DT_LNK:
                if (pathspec &&
-                   !do_match_pathspec(istate, pathspec, path->buf, path->len,
-                                      0 /* prefix */, NULL /* seen */,
-                                      0 /* flags */))
+                   !match_pathspec(istate, pathspec, path->buf, path->len,
+                                   0 /* prefix */, NULL /* seen */,
+                                   0 /* is_dir */))
                        return path_none;
                if (excluded)
                        return path_excluded;
@@ -2917,10 +3018,10 @@ int remove_path(const char *name)
 }
 
 /*
- * Frees memory within dir which was allocated for exclude lists and
- * the exclude_stack.  Does not free dir itself.
+ * Frees memory within dir which was allocated, and resets fields for further
+ * use.  Does not free dir itself.
  */
-void clear_directory(struct dir_struct *dir)
+void dir_clear(struct dir_struct *dir)
 {
        int i, j;
        struct exclude_list_group *group;
@@ -2938,6 +3039,13 @@ void clear_directory(struct dir_struct *dir)
                free(group->pl);
        }
 
+       for (i = 0; i < dir->ignored_nr; i++)
+               free(dir->ignored[i]);
+       for (i = 0; i < dir->nr; i++)
+               free(dir->entries[i]);
+       free(dir->ignored);
+       free(dir->entries);
+
        stk = dir->exclude_stack;
        while (stk) {
                struct exclude_stack *prev = stk->prev;
@@ -2945,6 +3053,8 @@ void clear_directory(struct dir_struct *dir)
                stk = prev;
        }
        strbuf_release(&dir->basebuf);
+
+       dir_init(dir);
 }
 
 struct ondisk_untracked_cache {