dir: convert add_excludes to take an index
[gitweb.git] / unpack-trees.c
index 8333da2cc908d7ea373e3d67fbf3ac6526d9019d..f278cd23f0f365f17c42d02198d46b92b0357a1f 100644 (file)
@@ -167,7 +167,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
                _("The following working tree files would be removed by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_SUBMODULE] =
-               _("Submodule '%s' cannot checkout new HEAD");
+               _("Cannot update submodule:\n%s");
 
        opts->show_all_errors = 1;
        /* rejected paths may not have a static buffer */
@@ -604,12 +604,18 @@ static int switch_cache_bottom(struct traverse_info *info)
        return ret;
 }
 
+static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
+{
+       return name_j->oid && name_k->oid && !oidcmp(name_j->oid, name_k->oid);
+}
+
 static int traverse_trees_recursive(int n, unsigned long dirmask,
                                    unsigned long df_conflicts,
                                    struct name_entry *names,
                                    struct traverse_info *info)
 {
        int i, ret, bottom;
+       int nr_buf = 0;
        struct tree_desc t[MAX_UNPACK_TREES];
        void *buf[MAX_UNPACK_TREES];
        struct traverse_info newinfo;
@@ -626,18 +632,40 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
        newinfo.pathlen += tree_entry_len(p) + 1;
        newinfo.df_conflicts |= df_conflicts;
 
+       /*
+        * Fetch the tree from the ODB for each peer directory in the
+        * n commits.
+        *
+        * For 2- and 3-way traversals, we try to avoid hitting the
+        * ODB twice for the same OID.  This should yield a nice speed
+        * up in checkouts and merges when the commits are similar.
+        *
+        * We don't bother doing the full O(n^2) search for larger n,
+        * because wider traversals don't happen that often and we
+        * avoid the search setup.
+        *
+        * When 2 peer OIDs are the same, we just copy the tree
+        * descriptor data.  This implicitly borrows the buffer
+        * data from the earlier cell.
+        */
        for (i = 0; i < n; i++, dirmask >>= 1) {
-               const unsigned char *sha1 = NULL;
-               if (dirmask & 1)
-                       sha1 = names[i].oid->hash;
-               buf[i] = fill_tree_descriptor(t+i, sha1);
+               if (i > 0 && are_same_oid(&names[i], &names[i - 1]))
+                       t[i] = t[i - 1];
+               else if (i > 1 && are_same_oid(&names[i], &names[i - 2]))
+                       t[i] = t[i - 2];
+               else {
+                       const unsigned char *sha1 = NULL;
+                       if (dirmask & 1)
+                               sha1 = names[i].oid->hash;
+                       buf[nr_buf++] = fill_tree_descriptor(t+i, sha1);
+               }
        }
 
        bottom = switch_cache_bottom(&newinfo);
        ret = traverse_trees(n, t, &newinfo);
        restore_cache_bottom(&newinfo, bottom);
 
-       for (i = 0; i < n; i++)
+       for (i = 0; i < nr_buf; i++)
                free(buf[i]);
 
        return ret;
@@ -1040,7 +1068,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
        struct cache_entry **cache_end;
        int dtype = DT_DIR;
        int ret = is_excluded_from_list(prefix->buf, prefix->len,
-                                       basename, &dtype, el);
+                                       basename, &dtype, el, &the_index);
        int rc;
 
        strbuf_addch(prefix, '/');
@@ -1143,7 +1171,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                /* Non-directory */
                dtype = ce_to_dtype(ce);
                ret = is_excluded_from_list(ce->name, ce_namelen(ce),
-                                           name, &dtype, el);
+                                           name, &dtype, el, &the_index);
                if (ret < 0)
                        ret = defval;
                if (ret > 0)
@@ -1223,7 +1251,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                o->skip_sparse_checkout = 1;
        if (!o->skip_sparse_checkout) {
                char *sparse = git_pathdup("info/sparse-checkout");
-               if (add_excludes_from_file_to_list(sparse, "", 0, &el, 0) < 0)
+               if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0)
                        o->skip_sparse_checkout = 1;
                else
                        o->el = &el;