]> git.proxmox.com Git - libgit2.git/blobdiff - src/iterator.c
Improvements to ignore performance on Windows.
[libgit2.git] / src / iterator.c
index c664f17cd25cc50da1c2de30411cc3470f01e133..8bab1aab0ac7426f126e72d113be1c16e8409175 100644 (file)
@@ -336,7 +336,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
 {
        int error = 0;
        tree_iterator_frame *head = ti->head, *tf = NULL;
-       size_t i, n_entries = 0;
+       size_t i, n_entries = 0, alloclen;
 
        if (head->current >= head->n_entries || !head->entries[head->current]->tree)
                return GIT_ITEROVER;
@@ -344,8 +344,10 @@ static int tree_iterator__push_frame(tree_iterator *ti)
        for (i = head->current; i < head->next; ++i)
                n_entries += git_tree_entrycount(head->entries[i]->tree);
 
-       tf = git__calloc(sizeof(tree_iterator_frame) +
-               n_entries * sizeof(tree_iterator_entry *), 1);
+       GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, sizeof(tree_iterator_entry *), n_entries);
+       GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, sizeof(tree_iterator_frame));
+
+       tf = git__calloc(1, alloclen);
        GITERR_CHECK_ALLOC(tf);
 
        tf->n_entries = n_entries;
@@ -1268,6 +1270,16 @@ typedef struct {
        fs_iterator fi;
        git_ignores ignores;
        int is_ignored;
+
+       /*
+        * We may have a tree or the index+snapshot to compare against
+        * when checking for submodules.
+        */
+       git_tree *tree;
+       git_index *index;
+       git_vector index_snapshot;
+       git_vector_cmp entry_srch;
+
 } workdir_iterator;
 
 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
@@ -1289,6 +1301,59 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
        return (len == 4 || path->ptr[len - 5] == '/');
 }
 
+/**
+ * Figure out if an entry is a submodule.
+ *
+ * We consider it a submodule if the path is listed as a submodule in
+ * either the tree or the index.
+ */
+static int is_submodule(workdir_iterator *wi, git_path_with_stat *ie)
+{
+       int error, is_submodule = 0;
+
+       if (wi->tree) {
+               git_tree_entry *e;
+
+               /* remove the trailing slash for finding */
+               ie->path[ie->path_len-1] = '\0';
+               error = git_tree_entry_bypath(&e, wi->tree, ie->path);
+               ie->path[ie->path_len-1] = '/';
+               if (error < 0 && error != GIT_ENOTFOUND)
+                       return 0;
+               if (!error) {
+                       is_submodule = e->attr == GIT_FILEMODE_COMMIT;
+                       git_tree_entry_free(e);
+               }
+       }
+
+       if (!is_submodule && wi->index) {
+               git_index_entry *e;
+               size_t pos;
+
+               error = git_index_snapshot_find(&pos, &wi->index_snapshot, wi->entry_srch, ie->path, ie->path_len-1, 0);
+               if (error < 0 && error != GIT_ENOTFOUND)
+                       return 0;
+
+               if (!error) {
+                       e = git_vector_get(&wi->index_snapshot, pos);
+
+                       is_submodule = e->mode == GIT_FILEMODE_COMMIT;
+               }
+       }
+
+       return is_submodule;
+}
+
+GIT_INLINE(git_dir_flag) git_entry__dir_flag(git_index_entry *entry) {
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+       return (entry && entry->mode)
+               ? S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE
+               : GIT_DIR_FLAG_UNKNOWN;
+#else
+       return GIT_DIR_FLAG_UNKNOWN;
+#endif
+}
+
 static int workdir_iterator__enter_dir(fs_iterator *fi)
 {
        workdir_iterator *wi = (workdir_iterator *)fi;
@@ -1297,9 +1362,10 @@ static int workdir_iterator__enter_dir(fs_iterator *fi)
        git_path_with_stat *entry;
        bool found_submodules = false;
 
+       git_dir_flag dir_flag = git_entry__dir_flag(&fi->entry);
+
        /* check if this directory is ignored */
-       if (git_ignore__lookup(
-                       &ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len) < 0) {
+       if (git_ignore__lookup(&ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len, dir_flag) < 0) {
                giterr_clear();
                ff->is_ignored = GIT_IGNORE_NOTFOUND;
        }
@@ -1321,7 +1387,7 @@ static int workdir_iterator__enter_dir(fs_iterator *fi)
                if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path))
                        continue;
 
-               if (git_submodule__is_submodule(fi->base.repo, entry->path)) {
+               if (is_submodule(wi, entry)) {
                        entry->st.st_mode = GIT_FILEMODE_COMMIT;
                        entry->path_len--;
                        entry->path[entry->path_len] = '\0';
@@ -1363,6 +1429,9 @@ static int workdir_iterator__update_entry(fs_iterator *fi)
 static void workdir_iterator__free(git_iterator *self)
 {
        workdir_iterator *wi = (workdir_iterator *)self;
+       if (wi->index)
+               git_index_snapshot_release(&wi->index_snapshot, wi->index);
+       git_tree_free(wi->tree);
        fs_iterator__free(self);
        git_ignore__free(&wi->ignores);
 }
@@ -1371,6 +1440,8 @@ int git_iterator_for_workdir_ext(
        git_iterator **out,
        git_repository *repo,
        const char *repo_workdir,
+       git_index *index,
+       git_tree *tree,
        git_iterator_flag_t flags,
        const char *start,
        const char *end)
@@ -1402,6 +1473,18 @@ int git_iterator_for_workdir_ext(
                return error;
        }
 
+       if (tree && (error = git_object_dup((git_object **)&wi->tree, (git_object *)tree)) < 0)
+               return error;
+
+       wi->index = index;
+       if (index && (error = git_index_snapshot_new(&wi->index_snapshot, index)) < 0) {
+               git_iterator_free((git_iterator *)wi);
+               return error;
+       }
+       wi->entry_srch = iterator__ignore_case(wi) ?
+               git_index_entry_isrch : git_index_entry_srch;
+
+
        /* try to look up precompose and set flag if appropriate */
        if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
                giterr_clear();
@@ -1411,7 +1494,6 @@ int git_iterator_for_workdir_ext(
        return fs_iterator__initialize(out, &wi->fi, repo_workdir);
 }
 
-
 void git_iterator_free(git_iterator *iter)
 {
        if (iter == NULL)
@@ -1502,8 +1584,9 @@ int git_iterator_current_parent_tree(
 
 static void workdir_iterator_update_is_ignored(workdir_iterator *wi)
 {
-       if (git_ignore__lookup(
-                       &wi->is_ignored, &wi->ignores, wi->fi.entry.path) < 0) {
+       git_dir_flag dir_flag = git_entry__dir_flag(&wi->fi.entry);
+
+       if (git_ignore__lookup(&wi->is_ignored, &wi->ignores, wi->fi.entry.path, dir_flag) < 0) {
                giterr_clear();
                wi->is_ignored = GIT_IGNORE_NOTFOUND;
        }