Merge branch 'bc/tree-walk-oid'
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:56 +0000 (12:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:56 +0000 (12:47 -0800)
The code to walk tree objects has been taught that we may be
working with object names that are not computed with SHA-1.

* bc/tree-walk-oid:
cache: make oidcpy always copy GIT_MAX_RAWSZ bytes
tree-walk: store object_id in a separate member
match-trees: use hashcpy to splice trees
match-trees: compute buffer offset correctly when splicing
tree-walk: copy object ID before use

13 files changed:
1  2 
builtin/grep.c
builtin/merge-tree.c
builtin/pack-objects.c
cache-tree.c
cache.h
list-objects.c
packfile.c
revision.c
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
unpack-trees.c
diff --combined builtin/grep.c
index dc9183876499e30e00f81b10c74c54270160448d,6fb93c037031e7d32e1504247f3e7a677c51175d..dd52ea968baf1b5b971c5f59152a561744cf9b92
@@@ -404,10 -404,7 +404,10 @@@ static int grep_submodule(struct grep_o
                          const struct object_id *oid,
                          const char *filename, const char *path)
  {
 -      struct repository submodule;
 +      struct repository subrepo;
 +      const struct submodule *sub = submodule_from_path(superproject,
 +                                                        &null_oid, path);
 +
        int hit;
  
        /*
                return 0;
        }
  
 -      if (repo_submodule_init(&submodule, superproject, path)) {
 +      if (repo_submodule_init(&subrepo, superproject, sub)) {
                grep_read_unlock();
                return 0;
        }
  
 -      repo_read_gitmodules(&submodule);
 +      repo_read_gitmodules(&subrepo);
  
        /*
         * NEEDSWORK: This adds the submodule's object directory to the list of
         * store is no longer global and instead is a member of the repository
         * object.
         */
 -      add_to_alternates_memory(submodule.objects->odb->path);
 +      add_to_alternates_memory(subrepo.objects->odb->path);
        grep_read_unlock();
  
        if (oid) {
  
                init_tree_desc(&tree, data, size);
                hit = grep_tree(opt, pathspec, &tree, &base, base.len,
 -                              object->type == OBJ_COMMIT, &submodule);
 +                              object->type == OBJ_COMMIT, &subrepo);
                strbuf_release(&base);
                free(data);
        } else {
 -              hit = grep_cache(opt, &submodule, pathspec, 1);
 +              hit = grep_cache(opt, &subrepo, pathspec, 1);
        }
  
 -      repo_clear(&submodule);
 +      repo_clear(&subrepo);
        return hit;
  }
  
