Imported Upstream version 2.23.1
[platform/upstream/git.git] / tree-walk.c
index 828f435..4eaf29f 100644 (file)
@@ -2,6 +2,7 @@
 #include "tree-walk.h"
 #include "unpack-trees.h"
 #include "dir.h"
+#include "object-store.h"
 #include "tree.h"
 #include "pathspec.h"
 
@@ -26,8 +27,9 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
 {
        const char *path;
        unsigned int mode, len;
+       const unsigned hashsz = the_hash_algo->rawsz;
 
-       if (size < 23 || buf[size - 21]) {
+       if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
                strbuf_addstr(err, _("too-short tree object"));
                return -1;
        }
@@ -41,12 +43,19 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
                strbuf_addstr(err, _("empty filename in tree entry"));
                return -1;
        }
+#ifdef GIT_WINDOWS_NATIVE
+       if (protect_ntfs && strchr(path, '\\')) {
+               strbuf_addf(err, _("filename in tree entry contains backslash: '%s'"), path);
+               return -1;
+       }
+#endif
        len = strlen(path) + 1;
 
        /* Initialize the descriptor entry */
        desc->entry.path = path;
        desc->entry.mode = canon_mode(mode);
-       desc->entry.oid  = (const struct object_id *)(path + len);
+       desc->entry.pathlen = len - 1;
+       hashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);
 
        return 0;
 }
@@ -78,15 +87,17 @@ int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned l
        return result;
 }
 
-void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
+void *fill_tree_descriptor(struct repository *r,
+                          struct tree_desc *desc,
+                          const struct object_id *oid)
 {
        unsigned long size = 0;
        void *buf = NULL;
 
-       if (sha1) {
-               buf = read_object_with_reference(sha1, tree_type, &size, NULL);
+       if (oid) {
+               buf = read_object_with_reference(r, oid, tree_type, &size, NULL);
                if (!buf)
-                       die("unable to read tree %s", sha1_to_hex(sha1));
+                       die("unable to read tree %s", oid_to_hex(oid));
        }
        init_tree_desc(desc, buf, size);
        return buf;
@@ -105,7 +116,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
 static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
 {
        const void *buf = desc->buffer;
-       const unsigned char *end = desc->entry.oid->hash + 20;
+       const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
        unsigned long size = desc->size;
        unsigned long len = end - (const unsigned char *)buf;
 
@@ -173,9 +184,11 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
                pathlen--;
        info->pathlen = pathlen ? pathlen + 1 : 0;
        info->name.path = base;
-       info->name.oid = (void *)(base + pathlen + 1);
-       if (pathlen)
+       info->name.pathlen = pathlen;
+       if (pathlen) {
+               hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1);
                info->prev = &dummy;
+       }
 }
 
 char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
@@ -363,7 +376,8 @@ static void free_extended_entry(struct tree_desc_x *t)
        }
 }
 
