t/helper: add a test helper to compute hash speed
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 41aac3b7b38e76bb790da4c21c362d7d0f1d1a07..47c2fca8dc33970a1f4f041360ef954da6fa75a8 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -11,6 +11,7 @@
 #include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "object-store.h"
 #include "attr.h"
 #include "refs.h"
 #include "wildmatch.h"
@@ -19,6 +20,7 @@
 #include "varint.h"
 #include "ewah/ewok.h"
 #include "fsmonitor.h"
+#include "submodule-config.h"
 
 /*
  * Tells read_directory_recursive how a file or directory should be treated.
@@ -243,7 +245,7 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
        *size_out = 0;
        *data_out = NULL;
 
-       data = read_sha1_file(oid->hash, &type, &sz);
+       data = read_object_file(oid, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return -1;
@@ -274,12 +276,13 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
 #define DO_MATCH_DIRECTORY (1<<1)
 #define DO_MATCH_SUBMODULE (1<<2)
 
-static int match_attrs(const char *name, int namelen,
+static int match_attrs(const struct index_state *istate,
+                      const char *name, int namelen,
                       const struct pathspec_item *item)
 {
        int i;
 
-       git_check_attr(name, item->attr_check);
+       git_check_attr(istate, name, item->attr_check);
        for (i = 0; i < item->attr_match_nr; i++) {
                const char *value;
                int matched;
@@ -316,7 +319,8 @@ static int match_attrs(const char *name, int namelen,
  *
  * It returns 0 when there is no match.
  */
