summaryrefslogtreecommitdiff
path: root/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'dir.c')
-rw-r--r--dir.c192
1 files changed, 129 insertions, 63 deletions
diff --git a/dir.c b/dir.c
index fd8aa7c40f..ebe5ec046e 100644
--- a/dir.c
+++ b/dir.c
@@ -59,6 +59,17 @@ void dir_init(struct dir_struct *dir)
memset(dir, 0, sizeof(*dir));
}
+struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp)
+{
+ struct dirent *e;
+
+ while ((e = readdir(dirp)) != NULL) {
+ if (!is_dot_or_dotdot(e->d_name))
+ break;
+ }
+ return e;
+}
+
int count_slashes(const char *s)
{
int cnt = 0;
@@ -306,7 +317,7 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
* [1] Only if DO_MATCH_DIRECTORY is passed; otherwise, this is NOT a match.
* [2] Only if DO_MATCH_LEADING_PATHSPEC is passed; otherwise, not a match.
*/
-static int match_pathspec_item(const struct index_state *istate,
+static int match_pathspec_item(struct index_state *istate,
const struct pathspec_item *item, int prefix,
const char *name, int namelen, unsigned flags)
{
@@ -429,7 +440,7 @@ static int match_pathspec_item(const struct index_state *istate,
* pathspec did not match any names, which could indicate that the
* user mistyped the nth pathspec.
*/
-static int do_match_pathspec(const struct index_state *istate,
+static int do_match_pathspec(struct index_state *istate,
const struct pathspec *ps,
const char *name, int namelen,
int prefix, char *seen,
@@ -500,7 +511,7 @@ static int do_match_pathspec(const struct index_state *istate,
return retval;
}
-static int match_pathspec_with_flags(const struct index_state *istate,
+static int match_pathspec_with_flags(struct index_state *istate,
const struct pathspec *ps,
const char *name, int namelen,
int prefix, char *seen, unsigned flags)
@@ -516,7 +527,7 @@ static int match_pathspec_with_flags(const struct index_state *istate,
return negative ? 0 : positive;
}
-int match_pathspec(const struct index_state *istate,
+int match_pathspec(struct index_state *istate,
const struct pathspec *ps,
const char *name, int namelen,
int prefix, char *seen, int is_dir)
@@ -529,7 +540,7 @@ int match_pathspec(const struct index_state *istate,
/**
* Check if a submodule is a superset of the pathspec
*/
-int submodule_path_match(const struct index_state *istate,
+int submodule_path_match(struct index_state *istate,
const struct pathspec *ps,
const char *submodule_name,
char *seen)
@@ -892,7 +903,7 @@ void add_pattern(const char *string, const char *base,
add_pattern_to_hashsets(pl, pattern);
}
-static int read_skip_worktree_file_from_index(const struct index_state *istate,
+static int read_skip_worktree_file_from_index(struct index_state *istate,
const char *path,
size_t *size_out, char **data_out,
struct oid_stat *oid_stat)
@@ -1035,6 +1046,9 @@ static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl);
+/* Flags for add_patterns() */
+#define PATTERN_NOFOLLOW (1<<0)
+
/*
* Given a file with name "fname", read it (either from disk, or from
* an index if 'istate' is non-null), parse it and store the
@@ -1046,7 +1060,7 @@ static int add_patterns_from_buffer(char *buf, size_t size,
*/
static int add_patterns(const char *fname, const char *base, int baselen,
struct pattern_list *pl, struct index_state *istate,
- struct oid_stat *oid_stat)
+ unsigned flags, struct oid_stat *oid_stat)
{
struct stat st;
int r;
@@ -1054,7 +1068,11 @@ static int add_patterns(const char *fname, const char *base, int baselen,
size_t size = 0;
char *buf;
- fd = open(fname, O_RDONLY);
+ if (flags & PATTERN_NOFOLLOW)
+ fd = open_nofollow(fname, O_RDONLY);
+ else
+ fd = open(fname, O_RDONLY);
+
if (fd < 0 || fstat(fd, &st) < 0) {
if (fd < 0)
warn_on_fopen_errors(fname);
@@ -1143,9 +1161,10 @@ static int add_patterns_from_buffer(char *buf, size_t size,
int add_patterns_from_file_to_list(const char *fname, const char *base,
int baselen, struct pattern_list *pl,
- struct index_state *istate)
+ struct index_state *istate,
+ unsigned flags)
{
- return add_patterns(fname, base, baselen, pl, istate, NULL);
+ return add_patterns(fname, base, baselen, pl, istate, flags, NULL);
}
int add_patterns_from_blob_to_list(
@@ -1194,7 +1213,7 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
if (!dir->untracked)
dir->unmanaged_exclude_files++;
pl = add_pattern_list(dir, EXC_FILE, fname);
- if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0)
+ if (add_patterns(fname, "", 0, pl, NULL, 0, oid_stat) < 0)
die(_("cannot use %s as an exclude file"), fname);
}
@@ -1488,7 +1507,7 @@ static void prep_exclude(struct dir_struct *dir,
const char *cp;
struct oid_stat oid_stat;
- stk = xcalloc(1, sizeof(*stk));
+ CALLOC_ARRAY(stk, 1);
if (current < 0) {
cp = base;
current = 0;
@@ -1558,6 +1577,7 @@ static void prep_exclude(struct dir_struct *dir,
strbuf_addstr(&sb, dir->exclude_per_dir);
pl->src = strbuf_detach(&sb, NULL);
add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
+ PATTERN_NOFOLLOW,
untracked ? &oid_stat : NULL);
}
/*
@@ -1740,13 +1760,13 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,
* Case 3: if we didn't have it in the index previously, we
* have a few sub-cases:
*
- * (a) if "show_other_directories" is true, we show it as
- * just a directory, unless "hide_empty_directories" is
+ * (a) if DIR_SHOW_OTHER_DIRECTORIES flag is set, we show it as
+ * just a directory, unless DIR_HIDE_EMPTY_DIRECTORIES is
* also true, in which case we need to check if it contains any
* untracked and / or ignored files.
- * (b) if it looks like a git directory, and we don't have
- * 'no_gitlinks' set we treat it as a gitlink, and show it
- * as a directory.
+ * (b) if it looks like a git directory and we don't have the
+ * DIR_NO_GITLINKS flag, then we treat it as a gitlink, and
+ * show it as a directory.
* (c) otherwise, we recurse into it.
*/
static enum path_treatment treat_directory(struct dir_struct *dir,
@@ -1834,7 +1854,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
return path_recurse;
}
- /* This is the "show_other_directories" case */
+ assert(dir->flags & DIR_SHOW_OTHER_DIRECTORIES);
/*
* If we have a pathspec which could match something _below_ this
@@ -1845,27 +1865,42 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
if (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC)
return path_recurse;
+ /* Special cases for where this directory is excluded/ignored */
+ if (excluded) {
+ /*
+ * If DIR_SHOW_OTHER_DIRECTORIES is set and we're not
+ * hiding empty directories, there is no need to
+ * recurse into an ignored directory.
+ */
+ if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
+ return path_excluded;
+
+ /*
+ * Even if we are hiding empty directories, we can still avoid
+ * recursing into ignored directories for DIR_SHOW_IGNORED_TOO
+ * if DIR_SHOW_IGNORED_TOO_MODE_MATCHING is also set.
+ */
+ if ((dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
+ return path_excluded;
+ }
+
/*
- * Other than the path_recurse case immediately above, we only need
- * to recurse into untracked/ignored directories if either of the
- * following bits is set:
- * - DIR_SHOW_IGNORED_TOO (because then we need to determine if
- * there are ignored entries below)
+ * Other than the path_recurse case above, we only need to
+ * recurse into untracked directories if any of the following
+ * bits is set:
+ * - DIR_SHOW_IGNORED (because then we need to determine if
+ * there are ignored entries below)
+ * - DIR_SHOW_IGNORED_TOO (same as above)
* - DIR_HIDE_EMPTY_DIRECTORIES (because we have to determine if
* the directory is empty)
*/
- if (!(dir->flags & (DIR_SHOW_IGNORED_TOO | DIR_HIDE_EMPTY_DIRECTORIES)))
- return excluded ? path_excluded : path_untracked;
-
- /*
- * ...and even if DIR_SHOW_IGNORED_TOO is set, we can still avoid
- * recursing into ignored directories if the path is excluded and
- * DIR_SHOW_IGNORED_TOO_MODE_MATCHING is also set.
- */
- if (excluded &&
- (dir->flags & DIR_SHOW_IGNORED_TOO) &&
- (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
- return path_excluded;
+ if (!excluded &&
+ !(dir->flags & (DIR_SHOW_IGNORED |
+ DIR_SHOW_IGNORED_TOO |
+ DIR_HIDE_EMPTY_DIRECTORIES))) {
+ return path_untracked;
+ }
/*
* Even if we don't want to know all the paths under an untracked or
@@ -2317,7 +2352,7 @@ static int read_cached_dir(struct cached_dir *cdir)
struct dirent *de;
if (cdir->fdir) {
- de = readdir(cdir->fdir);
+ de = readdir_skip_dot_and_dotdot(cdir->fdir);
if (!de) {
cdir->d_name = NULL;
cdir->d_type = DT_UNKNOWN;
@@ -2431,6 +2466,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only))
goto out;
+ dir->visited_directories++;
if (untracked)
untracked->check_only = !!check_only;
@@ -2439,6 +2475,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
/* check how the file or directory should be treated */
state = treat_path(dir, untracked, &cdir, istate, &path,
baselen, pathspec);
+ dir->visited_paths++;
if (state > dir_state)
dir_state = state;
@@ -2751,15 +2788,53 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
return root;
}
+static void emit_traversal_statistics(struct dir_struct *dir,
+ struct repository *repo,
+ const char *path,
+ int path_len)
+{
+ if (!trace2_is_enabled())
+ return;
+
+ if (!path_len) {
+ trace2_data_string("read_directory", repo, "path", "");
+ } else {
+ struct strbuf tmp = STRBUF_INIT;
+ strbuf_add(&tmp, path, path_len);
+ trace2_data_string("read_directory", repo, "path", tmp.buf);
+ strbuf_release(&tmp);
+ }
+
+ trace2_data_intmax("read_directory", repo,
+ "directories-visited", dir->visited_directories);
+ trace2_data_intmax("read_directory", repo,
+ "paths-visited", dir->visited_paths);
+
+ if (!dir->untracked)
+ return;
+ trace2_data_intmax("read_directory", repo,
+ "node-creation", dir->untracked->dir_created);
+ trace2_data_intmax("read_directory", repo,
+ "gitignore-invalidation",
+ dir->untracked->gitignore_invalidated);
+ trace2_data_intmax("read_directory", repo,
+ "directory-invalidation",
+ dir->untracked->dir_invalidated);
+ trace2_data_intmax("read_directory", repo,
+ "opendir", dir->untracked->dir_opened);
+}
+
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;
- trace_performance_enter();
+ trace2_region_enter("dir", "read_directory", istate->repo);
+ dir->visited_paths = 0;
+ dir->visited_directories = 0;
if (has_symlink_leading_path(path, len)) {
- trace_performance_leave("read directory %.*s", len, path);
+ trace2_region_leave("dir", "read_directory", istate->repo);
return dir->nr;
}
@@ -2775,23 +2850,15 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
QSORT(dir->entries, dir->nr, cmp_dir_entry);
QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
- trace_performance_leave("read directory %.*s", len, path);
+ emit_traversal_statistics(dir, istate->repo, path, len);
+
+ trace2_region_leave("dir", "read_directory", istate->repo);
if (dir->untracked) {
static int force_untracked_cache = -1;
- static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
if (force_untracked_cache < 0)
force_untracked_cache =
git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
- trace_printf_key(&trace_untracked_stats,
- "node creation: %u\n"
- "gitignore invalidation: %u\n"
- "directory invalidation: %u\n"
- "opendir: %u\n",
- dir->untracked->dir_created,
- dir->untracked->gitignore_invalidated,
- dir->untracked->dir_invalidated,
- dir->untracked->dir_opened);
if (force_untracked_cache &&
dir->untracked == istate->untracked &&
(dir->untracked->dir_opened ||
@@ -2802,6 +2869,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
FREE_AND_NULL(dir->untracked);
}
}
+
return dir->nr;
}
@@ -2883,11 +2951,9 @@ int is_empty_dir(const char *path)
if (!dir)
return 0;
- while ((e = readdir(dir)) != NULL)
- if (!is_dot_or_dotdot(e->d_name)) {
- ret = 0;
- break;
- }
+ e = readdir_skip_dot_and_dotdot(dir);
+ if (e)
+ ret = 0;
closedir(dir);
return ret;
@@ -2927,10 +2993,8 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
strbuf_complete(path, '/');
len = path->len;
- while ((e = readdir(dir)) != NULL) {
+ while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
struct stat st;
- if (is_dot_or_dotdot(e->d_name))
- continue;
strbuf_setlen(path, len);
strbuf_addstr(path, e->d_name);
@@ -3006,7 +3070,7 @@ int get_sparse_checkout_patterns(struct pattern_list *pl)
char *sparse_filename = get_sparse_checkout_filename();
pl->use_cone_patterns = core_sparse_checkout_cone;
- res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL);
+ res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL, 0);
free(sparse_filename);
return res;
@@ -3162,7 +3226,7 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra
int varint_len;
const unsigned hashsz = the_hash_algo->rawsz;
- ouc = xcalloc(1, sizeof(*ouc));
+ CALLOC_ARRAY(ouc, 1);
stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
ouc->dir_flags = htonl(untracked->dir_flags);
@@ -3335,7 +3399,7 @@ static void read_oid(size_t pos, void *cb)
rd->data = rd->end + 1;
return;
}
- hashcpy(ud->exclude_oid.hash, rd->data);
+ oidread(&ud->exclude_oid, rd->data);
rd->data += the_hash_algo->rawsz;
}
@@ -3343,7 +3407,7 @@ static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
const unsigned char *sha1)
{
stat_data_from_disk(&oid_stat->stat, data);
- hashcpy(oid_stat->oid.hash, sha1);
+ oidread(&oid_stat->oid, sha1);
oid_stat->valid = 1;
}
@@ -3373,7 +3437,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
if (next + exclude_per_dir_offset + 1 > end)
return NULL;
- uc = xcalloc(1, sizeof(*uc));
+ CALLOC_ARRAY(uc, 1);
strbuf_init(&uc->ident, ident_len);
strbuf_add(&uc->ident, ident, ident_len);
load_oid_stat(&uc->ss_info_exclude,
@@ -3533,6 +3597,8 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree,
if (repo_read_index(&subrepo) < 0)
die(_("index file corrupt in repo %s"), subrepo.gitdir);
+ /* TODO: audit for interaction with sparse-index. */
+ ensure_full_index(subrepo.index);
for (i = 0; i < subrepo.index->cache_nr; i++) {
const struct cache_entry *ce = subrepo.index->cache[i];
@@ -3547,7 +3613,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree,
*/
i++;
- sub = submodule_from_path(&subrepo, &null_oid, ce->name);
+ sub = submodule_from_path(&subrepo, null_oid(), ce->name);
if (!sub || !is_submodule_active(&subrepo, ce->name))
/* .gitmodules broken or inactive sub */
continue;