-static inline int prune_traversal(struct name_entry *e,
+static inline int prune_traversal(struct index_state *istate,
+                                 struct name_entry *e,
                                  struct traverse_info *info,
                                  struct strbuf *base,
                                  int still_interesting)
@@ -372,10 +386,13 @@ static inline int prune_traversal(struct name_entry *e,
                return 2;
        if (still_interesting < 0)
                return still_interesting;
-       return tree_entry_interesting(e, base, 0, info->pathspec);
+       return tree_entry_interesting(istate, e, base,
+                                     0, info->pathspec);
 }
 
-int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
+int traverse_trees(struct index_state *istate,
+                  int n, struct tree_desc *t,
+                  struct traverse_info *info)
 {
        int error = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
@@ -459,7 +476,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                }
                if (!mask)
                        break;
-               interesting = prune_traversal(e, info, &base, interesting);
+               interesting = prune_traversal(istate, e, info, &base, interesting);
                if (interesting < 0)
                        break;
                if (interesting) {
@@ -488,18 +505,20 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 struct dir_state {
        void *tree;
        unsigned long size;
-       unsigned char sha1[20];
+       struct object_id oid;
 };
 
-static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
+static int find_tree_entry(struct repository *r, struct tree_desc *t,
+                          const char *name, struct object_id *result,
+                          unsigned short *mode)
 {
        int namelen = strlen(name);
        while (t->size) {
                const char *entry;
-               const struct object_id *oid;
+               struct object_id oid;
                int entrylen, cmp;
 
-               oid = tree_entry_extract(t, &entry, mode);
+               oidcpy(&oid, tree_entry_extract(t, &entry, mode));
                entrylen = tree_entry_len(&t->entry);
                update_tree_entry(t);
                if (entrylen > namelen)
@@ -510,7 +529,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
                if (cmp < 0)
                        break;
                if (entrylen == namelen) {
-                       hashcpy(result, oid->hash);
+                       oidcpy(result, &oid);
                        return 0;
                }
                if (name[entrylen] != '/')
@@ -518,27 +537,31 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
                if (!S_ISDIR(*mode))
                        break;
                if (++entrylen == namelen) {
-                       hashcpy(result, oid->hash);
+                       oidcpy(result, &oid);
                        return 0;
                }
-               return get_tree_entry(oid->hash, name + entrylen, result, mode);
+               return get_tree_entry(r, &oid, name + entrylen, result, mode);
        }
        return -1;
 }
 
-int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
+int get_tree_entry(struct repository *r,
+                  const struct object_id *tree_oid,
+                  const char *name,
+                  struct object_id *oid,
+                  unsigned short *mode)
 {
        int retval;
        void *tree;
        unsigned long size;
-       unsigned char root[20];
+       struct object_id root;
 
-       tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
+       tree = read_object_with_reference(r, tree_oid, tree_type, &size, &root);
        if (!tree)
                return -1;
 
        if (name[0] == '\0') {
-               hashcpy(sha1, root);
+               oidcpy(oid, &root);
                free(tree);
                return 0;
        }
@@ -548,7 +571,7 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
        } else {
                struct tree_desc t;
                init_tree_desc(&t, tree, size);
-               retval = find_tree_entry(&t, name, sha1, mode);
+               retval = find_tree_entry(r, &t, name, oid, mode);
        }
        free(tree);
        return retval;
@@ -573,25 +596,26 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
  * with the sha1 of the found object, and *mode will hold the mode of
  * the object.
  *
- * See the code for enum follow_symlink_result for a description of
+ * See the code for enum get_oid_result for a description of
  * the return values.
  */
-enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode)
+enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
+               struct object_id *tree_oid, const char *name,
+               struct object_id *result, struct strbuf *result_path,
+               unsigned short *mode)
 {
        int retval = MISSING_OBJECT;
        struct dir_state *parents = NULL;
        size_t parents_alloc = 0;
-       ssize_t parents_nr = 0;
-       unsigned char current_tree_sha1[20];
+       size_t i, parents_nr = 0;
+       struct object_id current_tree_oid;
        struct strbuf namebuf = STRBUF_INIT;
        struct tree_desc t;
        int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
-       int i;
 
        init_tree_desc(&t, NULL, 0UL);
-       strbuf_init(result_path, 0);
        strbuf_addstr(&namebuf, name);
-       hashcpy(current_tree_sha1, tree_sha1);
+       oidcpy(&current_tree_oid, tree_oid);
 
        while (1) {
                int find_result;
@@ -600,22 +624,23 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
 
                if (!t.buffer) {
                        void *tree;
-                       unsigned char root[20];
+                       struct object_id root;
                        unsigned long size;
-                       tree = read_object_with_reference(current_tree_sha1,
+                       tree = read_object_with_reference(r,
+                                                         &current_tree_oid,
                                                          tree_type, &size,
-                                                         root);
+                                                         &root);
                        if (!tree)
                                goto done;
 
                        ALLOC_GROW(parents, parents_nr + 1, parents_alloc);
                        parents[parents_nr].tree = tree;
                        parents[parents_nr].size = size;
-                       hashcpy(parents[parents_nr].sha1, root);
+                       oidcpy(&parents[parents_nr].oid, &root);
                        parents_nr++;
 
                        if (namebuf.buf[0] == '\0') {
-                               hashcpy(result, root);
+                               oidcpy(result, &root);
                                retval = FOUND;
                                goto done;
                        }
@@ -665,21 +690,21 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
 
                /* We could end up here via a symlink to dir/.. */
                if (namebuf.buf[0] == '\0') {
-                       hashcpy(result, parents[parents_nr - 1].sha1);
+                       oidcpy(result, &parents[parents_nr - 1].oid);
                        retval = FOUND;
                        goto done;
                }
 
                /* Look up the first (or only) path component in the tree. */
-               find_result = find_tree_entry(&t, namebuf.buf,
-                                             current_tree_sha1, mode);
+               find_result = find_tree_entry(r, &t, namebuf.buf,
+                                             &current_tree_oid, mode);
                if (find_result) {
                        goto done;
                }
 
                if (S_ISDIR(*mode)) {
                        if (!remainder) {
-                               hashcpy(result, current_tree_sha1);
+                               oidcpy(result, &current_tree_oid);
                                retval = FOUND;
                                goto done;
                        }
@@ -689,7 +714,7 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
                                      1 + first_slash - namebuf.buf);
                } else if (S_ISREG(*mode)) {
                        if (!remainder) {
-                               hashcpy(result, current_tree_sha1);
+                               oidcpy(result, &current_tree_oid);
                                retval = FOUND;
                        } else {
                                retval = NOT_DIR;
@@ -715,8 +740,9 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
                         */
                        retval = DANGLING_SYMLINK;
 
-                       contents = read_sha1_file(current_tree_sha1, &type,
-                                                 &link_len);
+                       contents = repo_read_object_file(r,
+                                                   &current_tree_oid, &type,
+                                                   &link_len);
 
                        if (!contents)
                                goto done;
@@ -928,7 +954,8 @@ static int match_wildcard_base(const struct pathspec_item *item,
  * Pre-condition: either baselen == base_offset (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
-static enum interesting do_match(const struct name_entry *entry,
+static enum interesting do_match(struct index_state *istate,
+                                const struct name_entry *entry,
                                 struct strbuf *base, int base_offset,
                                 const struct pathspec *ps,
                                 int exclude)
@@ -944,7 +971,8 @@ static enum interesting do_match(const struct name_entry *entry,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -976,14 +1004,20 @@ static enum interesting do_match(const struct name_entry *entry,
 
                        if (!ps->recursive ||
                            !(ps->magic & PATHSPEC_MAXDEPTH) ||
-                           ps->max_depth == -1)
-                               return all_entries_interesting;
-
-                       return within_depth(base_str + matchlen + 1,
-                                           baselen - matchlen - 1,
-                                           !!S_ISDIR(entry->mode),
-                                           ps->max_depth) ?
-                               entry_interesting : entry_not_interesting;
+                           ps->max_depth == -1) {
+                               if (!item->attr_match_nr)
+                                       return all_entries_interesting;
+                               else
+                                       goto interesting;
+                       }
+
+                       if (within_depth(base_str + matchlen + 1,
+                                        baselen - matchlen - 1,
+                                        !!S_ISDIR(entry->mode),
+                                        ps->max_depth))
+                               goto interesting;
+                       else
+                               return entry_not_interesting;
                }
 
                /* Either there must be no base, or the base must match. */
@@ -991,12 +1025,12 @@ static enum interesting do_match(const struct name_entry *entry,
                        if (match_entry(item, entry, pathlen,
                                        match + baselen, matchlen - baselen,
                                        &never_interesting))
-                               return entry_interesting;
+                               goto interesting;
 
                        if (item->nowildcard_len < item->len) {
                                if (!git_fnmatch(item, match + baselen, entry->path,
                                                 item->nowildcard_len - baselen))
-                                       return entry_interesting;
+                                       goto interesting;
 
                                /*
                                 * Match all directories. We'll try to
@@ -1004,6 +1038,20 @@ static enum interesting do_match(const struct name_entry *entry,
                                 */
                                if (ps->recursive && S_ISDIR(entry->mode))
                                        return entry_interesting;
+
+                               /*
+                                * When matching against submodules with
+                                * wildcard characters, ensure that the entry
+                                * at least matches up to the first wild
+                                * character.  More accurate matching can then
+                                * be performed in the submodule itself.
+                                */
+                               if (ps->recurse_submodules &&
+                                   S_ISGITLINK(entry->mode) &&
+                                   !ps_strncmp(item, match + baselen,
+                                               entry->path,
+                                               item->nowildcard_len - baselen))
+                                       goto interesting;
                        }
 
                        continue;
@@ -1038,8 +1086,23 @@ match_wildcards:
                if (!git_fnmatch(item, match, base->buf + base_offset,
                                 item->nowildcard_len)) {
                        strbuf_setlen(base, base_offset + baselen);
-                       return entry_interesting;
+                       goto interesting;
                }
+
+               /*
+                * When matching against submodules with
+                * wildcard characters, ensure that the entry
+                * at least matches up to the first wild
+                * character.  More accurate matching can then
+                * be performed in the submodule itself.
+                */
+               if (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&
+                   !ps_strncmp(item, match, base->buf + base_offset,
+                               item->nowildcard_len)) {
+                       strbuf_setlen(base, base_offset + baselen);
+                       goto interesting;
+               }
+
                strbuf_setlen(base, base_offset + baselen);
 
                /*
@@ -1047,10 +1110,42 @@ match_wildcards:
                 * later on.
                 * max_depth is ignored but we may consider support it
                 * in future, see
-                * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840
+                * https://public-inbox.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/
                 */
                if (ps->recursive && S_ISDIR(entry->mode))
                        return entry_interesting;
+               continue;
+interesting:
+               if (item->attr_match_nr) {
+                       int ret;
+
+                       /*
+                        * Must not return all_entries_not_interesting
+                        * prematurely. We do not know if all entries do not
+                        * match some attributes with current attr API.
+                        */
+                       never_interesting = entry_not_interesting;
+
+                       /*
+                        * Consider all directories interesting (because some
+                        * of those files inside may match some attributes
+                        * even though the parent dir does not)
+                        *
+                        * FIXME: attributes _can_ match directories and we
+                        * can probably return all_entries_interesting or
+                        * all_entries_not_interesting here if matched.
+                        */
+                       if (S_ISDIR(entry->mode))
+                               return entry_interesting;
+
+                       strbuf_add(base, entry->path, pathlen);
+                       ret = match_pathspec_attrs(istate, base->buf + base_offset,
+                                                  base->len - base_offset, item);
+                       strbuf_setlen(base, base_offset + baselen);
+                       if (!ret)
+                               continue;
+               }
+               return entry_interesting;
        }
        return never_interesting; /* No matches */
 }
@@ -1061,12 +1156,13 @@ match_wildcards:
  * Pre-condition: either baselen == base_offset (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
-enum interesting tree_entry_interesting(const struct name_entry *entry,
+enum interesting tree_entry_interesting(struct index_state *istate,
+                                       const struct name_entry *entry,
                                        struct strbuf *base, int base_offset,
                                        const struct pathspec *ps)
 {
        enum interesting positive, negative;
-       positive = do_match(entry, base, base_offset, ps, 0);
+       positive = do_match(istate, entry, base, base_offset, ps, 0);
 
        /*
         * case | entry | positive | negative | result
@@ -1078,7 +1174,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
         *   5  |  file |    1     |    1     |   0
         *   6  |  file |    1     |    2     |   0
         *   7  |  file |    2     |   -1     |   2
-        *   8  |  file |    2     |    0     |   2
+        *   8  |  file |    2     |    0     |   1
         *   9  |  file |    2     |    1     |   0
         *  10  |  file |    2     |    2     |  -1
         * -----+-------+----------+----------+-------
@@ -1089,7 +1185,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
         *  15  |  dir  |    1     |    1     |   1 (*)
         *  16  |  dir  |    1     |    2     |   0
         *  17  |  dir  |    2     |   -1     |   2
-        *  18  |  dir  |    2     |    0     |   2
+        *  18  |  dir  |    2     |    0     |   1
         *  19  |  dir  |    2     |    1     |   1 (*)
         *  20  |  dir  |    2     |    2     |  -1
         *
@@ -1103,9 +1199,14 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
            positive <= entry_not_interesting) /* #1, #2, #11, #12 */
                return positive;
 
-       negative = do_match(entry, base, base_offset, ps, 1);
+       negative = do_match(istate, entry, base, base_offset, ps, 1);
+
+       /* #8, #18 */
+       if (positive == all_entries_interesting &&
+           negative == entry_not_interesting)
+               return entry_interesting;
 
-       /* #3, #4, #7, #8, #13, #14, #17, #18 */
+       /* #3, #4, #7, #13, #14, #17 */
        if (negative <= entry_not_interesting)
                return positive;