-static int match_pathspec_item(const struct pathspec_item *item, int prefix,
+static int match_pathspec_item(const struct index_state *istate,
+                              const struct pathspec_item *item, int prefix,
                               const char *name, int namelen, unsigned flags)
 {
        /* name/namelen has prefix cut off by caller */
@@ -356,7 +360,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
            strncmp(item->match, name - prefix, item->prefix))
                return 0;
 
-       if (item->attr_match_nr && !match_attrs(name, namelen, item))
+       if (item->attr_match_nr && !match_attrs(istate, name, namelen, item))
                return 0;
 
        /* If the match was just the prefix, we matched */
@@ -424,7 +428,8 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
  * pathspec did not match any names, which could indicate that the
  * user mistyped the nth pathspec.
  */
-static int do_match_pathspec(const struct pathspec *ps,
+static int do_match_pathspec(const struct index_state *istate,
+                            const struct pathspec *ps,
                             const char *name, int namelen,
                             int prefix, char *seen,
                             unsigned flags)
@@ -470,7 +475,7 @@ static int do_match_pathspec(const struct pathspec *ps,
                 */
                if (seen && ps->items[i].magic & PATHSPEC_EXCLUDE)
                        seen[i] = MATCHED_FNMATCH;
-               how = match_pathspec_item(ps->items+i, prefix, name,
+               how = match_pathspec_item(istate, ps->items+i, prefix, name,
                                          namelen, flags);
                if (ps->recursive &&
                    (ps->magic & PATHSPEC_MAXDEPTH) &&
@@ -494,17 +499,18 @@ static int do_match_pathspec(const struct pathspec *ps,
        return retval;
 }
 
-int match_pathspec(const struct pathspec *ps,
+int match_pathspec(const struct index_state *istate,
+                  const struct pathspec *ps,
                   const char *name, int namelen,
                   int prefix, char *seen, int is_dir)
 {
        int positive, negative;
        unsigned flags = is_dir ? DO_MATCH_DIRECTORY : 0;
-       positive = do_match_pathspec(ps, name, namelen,
+       positive = do_match_pathspec(istate, ps, name, namelen,
                                     prefix, seen, flags);
        if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
                return positive;
-       negative = do_match_pathspec(ps, name, namelen,
+       negative = do_match_pathspec(istate, ps, name, namelen,
                                     prefix, seen,
                                     flags | DO_MATCH_EXCLUDE);
        return negative ? 0 : positive;
@@ -513,11 +519,12 @@ int match_pathspec(const struct pathspec *ps,
 /**
  * Check if a submodule is a superset of the pathspec
  */
-int submodule_path_match(const struct pathspec *ps,
+int submodule_path_match(const struct index_state *istate,
+                        const struct pathspec *ps,
                         const char *submodule_name,
                         char *seen)
 {
-       int matched = do_match_pathspec(ps, submodule_name,
+       int matched = do_match_pathspec(istate, ps, submodule_name,
                                        strlen(submodule_name),
                                        0, seen,
                                        DO_MATCH_DIRECTORY |
@@ -559,7 +566,7 @@ int report_path_error(const char *ps_matched,
                if (found_dup)
                        continue;
 
-               error("pathspec '%s' did not match any file(s) known to git.",
+               error(_("pathspec '%s' did not match any file(s) known to git"),
                      pathspec->items[num].original);
                errors++;
        }
@@ -828,7 +835,7 @@ static int add_excludes(const char *fname, const char *base, int baselen,
                if (size == 0) {
                        if (oid_stat) {
                                fill_stat_data(&oid_stat->stat, &st);
-                               oidcpy(&oid_stat->oid, &empty_blob_oid);
+                               oidcpy(&oid_stat->oid, the_hash_algo->empty_blob);
                                oid_stat->valid = 1;
                        }
                        close(fd);
@@ -948,7 +955,7 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
                dir->unmanaged_exclude_files++;
        el = add_exclude_list(dir, EXC_FILE, fname);
        if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
-               die("cannot use %s as an exclude file", fname);
+               die(_("cannot use %s as an exclude file"), fname);
 }
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
@@ -1240,11 +1247,11 @@ static void prep_exclude(struct dir_struct *dir,
                    (!untracked || !untracked->valid ||
                     /*
                      * .. and .gitignore does not exist before
-                     * (i.e. null exclude_sha1). Then we can skip
+                     * (i.e. null exclude_oid). Then we can skip
                      * loading .gitignore, which would result in
                      * ENOENT anyway.
                      */
-                    !is_null_sha1(untracked->exclude_sha1))) {
+                    !is_null_oid(&untracked->exclude_oid))) {
                        /*
                         * dir->basebuf gets reused by the traversal, but we
                         * need fname to remain unchanged to ensure the src
@@ -1275,9 +1282,9 @@ static void prep_exclude(struct dir_struct *dir,
                 * order, though, if you do that.
                 */
                if (untracked &&
-                   hashcmp(oid_stat.oid.hash, untracked->exclude_sha1)) {
+                   !oideq(&oid_stat.oid, &untracked->exclude_oid)) {
                        invalidate_gitignore(dir->untracked, untracked);
-                       hashcpy(untracked->exclude_sha1, oid_stat.oid.hash);
+                       oidcpy(&untracked->exclude_oid, &oid_stat.oid);
                }
                dir->exclude_stack = stk;
                current = stk->baselen;
@@ -2229,7 +2236,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
                return NULL;
 
        if (!ident_in_untracked(dir->untracked)) {
-               warning(_("Untracked cache is disabled on this system or location."));
+               warning(_("untracked cache is disabled on this system or location"));
                return NULL;
        }
 
@@ -2241,12 +2248,12 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
 
        /* Validate $GIT_DIR/info/exclude and core.excludesfile */
        root = dir->untracked->root;
-       if (oidcmp(&dir->ss_info_exclude.oid,
+       if (!oideq(&dir->ss_info_exclude.oid,
                   &dir->untracked->ss_info_exclude.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_info_exclude = dir->ss_info_exclude;
        }
-       if (oidcmp(&dir->ss_excludes_file.oid,
+       if (!oideq(&dir->ss_excludes_file.oid,
                   &dir->untracked->ss_excludes_file.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_excludes_file = dir->ss_excludes_file;
@@ -2261,10 +2268,13 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
                   const char *path, int len, const struct pathspec *pathspec)
 {
        struct untracked_cache_dir *untracked;
-       uint64_t start = getnanotime();
 
-       if (has_symlink_leading_path(path, len))
+       trace_performance_enter();
+
+       if (has_symlink_leading_path(path, len)) {
+               trace_performance_leave("read directory %.*s", len, path);
                return dir->nr;
+       }
 
        untracked = validate_untracked_cache(dir, len, pathspec);
        if (!untracked)
@@ -2300,7 +2310,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
                dir->nr = i;
        }
 
-       trace_performance_since(start, "read directory %.*s", len, path);
+       trace_performance_leave("read directory %.*s", len, path);
        if (dir->untracked) {
                static int force_untracked_cache = -1;
                static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
@@ -2496,7 +2506,7 @@ void setup_standard_excludes(struct dir_struct *dir)
 {
        dir->exclude_per_dir = ".gitignore";
 
-       /* core.excludefile defaulting to $XDG_HOME/git/ignore */
+       /* core.excludesfile defaulting to $XDG_CONFIG_HOME/git/ignore */
        if (!excludes_file)
                excludes_file = xdg_config_home("ignore");
        if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
@@ -2622,9 +2632,10 @@ static void write_one_dir(struct untracked_cache_dir *untracked,
                stat_data_to_disk(&stat_data, &untracked->stat_data);
                strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data));
        }
-       if (!is_null_sha1(untracked->exclude_sha1)) {
+       if (!is_null_oid(&untracked->exclude_oid)) {
                ewah_set(wd->sha1_valid, i);
-               strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20);
+               strbuf_add(&wd->sb_sha1, untracked->exclude_oid.hash,
+                          the_hash_algo->rawsz);
        }
 
        intlen = encode_varint(untracked->untracked_nr, intbuf);
@@ -2825,16 +2836,16 @@ static void read_stat(size_t pos, void *cb)
        ud->valid = 1;
 }
 
-static void read_sha1(size_t pos, void *cb)
+static void read_oid(size_t pos, void *cb)
 {
        struct read_data *rd = cb;
        struct untracked_cache_dir *ud = rd->ucd[pos];
-       if (rd->data + 20 > rd->end) {
+       if (rd->data + the_hash_algo->rawsz > rd->end) {
                rd->data = rd->end + 1;
                return;
        }
-       hashcpy(ud->exclude_sha1, rd->data);
-       rd->data += 20;
+       hashcpy(ud->exclude_oid.hash, rd->data);
+       rd->data += the_hash_algo->rawsz;
 }
 
 static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
@@ -2851,7 +2862,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
        struct read_data rd;
        const unsigned char *next = data, *end = (const unsigned char *)data + sz;
        const char *ident;
-       int ident_len, len;
+       int ident_len;
+       ssize_t len;
        const char *exclude_per_dir;
 
        if (sz <= 1 || end[-1] != '\0')
@@ -2917,7 +2929,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
        ewah_each_bit(rd.check_only, set_check_only, &rd);
        rd.data = next + len;
        ewah_each_bit(rd.valid, read_stat, &rd);
-       ewah_each_bit(rd.sha1_valid, read_sha1, &rd);
+       ewah_each_bit(rd.sha1_valid, read_oid, &rd);
        next = rd.data;
 
 done:
@@ -3010,8 +3022,57 @@ void untracked_cache_add_to_index(struct index_state *istate,
        untracked_cache_invalidate_path(istate, path, 1);
 }
 
-/* Update gitfile and core.worktree setting to connect work tree and git dir */
-void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
+static void connect_wt_gitdir_in_nested(const char *sub_worktree,
+                                       const char *sub_gitdir)
+{
+       int i;
+       struct repository subrepo;
+       struct strbuf sub_wt = STRBUF_INIT;
+       struct strbuf sub_gd = STRBUF_INIT;
+
+       const struct submodule *sub;
+
+       /* If the submodule has no working tree, we can ignore it. */
+       if (repo_init(&subrepo, sub_gitdir, sub_worktree))
+               return;
+
+       if (repo_read_index(&subrepo) < 0)
+               die(_("index file corrupt in repo %s"), subrepo.gitdir);
+
+       for (i = 0; i < subrepo.index->cache_nr; i++) {
+               const struct cache_entry *ce = subrepo.index->cache[i];
+
+               if (!S_ISGITLINK(ce->ce_mode))
+                       continue;
+
+               while (i + 1 < subrepo.index->cache_nr &&
+                      !strcmp(ce->name, subrepo.index->cache[i + 1]->name))
+                       /*
+                        * Skip entries with the same name in different stages
+                        * to make sure an entry is returned only once.
+                        */
+                       i++;
+
+               sub = submodule_from_path(&subrepo, &null_oid, ce->name);
+               if (!sub || !is_submodule_active(&subrepo, ce->name))
+                       /* .gitmodules broken or inactive sub */
+                       continue;
+
+               strbuf_reset(&sub_wt);
+               strbuf_reset(&sub_gd);
+               strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path);
+               strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name);
+
+               connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1);
+       }
+       strbuf_release(&sub_wt);
+       strbuf_release(&sub_gd);
+       repo_clear(&subrepo);
+}
+
+void connect_work_tree_and_git_dir(const char *work_tree_,
+                                  const char *git_dir_,
+                                  int recurse_into_nested)
 {
        struct strbuf gitfile_sb = STRBUF_INIT;
        struct strbuf cfg_sb = STRBUF_INIT;
@@ -3041,6 +3102,10 @@ void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
        strbuf_release(&gitfile_sb);
        strbuf_release(&cfg_sb);
        strbuf_release(&rel_path);
+
+       if (recurse_into_nested)
+               connect_wt_gitdir_in_nested(work_tree, git_dir);
+
        free(work_tree);
        free(git_dir);
 }
@@ -3054,5 +3119,5 @@ void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_
                die_errno(_("could not migrate git directory from '%s' to '%s'"),
                        old_git_dir, new_git_dir);
 
-       connect_work_tree_and_git_dir(path, new_git_dir);
+       connect_work_tree_and_git_dir(path, new_git_dir, 0);
 }