@@@ -556,8 -553,7 +556,8 @@@ static int grep_tree(struct grep_opt *o
  
                if (match != all_entries_interesting) {
                        strbuf_addstr(&name, base->buf + tn_len);
 -                      match = tree_entry_interesting(&entry, &name,
 +                      match = tree_entry_interesting(repo->index,
 +                                                     &entry, &name,
                                                       0, pathspec);
                        strbuf_setlen(&name, name_base_len);
  
                strbuf_add(base, entry.path, te_len);
  
                if (S_ISREG(entry.mode)) {
-                       hit |= grep_oid(opt, entry.oid, base->buf, tn_len,
+                       hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
                        void *data;
                        unsigned long size;
  
-                       data = lock_and_read_oid_file(entry.oid, &type, &size);
+                       data = lock_and_read_oid_file(&entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
-                                   oid_to_hex(entry.oid));
+                                   oid_to_hex(&entry.oid));
  
                        strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
                                         check_attr, repo);
                        free(data);
                } else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
-                       hit |= grep_submodule(opt, repo, pathspec, entry.oid,
+                       hit |= grep_submodule(opt, repo, pathspec, &entry.oid,
                                              base->buf, base->buf + tn_len);
                }
  
diff --combined builtin/merge-tree.c
index 4984b7e12e90aab356606e6e7d3e6526f31c8c73,4500c41e944022a0b525ea82ca2ea8fb8a17c722..5541ad091eb8c4b975d25be6386ea79be4831e5b
@@@ -154,15 -154,15 +154,15 @@@ static void show_result(void
  /* An empty entry never compares same, not even to another empty entry */
  static int same_entry(struct name_entry *a, struct name_entry *b)
  {
-       return  a->oid &&
-               b->oid &&
-               oideq(a->oid, b->oid) &&
+       return  !is_null_oid(&a->oid) &&
+               !is_null_oid(&b->oid) &&
+               oideq(&a->oid, &b->oid) &&
                a->mode == b->mode;
  }
  
  static int both_empty(struct name_entry *a, struct name_entry *b)
  {
-       return !(a->oid || b->oid);
+       return is_null_oid(&a->oid) && is_null_oid(&b->oid);
  }
  
  static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
  
  static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
  {
-       char *path = xmallocz(traverse_path_len(info, n));
+       char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
        return make_traverse_path(path, info, n);
  }
  
@@@ -192,8 -192,8 +192,8 @@@ static void resolve(const struct traver
                return;
  
        path = traverse_path(info, result);
-       orig = create_entry(2, ours->mode, ours->oid, path);
-       final = create_entry(0, result->mode, result->oid, path);
+       orig = create_entry(2, ours->mode, &ours->oid, path);
+       final = create_entry(0, result->mode, &result->oid, path);
  
        final->link = orig;
  
@@@ -217,7 -217,7 +217,7 @@@ static void unresolved_directory(const 
  
        newbase = traverse_path(info, p);
  
- #define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid : NULL)
+ #define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
        buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
        buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
        buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
@@@ -243,7 -243,7 +243,7 @@@ static struct merge_list *link_entry(un
                path = entry->path;
        else
                path = traverse_path(info, n);
-       link = create_entry(stage, n->mode, n->oid, path);
+       link = create_entry(stage, n->mode, &n->oid, path);
        link->link = entry;
        return link;
  }
@@@ -318,7 -318,7 +318,7 @@@ static int threeway_callback(int n, uns
        }
  
        if (same_entry(entry+0, entry+1)) {
-               if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
+               if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {
                        /* We did not touch, they modified -- take theirs */
                        resolve(info, entry+1, entry+2);
                        return mask;
@@@ -346,7 -346,7 +346,7 @@@ static void merge_trees(struct tree_des
  
        setup_traverse_info(&info, base);
        info.fn = threeway_callback;
 -      traverse_trees(3, t, &info);
 +      traverse_trees(&the_index, 3, t, &info);
  }
  
  static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
diff --combined builtin/pack-objects.c
index 889df2c755176b60fc5c22b0ae553ced23629dc2,327d9170c4daa15401e0dbe9627e211f0799af72..0a70d046043ec9b7ae2e604a7ad098d22d36ed59
@@@ -1334,7 -1334,7 +1334,7 @@@ static void add_pbase_object(struct tre
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       add_object_entry(entry.oid,
+                       add_object_entry(&entry.oid,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
  
-                       tree = pbase_tree_get(entry.oid);
+                       tree = pbase_tree_get(&entry.oid);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@@ -3084,16 -3084,14 +3084,16 @@@ static void record_recent_commit(struc
  static void get_object_list(int ac, const char **av)
  {
        struct rev_info revs;
 +      struct setup_revision_opt s_r_opt = {
 +              .allow_exclude_promisor_objects = 1,
 +      };
        char line[1000];
        int flags = 0;
        int save_warning;
  
        repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
 -      revs.allow_exclude_promisor_objects_opt = 1;
 -      setup_revisions(ac, av, &revs, NULL);
 +      setup_revisions(ac, av, &revs, &s_r_opt);
  
        /* make sure shallows are read */
        is_repository_shallow(the_repository);
diff --combined cache-tree.c
index eabb8fb6546cbb5e418f30a4e9329d635520d601,98cb66587bff47db4180ea53dfae2238782a6687..c4b8a1fa16d4e1376c1df3f2784db32c20001314
@@@ -448,7 -448,7 +448,7 @@@ int cache_tree_update(struct index_stat
  }
  
  static void write_one(struct strbuf *buffer, struct cache_tree *it,
 -                      const char *path, int pathlen)
 +                    const char *path, int pathlen)
  {
        int i;
  
@@@ -675,7 -675,7 +675,7 @@@ static void prime_cache_tree_rec(struc
                        cnt++;
                else {
                        struct cache_tree_sub *sub;
-                       struct tree *subtree = lookup_tree(r, entry.oid);
+                       struct tree *subtree = lookup_tree(r, &entry.oid);
                        if (!subtree->object.parsed)
                                parse_tree(subtree);
                        sub = cache_tree_sub(it, entry.path);
@@@ -724,7 -724,7 +724,7 @@@ int cache_tree_matches_traversal(struc
  
        it = find_cache_tree_from_traversal(root, info);
        it = cache_tree_find(it, ent->path);
-       if (it && it->entry_count > 0 && oideq(ent->oid, &it->oid))
+       if (it && it->entry_count > 0 && oideq(&ent->oid, &it->oid))
                return it->entry_count;
        return 0;
  }
diff --combined cache.h
index d94eff88a960ce730d8ab5d500b3e89d4c84df9d,8b8c6a1a2a0663d25565924079889d587868645b..009e8b3b150eaab7c5c3705c0d466773743dbe0f
+++ b/cache.h
@@@ -45,20 -45,10 +45,20 @@@ unsigned long git_deflate_bound(git_zst
  /* The length in bytes and in hex digits of an object name (SHA-1 value). */
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 +/* The block size of SHA-1. */
 +#define GIT_SHA1_BLKSZ 64
 +
 +/* The length in bytes and in hex digits of an object name (SHA-256 value). */
 +#define GIT_SHA256_RAWSZ 32
 +#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
 +/* The block size of SHA-256. */
 +#define GIT_SHA256_BLKSZ 64
  
  /* The length in byte and in hex digits of the largest possible hash value. */
 -#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
 -#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
 +#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
 +#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
 +/* The largest possible block size for any supported hash. */
 +#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
  
  struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@@ -1038,12 -1028,16 +1038,12 @@@ extern const struct object_id null_oid
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
        /*
 -       * This is a temporary optimization hack. By asserting the size here,
 -       * we let the compiler know that it's always going to be 20, which lets
 -       * it turn this fixed-size memcmp into a few inline instructions.
 -       *
 -       * This will need to be extended or ripped out when we learn about
 -       * hashes of different sizes.
 +       * Teach the compiler that there are only two possibilities of hash size
 +       * here, so that it can optimize for this case as much as possible.
         */
 -      if (the_hash_algo->rawsz != 20)
 -              BUG("hash size not yet supported by hashcmp");
 -      return memcmp(sha1, sha2, the_hash_algo->rawsz);
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
  
  static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return !hashcmp(sha1, sha2);
 +      /*
 +       * We write this here instead of deferring to hashcmp so that the
 +       * compiler can properly inline it and avoid calling memcmp.
 +       */
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@@ -1084,7 -1072,7 +1084,7 @@@ static inline void hashcpy(unsigned cha
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
  {
-       hashcpy(dst->hash, src->hash);
+       memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
  }
  
  static inline struct object_id *oiddup(const struct object_id *src)
@@@ -1377,9 -1365,9 +1377,9 @@@ extern int get_oid_hex(const char *hex
  extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  
  /*
 - * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
 + * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
 - * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
 + * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
   * convenience.
   *
   * The non-`_r` variant returns a static buffer, but uses a ring of 4
   *
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
 -extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 -extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 -extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
 +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
 +char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +char *oid_to_hex_r(char *out, const struct object_id *oid);
 +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);     /* static buffer result! */
 +char *sha1_to_hex(const unsigned char *sha1);                                         /* same static buffer */
 +char *hash_to_hex(const unsigned char *hash);                                         /* same static buffer */
 +char *oid_to_hex(const struct object_id *oid);                                                /* same static buffer */
  
  /*
   * Parse a 40-character hexadecimal object ID starting from hex, updating the
@@@ -1554,9 -1539,9 +1554,9 @@@ struct checkout 
  #define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
 -extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 +extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
  extern void enable_delayed_checkout(struct checkout *state);
 -extern int finish_delayed_checkout(struct checkout *state);
 +extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
  
  struct cache_def {
        struct strbuf path;
diff --combined list-objects.c
index 4e2789768d21ccb47a8fe2d5de62b6be58ea9bb7,ee6aff0cef7f9dd1bf1169f4f1c6dff0963886b7..a2296a8e7b42a3d5d044c648d353ac94f9b4ed81
@@@ -114,8 -114,7 +114,8 @@@ static void process_tree_contents(struc
  
        while (tree_entry(&desc, &entry)) {
                if (match != all_entries_interesting) {
 -                      match = tree_entry_interesting(&entry, base, 0,
 +                      match = tree_entry_interesting(ctx->revs->repo->index,
 +                                                     &entry, base, 0,
                                                       &ctx->revs->diffopt.pathspec);
                        if (match == all_entries_not_interesting)
                                break;
                }
  
                if (S_ISDIR(entry.mode)) {
-                       struct tree *t = lookup_tree(ctx->revs->repo, entry.oid);
+                       struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);
                        t->object.flags |= NOT_USER_GIVEN;
                        process_tree(ctx, t, base, entry.path);
                }
                else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(ctx, entry.oid->hash,
+                       process_gitlink(ctx, entry.oid.hash,
                                        base, entry.path);
                else {
-                       struct blob *b = lookup_blob(ctx->revs->repo, entry.oid);
+                       struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
                        b->object.flags |= NOT_USER_GIVEN;
                        process_blob(ctx, b, base, entry.path);
                }
diff --combined packfile.c
index 0fe9c21bf108965867ca691c245c3be0124a9102,e38af1a275a52a6cf5d7eda9789c4e061a735f74..ac6bb64bc39968801bc829389abcb077750a834f
@@@ -994,8 -994,11 +994,8 @@@ void reprepare_packed_git(struct reposi
  {
        struct object_directory *odb;
  
 -      for (odb = r->objects->odb; odb; odb = odb->next) {
 -              oid_array_clear(&odb->loose_objects_cache);
 -              memset(&odb->loose_objects_subdir_seen, 0,
 -                     sizeof(odb->loose_objects_subdir_seen));
 -      }
 +      for (odb = r->objects->odb; odb; odb = odb->next)
 +              odb_clear_loose_cache(odb);
  
        r->objects->approximate_object_count_valid = 0;
        r->objects->packed_git_initialized = 0;
@@@ -2097,7 -2100,7 +2097,7 @@@ static int add_promisor_object(const st
                         */
                        return 0;
                while (tree_entry_gently(&desc, &entry))
-                       oidset_insert(set, entry.oid);
+                       oidset_insert(set, &entry.oid);
        } else if (obj->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *) obj;
                struct commit_list *parents = commit->parents;
diff --combined revision.c
index 13cfb59b388d1503adbaef424ea1dcecc410b673,05fb390d55711fbf69047cbe7d22dee03da5d32c..119947ced0ba74cd20a007daf43f61386a393a6a
@@@ -67,10 -67,10 +67,10 @@@ static void mark_tree_contents_unintere
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
-                       mark_tree_uninteresting(r, lookup_tree(r, entry.oid));
+                       mark_tree_uninteresting(r, lookup_tree(r, &entry.oid));
                        break;
                case OBJ_BLOB:
-                       mark_blob_uninteresting(lookup_blob(r, entry.oid));
+                       mark_blob_uninteresting(lookup_blob(r, &entry.oid));
                        break;
                default:
                        /* Subproject commit - not in this repository */
@@@ -1463,7 -1463,6 +1463,7 @@@ void repo_init_revisions(struct reposit
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
 +      revs->pruning.repo = r;
        revs->pruning.flags.recursive = 1;
        revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
  }
  
  static void add_pending_commit_list(struct rev_info *revs,
 -                                    struct commit_list *commit_list,
 -                                    unsigned int flags)
 +                                  struct commit_list *commit_list,
 +                                  unsigned int flags)
  {
        while (commit_list) {
                struct object *object = &commit_list->item->object;
@@@ -1730,8 -1729,6 +1730,8 @@@ int handle_revision_arg(const char *arg
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, &oid, flags ^ local_flags);
 +      if (!object)
 +              return revs->ignore_missing ? 0 : -1;
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
        add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
        free(oc.path);
@@@ -1794,8 -1791,7 +1794,8 @@@ static void add_message_grep(struct rev
  }
  
  static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
 -                             int *unkc, const char **unkv)
 +                             int *unkc, const char **unkv,
 +                             const struct setup_revision_opt* opt)
  {
        const char *arg = argv[0];
        const char *optarg;
                revs->limited = 1;
        } else if (!strcmp(arg, "--ignore-missing")) {
                revs->ignore_missing = 1;
 -      } else if (revs->allow_exclude_promisor_objects_opt &&
 +      } else if (opt && opt->allow_exclude_promisor_objects &&
                   !strcmp(arg, "--exclude-promisor-objects")) {
                if (fetch_if_missing)
                        BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0");
@@@ -2177,7 -2173,7 +2177,7 @@@ void parse_revision_opt(struct rev_inf
                        const char * const usagestr[])
  {
        int n = handle_revision_opt(revs, ctx->argc, ctx->argv,
 -                                  &ctx->cpidx, ctx->out);
 +                                  &ctx->cpidx, ctx->out, NULL);
        if (n <= 0) {
                error("unknown option `%s'", ctx->argv[0]);
                usage_with_options(usagestr, options);
@@@ -2395,8 -2391,7 +2395,8 @@@ int setup_revisions(int argc, const cha
                                continue;
                        }
  
 -                      opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
 +                      opts = handle_revision_opt(revs, argc - i, argv + i,
 +                                                 &left, argv, opt);
                        if (opts > 0) {
                                i += opts - 1;
                                continue;
diff --combined tree-diff.c
index 34ee3b13b8f8369de4315f71f0d9ab99b1d42f22,4ffa00ae0c21bd128404a80d4363c662d82d4dbb..e6d306f69f940edd9613de0b38ffe2d9993f91ab
@@@ -239,7 -239,7 +239,7 @@@ static struct combine_diff_path *emit_p
                                                DIFF_STATUS_ADDED;
  
                        if (tpi_valid) {
-                               oid_i = tp[i].entry.oid;
+                               oid_i = &tp[i].entry.oid;
                                mode_i = tp[i].entry.mode;
                        }
                        else {
                        /* same rule as in emitthis */
                        int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);
  
-                       parents_oid[i] = tpi_valid ? tp[i].entry.oid : NULL;
+                       parents_oid[i] = tpi_valid ? &tp[i].entry.oid : NULL;
                }
  
                strbuf_add(base, path, pathlen);
@@@ -299,8 -299,7 +299,8 @@@ static void skip_uninteresting(struct t
        enum interesting match;
  
        while (t->size) {
 -              match = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
 +              match = tree_entry_interesting(opt->repo->index, &t->entry,
 +                                             base, 0, &opt->pathspec);
                if (match) {
                        if (match == all_entries_not_interesting)
                                t->size = 0;
@@@ -492,7 -491,7 +492,7 @@@ static struct combine_diff_path *ll_dif
                                                continue;
  
                                        /* diff(t,pi) != Ã¸ */
-                                       if (!oideq(t.entry.oid, tp[i].entry.oid) ||
+                                       if (!oideq(&t.entry.oid, &tp[i].entry.oid) ||
                                            (t.entry.mode != tp[i].entry.mode))
                                                continue;
  
diff --combined tree-walk.c
index 08210a4109c70364e630290a24478333aeee449a,56b951d3cb028121994c29daa1bda41969d13a45..277e3b3243011725cb24578e676309ed4444e485
@@@ -48,7 -48,8 +48,8 @@@ static int decode_tree_entry(struct tre
        /* 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;
  }
@@@ -107,7 -108,7 +108,7 @@@ static void entry_extract(struct tree_d
  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 + the_hash_algo->rawsz;
+       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;
  
@@@ -175,9 -176,11 +176,11 @@@ void setup_traverse_info(struct travers
                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)
@@@ -365,8 -368,7 +368,8 @@@ static void free_extended_entry(struct 
        }
  }
  
 -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)
                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));
                }
                if (!mask)
                        break;
 -              interesting = prune_traversal(e, info, &base, interesting);
 +              interesting = prune_traversal(istate, e, info, &base, interesting);
                if (interesting < 0)
                        break;
                if (interesting) {
@@@ -502,10 -501,10 +505,10 @@@ static int find_tree_entry(struct tree_
        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)
                if (cmp < 0)
                        break;
                if (entrylen == namelen) {
-                       oidcpy(result, oid);
+                       oidcpy(result, &oid);
                        return 0;
                }
                if (name[entrylen] != '/')
                if (!S_ISDIR(*mode))
                        break;
                if (++entrylen == namelen) {
-                       oidcpy(result, oid);
+                       oidcpy(result, &oid);
                        return 0;
                }
-               return get_tree_entry(oid, name + entrylen, result, mode);
+               return get_tree_entry(&oid, name + entrylen, result, mode);
        }
        return -1;
  }
@@@ -932,8 -931,7 +935,8 @@@ static int match_wildcard_base(const st
   * 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)
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
 -                     PATHSPEC_EXCLUDE);
 +                     PATHSPEC_EXCLUDE |
 +                     PATHSPEC_ATTR);
  
        if (!ps->nr) {
                if (!ps->recursive ||
  
                        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. */
                        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
                                    !ps_strncmp(item, match + baselen,
                                                entry->path,
                                                item->nowildcard_len - baselen))
 -                                      return entry_interesting;
 +                                      goto interesting;
                        }
  
                        continue;
@@@ -1064,7 -1055,7 +1067,7 @@@ 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;
                }
  
                /*
                    !ps_strncmp(item, match, base->buf + base_offset,
                                item->nowildcard_len)) {
                        strbuf_setlen(base, base_offset + baselen);
 -                      return entry_interesting;
 +                      goto interesting;
                }
  
                strbuf_setlen(base, base_offset + baselen);
                 */
                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 */
  }
   * 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
            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 &&
diff --combined tree-walk.h
index eefd26bb6214916f7c451d97710b5ee443e52088,08d349c54ffa413347bbf7237c84371bda0878e1..a4ad28ea5e62926b3450a4c7b093c9aa811107cd
@@@ -1,12 -1,14 +1,12 @@@
  #ifndef TREE_WALK_H
  #define TREE_WALK_H
  
- struct index_state;
- struct strbuf;
+ #include "cache.h"
  
 -struct strbuf;
 -
  struct name_entry {
-       const struct object_id *oid;
+       struct object_id oid;
        const char *path;
+       int pathlen;
        unsigned int mode;
  };
  
@@@ -20,12 -22,12 +20,12 @@@ static inline const struct object_id *t
  {
        *pathp = desc->entry.path;
        *modep = desc->entry.mode;
-       return desc->entry.oid;
+       return &desc->entry.oid;
  }
  
  static inline int tree_entry_len(const struct name_entry *ne)
  {
-       return (const char *)ne->oid - ne->path - 1;
+       return ne->pathlen;
  }
  
  /*
@@@ -49,7 -51,7 +49,7 @@@ void *fill_tree_descriptor(struct tree_
  
  struct traverse_info;
  typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
 -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);
  
  enum follow_symlinks_result {
        FOUND = 0, /* This includes out-of-tree links */
@@@ -99,9 -101,8 +99,9 @@@ enum interesting 
        all_entries_interesting = 2 /* yes, and all subsequent entries will be */
  };
  
 -extern enum interesting tree_entry_interesting(const struct name_entry *,
 -                                             struct strbuf *, int,
 -                                             const struct pathspec *ps);
 +enum interesting tree_entry_interesting(struct index_state *istate,
 +                                      const struct name_entry *,
 +                                      struct strbuf *, int,
 +                                      const struct pathspec *ps);
  
  #endif
diff --combined tree.c
index 0b5c84d0d79ff6e598d50c829df47c4dcce7d32c,7badea00094a98220b37874f60d1813abd42decb..9f0457c05acf1e4d7bd9f215dd3c710b3cb2df4f
--- 1/tree.c
--- 2/tree.c
+++ b/tree.c
@@@ -60,8 -60,7 +60,8 @@@ static int read_one_entry_quick(const s
                                  ADD_CACHE_JUST_APPEND);
  }
  
 -static int read_tree_1(struct tree *tree, struct strbuf *base,
 +static int read_tree_1(struct repository *r,
 +                     struct tree *tree, struct strbuf *base,
                       int stage, const struct pathspec *pathspec,
                       read_tree_fn_t fn, void *context)
  {
  
        while (tree_entry(&desc, &entry)) {
                if (retval != all_entries_interesting) {
 -                      retval = tree_entry_interesting(&entry, base, 0, pathspec);
 +                      retval = tree_entry_interesting(r->index, &entry,
 +                                                      base, 0, pathspec);
                        if (retval == all_entries_not_interesting)
                                break;
                        if (retval == entry_not_interesting)
                                continue;
                }
  
-               switch (fn(entry.oid, base,
+               switch (fn(&entry.oid, base,
                           entry.path, entry.mode, stage, context)) {
                case 0:
                        continue;
                }
  
                if (S_ISDIR(entry.mode))
-                       oidcpy(&oid, entry.oid);
+                       oidcpy(&oid, &entry.oid);
                else if (S_ISGITLINK(entry.mode)) {
                        struct commit *commit;
  
-                       commit = lookup_commit(r, entry.oid);
 -                      commit = lookup_commit(the_repository, &entry.oid);
++                      commit = lookup_commit(r, &entry.oid);
                        if (!commit)
                                die("Commit %s in submodule path %s%s not found",
-                                   oid_to_hex(entry.oid),
+                                   oid_to_hex(&entry.oid),
                                    base->buf, entry.path);
  
                        if (parse_commit(commit))
                                die("Invalid commit %s in submodule path %s%s",
-                                   oid_to_hex(entry.oid),
+                                   oid_to_hex(&entry.oid),
                                    base->buf, entry.path);
  
                        oidcpy(&oid, get_commit_tree_oid(commit));
                len = tree_entry_len(&entry);
                strbuf_add(base, entry.path, len);
                strbuf_addch(base, '/');
 -              retval = read_tree_1(lookup_tree(the_repository, &oid),
 +              retval = read_tree_1(r, lookup_tree(r, &oid),
                                     base, stage, pathspec,
                                     fn, context);
                strbuf_setlen(base, oldlen);
        return 0;
  }
  
 -int read_tree_recursive(struct tree *tree,
 +int read_tree_recursive(struct repository *r,
 +                      struct tree *tree,
                        const char *base, int baselen,
                        int stage, const struct pathspec *pathspec,
                        read_tree_fn_t fn, void *context)
        int ret;
  
        strbuf_add(&sb, base, baselen);
 -      ret = read_tree_1(tree, &sb, stage, pathspec, fn, context);
 +      ret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);
        strbuf_release(&sb);
        return ret;
  }
@@@ -155,8 -152,8 +155,8 @@@ static int cmp_cache_name_compare(cons
                                  ce2->name, ce2->ce_namelen, ce_stage(ce2));
  }
  
 -int read_tree(struct tree *tree, int stage, struct pathspec *match,
 -            struct index_state *istate)
 +int read_tree(struct repository *r, struct tree *tree, int stage,
 +            struct pathspec *match, struct index_state *istate)
  {
        read_tree_fn_t fn = NULL;
        int i, err;
  
        if (!fn)
                fn = read_one_entry_quick;
 -      err = read_tree_recursive(tree, "", 0, stage, match, fn, istate);
 +      err = read_tree_recursive(r, tree, "", 0, stage, match, fn, istate);
        if (fn == read_one_entry || err)
                return err;
  
diff --combined unpack-trees.c
index 94265a7df0fe78e49b720b7514d0457645f9ff05,734828fda2821fa2c2306717a44ebf95659ca988..01c2175f7c12f2bbfe6d24c73d32c60131fdc723
@@@ -294,7 -294,7 +294,7 @@@ static void load_gitmodules_file(struc
                        repo_read_gitmodules(the_repository);
                } else if (state && (ce->ce_flags & CE_UPDATE)) {
                        submodule_free(the_repository);
 -                      checkout_entry(ce, state, NULL);
 +                      checkout_entry(ce, state, NULL, NULL);
                        repo_read_gitmodules(the_repository);
                }
        }
@@@ -450,12 -450,12 +450,12 @@@ static int check_updates(struct unpack_
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
                        if (o->update && !o->dry_run) {
 -                              errs |= checkout_entry(ce, &state, NULL);
 +                              errs |= checkout_entry(ce, &state, NULL, NULL);
                        }
                }
        }
        stop_progress(&progress);
 -      errs |= finish_delayed_checkout(&state);
 +      errs |= finish_delayed_checkout(&state, NULL);
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKIN);
  
@@@ -679,7 -679,7 +679,7 @@@ static int switch_cache_bottom(struct t
  
  static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
  {
-       return name_j->oid && name_k->oid && oideq(name_j->oid, name_k->oid);
+       return !is_null_oid(&name_j->oid) && !is_null_oid(&name_k->oid) && oideq(&name_j->oid, &name_k->oid);
  }
  
  static int all_trees_same_as_cache_tree(int n, unsigned long dirmask,
@@@ -794,7 -794,6 +794,7 @@@ static int traverse_trees_recursive(in
                                    struct name_entry *names,
                                    struct traverse_info *info)
  {
 +      struct unpack_trees_options *o = info->data;
        int i, ret, bottom;
        int nr_buf = 0;
        struct tree_desc t[MAX_UNPACK_TREES];
  
        nr_entries = all_trees_same_as_cache_tree(n, dirmask, names, info);
        if (nr_entries > 0) {
 -              struct unpack_trees_options *o = info->data;
                int pos = index_pos_by_traverse_info(names, info);
  
                if (!o->merge || df_conflicts)
                else {
                        const struct object_id *oid = NULL;
                        if (dirmask & 1)
-                               oid = names[i].oid;
+                               oid = &names[i].oid;
                        buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
                }
        }
  
        bottom = switch_cache_bottom(&newinfo);
 -      ret = traverse_trees(n, t, &newinfo);
 +      ret = traverse_trees(o->src_index, n, t, &newinfo);
        restore_cache_bottom(&newinfo, bottom);
  
        for (i = 0; i < nr_buf; i++)
@@@ -981,7 -981,7 +981,7 @@@ static struct cache_entry *create_ce_en
        ce->ce_mode = create_ce_mode(n->mode);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
-       oidcpy(&ce->oid, n->oid);
+       oidcpy(&ce->oid, &n->oid);
        make_traverse_path(ce->name, info, n);
  
        return ce;
@@@ -1550,7 -1550,7 +1550,7 @@@ int unpack_trees(unsigned len, struct t
                }
  
                trace_performance_enter();
 -              ret = traverse_trees(len, t, &info);
 +              ret = traverse_trees(o->src_index, len, t, &info);
                trace_performance_leave("traverse_trees");
                if (ret < 0)
                        goto return_failed;