зеркало из https://github.com/microsoft/git.git
Merge branch 'kb/status-ignored-optim-2'
Fixes a handful of issues in the code to traverse working tree to find untracked and/or ignored files, cleans up and optimizes the codepath in general. * kb/status-ignored-optim-2: dir.c: git-status --ignored: don't scan the work tree twice dir.c: git-status --ignored: don't scan the work tree three times dir.c: git-status: avoid is_excluded checks for tracked files dir.c: replace is_path_excluded with now equivalent is_excluded API dir.c: unify is_excluded and is_path_excluded APIs dir.c: move prep_exclude dir.c: factor out parts of last_exclude_matching for later reuse dir.c: git-clean -d -X: don't delete tracked directories dir.c: make 'git-status --ignored' work within leading directories dir.c: git-status --ignored: don't list empty directories as ignored dir.c: git-ls-files --directories: don't hide empty directories dir.c: git-status --ignored: don't list empty ignored directories dir.c: git-status --ignored: don't list files in ignored directories dir.c: git-status --ignored: don't drop ignored directories
This commit is contained in:
Коммит
7093d2c0dd
|
@ -22,12 +22,23 @@ The notable options are:
|
|||
|
||||
`flags`::
|
||||
|
||||
A bit-field of options:
|
||||
A bit-field of options (the `*IGNORED*` flags are mutually exclusive):
|
||||
|
||||
`DIR_SHOW_IGNORED`:::
|
||||
|
||||
The traversal is for finding just ignored files, not unignored
|
||||
files.
|
||||
Return just ignored files in `entries[]`, not untracked files.
|
||||
|
||||
`DIR_SHOW_IGNORED_TOO`:::
|
||||
|
||||
Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
|
||||
in addition to untracked files in `entries[]`.
|
||||
|
||||
`DIR_COLLECT_IGNORED`:::
|
||||
|
||||
Special mode for git-add. Return ignored files in `ignored[]` and
|
||||
untracked files in `entries[]`. Only returns ignored files that match
|
||||
pathspec exactly (no wildcards). Does not recurse into ignored
|
||||
directories.
|
||||
|
||||
`DIR_SHOW_OTHER_DIRECTORIES`:::
|
||||
|
||||
|
@ -57,6 +68,14 @@ The result of the enumeration is left in these fields:
|
|||
|
||||
Internal use; keeps track of allocation of `entries[]` array.
|
||||
|
||||
`ignored[]`::
|
||||
|
||||
An array of `struct dir_entry`, used for ignored paths with the
|
||||
`DIR_SHOW_IGNORED_TOO` and `DIR_COLLECT_IGNORED` flags.
|
||||
|
||||
`ignored_nr`::
|
||||
|
||||
The number of members in `ignored[]` array.
|
||||
|
||||
Calling sequence
|
||||
----------------
|
||||
|
|
|
@ -545,9 +545,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||
|
||||
if (pathspec) {
|
||||
int i;
|
||||
struct path_exclude_check check;
|
||||
|
||||
path_exclude_check_init(&check, &dir);
|
||||
if (!seen)
|
||||
seen = find_pathspecs_matching_against_index(pathspec);
|
||||
for (i = 0; pathspec[i]; i++) {
|
||||
|
@ -555,7 +553,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||
&& !file_exists(pathspec[i])) {
|
||||
if (ignore_missing) {
|
||||
int dtype = DT_UNKNOWN;
|
||||
if (is_path_excluded(&check, pathspec[i], -1, &dtype))
|
||||
if (is_excluded(&dir, pathspec[i], &dtype))
|
||||
dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
|
||||
} else
|
||||
die(_("pathspec '%s' did not match any files"),
|
||||
|
@ -563,7 +561,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
}
|
||||
free(seen);
|
||||
path_exclude_check_clear(&check);
|
||||
}
|
||||
|
||||
plug_bulk_checkin();
|
||||
|
|
|
@ -59,7 +59,6 @@ static int check_ignore(const char *prefix, const char **pathspec)
|
|||
const char *path, *full_path;
|
||||
char *seen;
|
||||
int num_ignored = 0, dtype = DT_UNKNOWN, i;
|
||||
struct path_exclude_check check;
|
||||
struct exclude *exclude;
|
||||
|
||||
/* read_cache() is only necessary so we can watch out for submodules. */
|
||||
|
@ -67,7 +66,6 @@ static int check_ignore(const char *prefix, const char **pathspec)
|
|||
die(_("index file corrupt"));
|
||||
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
dir.flags |= DIR_COLLECT_IGNORED;
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
if (!pathspec || !*pathspec) {
|
||||
|
@ -76,7 +74,6 @@ static int check_ignore(const char *prefix, const char **pathspec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
path_exclude_check_init(&check, &dir);
|
||||
/*
|
||||
* look for pathspecs matching entries in the index, since these
|
||||
* should not be ignored, in order to be consistent with
|
||||
|
@ -90,8 +87,7 @@ static int check_ignore(const char *prefix, const char **pathspec)
|
|||
full_path = check_path_for_gitlink(full_path);
|
||||
die_if_path_beyond_symlink(full_path, prefix);
|
||||
if (!seen[i]) {
|
||||
exclude = last_exclude_matching_path(&check, full_path,
|
||||
-1, &dtype);
|
||||
exclude = last_exclude_matching(&dir, full_path, &dtype);
|
||||
if (exclude) {
|
||||
if (!quiet)
|
||||
output_exclude(path, exclude);
|
||||
|
@ -101,7 +97,6 @@ static int check_ignore(const char *prefix, const char **pathspec)
|
|||
}
|
||||
free(seen);
|
||||
clear_directory(&dir);
|
||||
path_exclude_check_clear(&check);
|
||||
|
||||
return num_ignored;
|
||||
}
|
||||
|
|
|
@ -201,19 +201,15 @@ static void show_ru_info(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)
|
||||
static int ce_excluded(struct dir_struct *dir, struct cache_entry *ce)
|
||||
{
|
||||
int dtype = ce_to_dtype(ce);
|
||||
return is_path_excluded(check, ce->name, ce_namelen(ce), &dtype);
|
||||
return is_excluded(dir, ce->name, &dtype);
|
||||
}
|
||||
|
||||
static void show_files(struct dir_struct *dir)
|
||||
{
|
||||
int i;
|
||||
struct path_exclude_check check;
|
||||
|
||||
if ((dir->flags & DIR_SHOW_IGNORED))
|
||||
path_exclude_check_init(&check, dir);
|
||||
|
||||
/* For cached/deleted files we don't need to even do the readdir */
|
||||
if (show_others || show_killed) {
|
||||
|
@ -227,7 +223,7 @@ static void show_files(struct dir_struct *dir)
|
|||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if ((dir->flags & DIR_SHOW_IGNORED) &&
|
||||
!ce_excluded(&check, ce))
|
||||
!ce_excluded(dir, ce))
|
||||
continue;
|
||||
if (show_unmerged && !ce_stage(ce))
|
||||
continue;
|
||||
|
@ -243,7 +239,7 @@ static void show_files(struct dir_struct *dir)
|
|||
struct stat st;
|
||||
int err;
|
||||
if ((dir->flags & DIR_SHOW_IGNORED) &&
|
||||
!ce_excluded(&check, ce))
|
||||
!ce_excluded(dir, ce))
|
||||
continue;
|
||||
if (ce->ce_flags & CE_UPDATE)
|
||||
continue;
|
||||
|
@ -256,9 +252,6 @@ static void show_files(struct dir_struct *dir)
|
|||
show_ce_entry(tag_modified, ce);
|
||||
}
|
||||
}
|
||||
|
||||
if ((dir->flags & DIR_SHOW_IGNORED))
|
||||
path_exclude_check_clear(&check);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
525
dir.c
525
dir.c
|
@ -17,7 +17,21 @@ struct path_simplify {
|
|||
const char *path;
|
||||
};
|
||||
|
||||
static int read_directory_recursive(struct dir_struct *dir, const char *path, int len,
|
||||
/*
|
||||
* Tells read_directory_recursive how a file or directory should be treated.
|
||||
* Values are ordered by significance, e.g. if a directory contains both
|
||||
* excluded and untracked files, it is listed as untracked because
|
||||
* path_untracked > path_excluded.
|
||||
*/
|
||||
enum path_treatment {
|
||||
path_none = 0,
|
||||
path_recurse,
|
||||
path_excluded,
|
||||
path_untracked
|
||||
};
|
||||
|
||||
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
||||
const char *path, int len,
|
||||
int check_only, const struct path_simplify *simplify);
|
||||
static int get_dtype(struct dirent *de, const char *path, int len);
|
||||
|
||||
|
@ -578,78 +592,6 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
|
|||
die("cannot use %s as an exclude file", fname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the per-directory exclude list for the substring of base
|
||||
* which has a char length of baselen.
|
||||
*/
|
||||
static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
{
|
||||
struct exclude_list_group *group;
|
||||
struct exclude_list *el;
|
||||
struct exclude_stack *stk = NULL;
|
||||
int current;
|
||||
|
||||
if ((!dir->exclude_per_dir) ||
|
||||
(baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
|
||||
return; /* too long a path -- ignore */
|
||||
|
||||
group = &dir->exclude_list_group[EXC_DIRS];
|
||||
|
||||
/* Pop the exclude lists from the EXCL_DIRS exclude_list_group
|
||||
* which originate from directories not in the prefix of the
|
||||
* path being checked. */
|
||||
while ((stk = dir->exclude_stack) != NULL) {
|
||||
if (stk->baselen <= baselen &&
|
||||
!strncmp(dir->basebuf, base, stk->baselen))
|
||||
break;
|
||||
el = &group->el[dir->exclude_stack->exclude_ix];
|
||||
dir->exclude_stack = stk->prev;
|
||||
free((char *)el->src); /* see strdup() below */
|
||||
clear_exclude_list(el);
|
||||
free(stk);
|
||||
group->nr--;
|
||||
}
|
||||
|
||||
/* Read from the parent directories and push them down. */
|
||||
current = stk ? stk->baselen : -1;
|
||||
while (current < baselen) {
|
||||
struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
|
||||
const char *cp;
|
||||
|
||||
if (current < 0) {
|
||||
cp = base;
|
||||
current = 0;
|
||||
}
|
||||
else {
|
||||
cp = strchr(base + current + 1, '/');
|
||||
if (!cp)
|
||||
die("oops in prep_exclude");
|
||||
cp++;
|
||||
}
|
||||
stk->prev = dir->exclude_stack;
|
||||
stk->baselen = cp - base;
|
||||
memcpy(dir->basebuf + current, base + current,
|
||||
stk->baselen - current);
|
||||
strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
|
||||
/*
|
||||
* dir->basebuf gets reused by the traversal, but we
|
||||
* need fname to remain unchanged to ensure the src
|
||||
* member of each struct exclude correctly
|
||||
* back-references its source file. Other invocations
|
||||
* of add_exclude_list provide stable strings, so we
|
||||
* strdup() and free() here in the caller.
|
||||
*/
|
||||
el = add_exclude_list(dir, EXC_DIRS, strdup(dir->basebuf));
|
||||
stk->exclude_ix = group->nr - 1;
|
||||
add_excludes_from_file_to_list(dir->basebuf,
|
||||
dir->basebuf, stk->baselen,
|
||||
el, 1);
|
||||
dir->exclude_stack = stk;
|
||||
current = stk->baselen;
|
||||
}
|
||||
dir->basebuf[baselen] = '\0';
|
||||
}
|
||||
|
||||
int match_basename(const char *basename, int basenamelen,
|
||||
const char *pattern, int prefix, int patternlen,
|
||||
int flags)
|
||||
|
@ -795,25 +737,13 @@ int is_excluded_from_list(const char *pathname,
|
|||
return -1; /* undecided */
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the exclude lists for the directory containing pathname, then
|
||||
* scans all exclude lists to determine whether pathname is excluded.
|
||||
* Returns the exclude_list element which matched, or NULL for
|
||||
* undecided.
|
||||
*/
|
||||
static struct exclude *last_exclude_matching(struct dir_struct *dir,
|
||||
const char *pathname,
|
||||
int *dtype_p)
|
||||
static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir,
|
||||
const char *pathname, int pathlen, const char *basename,
|
||||
int *dtype_p)
|
||||
{
|
||||
int pathlen = strlen(pathname);
|
||||
int i, j;
|
||||
struct exclude_list_group *group;
|
||||
struct exclude *exclude;
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
|
||||
prep_exclude(dir, pathname, basename-pathname);
|
||||
|
||||
for (i = EXC_CMDL; i <= EXC_FILE; i++) {
|
||||
group = &dir->exclude_list_group[i];
|
||||
for (j = group->nr - 1; j >= 0; j--) {
|
||||
|
@ -827,12 +757,129 @@ static struct exclude *last_exclude_matching(struct dir_struct *dir,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the per-directory exclude list for the substring of base
|
||||
* which has a char length of baselen.
|
||||
*/
|
||||
static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
{
|
||||
struct exclude_list_group *group;
|
||||
struct exclude_list *el;
|
||||
struct exclude_stack *stk = NULL;
|
||||
int current;
|
||||
|
||||
group = &dir->exclude_list_group[EXC_DIRS];
|
||||
|
||||
/* Pop the exclude lists from the EXCL_DIRS exclude_list_group
|
||||
* which originate from directories not in the prefix of the
|
||||
* path being checked. */
|
||||
while ((stk = dir->exclude_stack) != NULL) {
|
||||
if (stk->baselen <= baselen &&
|
||||
!strncmp(dir->basebuf, base, stk->baselen))
|
||||
break;
|
||||
el = &group->el[dir->exclude_stack->exclude_ix];
|
||||
dir->exclude_stack = stk->prev;
|
||||
dir->exclude = NULL;
|
||||
free((char *)el->src); /* see strdup() below */
|
||||
clear_exclude_list(el);
|
||||
free(stk);
|
||||
group->nr--;
|
||||
}
|
||||
|
||||
/* Skip traversing into sub directories if the parent is excluded */
|
||||
if (dir->exclude)
|
||||
return;
|
||||
|
||||
/* Read from the parent directories and push them down. */
|
||||
current = stk ? stk->baselen : -1;
|
||||
while (current < baselen) {
|
||||
struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
|
||||
const char *cp;
|
||||
|
||||
if (current < 0) {
|
||||
cp = base;
|
||||
current = 0;
|
||||
}
|
||||
else {
|
||||
cp = strchr(base + current + 1, '/');
|
||||
if (!cp)
|
||||
die("oops in prep_exclude");
|
||||
cp++;
|
||||
}
|
||||
stk->prev = dir->exclude_stack;
|
||||
stk->baselen = cp - base;
|
||||
stk->exclude_ix = group->nr;
|
||||
el = add_exclude_list(dir, EXC_DIRS, NULL);
|
||||
memcpy(dir->basebuf + current, base + current,
|
||||
stk->baselen - current);
|
||||
|
||||
/* Abort if the directory is excluded */
|
||||
if (stk->baselen) {
|
||||
int dt = DT_DIR;
|
||||
dir->basebuf[stk->baselen - 1] = 0;
|
||||
dir->exclude = last_exclude_matching_from_lists(dir,
|
||||
dir->basebuf, stk->baselen - 1,
|
||||
dir->basebuf + current, &dt);
|
||||
dir->basebuf[stk->baselen - 1] = '/';
|
||||
if (dir->exclude) {
|
||||
dir->basebuf[stk->baselen] = 0;
|
||||
dir->exclude_stack = stk;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to read per-directory file unless path is too long */
|
||||
if (dir->exclude_per_dir &&
|
||||
stk->baselen + strlen(dir->exclude_per_dir) < PATH_MAX) {
|
||||
strcpy(dir->basebuf + stk->baselen,
|
||||
dir->exclude_per_dir);
|
||||
/*
|
||||
* dir->basebuf gets reused by the traversal, but we
|
||||
* need fname to remain unchanged to ensure the src
|
||||
* member of each struct exclude correctly
|
||||
* back-references its source file. Other invocations
|
||||
* of add_exclude_list provide stable strings, so we
|
||||
* strdup() and free() here in the caller.
|
||||
*/
|
||||
el->src = strdup(dir->basebuf);
|
||||
add_excludes_from_file_to_list(dir->basebuf,
|
||||
dir->basebuf, stk->baselen, el, 1);
|
||||
}
|
||||
dir->exclude_stack = stk;
|
||||
current = stk->baselen;
|
||||
}
|
||||
dir->basebuf[baselen] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the exclude lists for the directory containing pathname, then
|
||||
* scans all exclude lists to determine whether pathname is excluded.
|
||||
* Returns the exclude_list element which matched, or NULL for
|
||||
* undecided.
|
||||
*/
|
||||
struct exclude *last_exclude_matching(struct dir_struct *dir,
|
||||
const char *pathname,
|
||||
int *dtype_p)
|
||||
{
|
||||
int pathlen = strlen(pathname);
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
|
||||
prep_exclude(dir, pathname, basename-pathname);
|
||||
|
||||
if (dir->exclude)
|
||||
return dir->exclude;
|
||||
|
||||
return last_exclude_matching_from_lists(dir, pathname, pathlen,
|
||||
basename, dtype_p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the exclude lists for the directory containing pathname, then
|
||||
* scans all exclude lists to determine whether pathname is excluded.
|
||||
* Returns 1 if true, otherwise 0.
|
||||
*/
|
||||
static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
|
||||
int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
|
||||
{
|
||||
struct exclude *exclude =
|
||||
last_exclude_matching(dir, pathname, dtype_p);
|
||||
|
@ -841,93 +888,6 @@ static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_
|
|||
return 0;
|
||||
}
|
||||
|
||||
void path_exclude_check_init(struct path_exclude_check *check,
|
||||
struct dir_struct *dir)
|
||||
{
|
||||
check->dir = dir;
|
||||
check->exclude = NULL;
|
||||
strbuf_init(&check->path, 256);
|
||||
}
|
||||
|
||||
void path_exclude_check_clear(struct path_exclude_check *check)
|
||||
{
|
||||
strbuf_release(&check->path);
|
||||
}
|
||||
|
||||
/*
|
||||
* For each subdirectory in name, starting with the top-most, checks
|
||||
* to see if that subdirectory is excluded, and if so, returns the
|
||||
* corresponding exclude structure. Otherwise, checks whether name
|
||||
* itself (which is presumably a file) is excluded.
|
||||
*
|
||||
* A path to a directory known to be excluded is left in check->path to
|
||||
* optimize for repeated checks for files in the same excluded directory.
|
||||
*/
|
||||
struct exclude *last_exclude_matching_path(struct path_exclude_check *check,
|
||||
const char *name, int namelen,
|
||||
int *dtype)
|
||||
{
|
||||
int i;
|
||||
struct strbuf *path = &check->path;
|
||||
struct exclude *exclude;
|
||||
|
||||
/*
|
||||
* we allow the caller to pass namelen as an optimization; it
|
||||
* must match the length of the name, as we eventually call
|
||||
* is_excluded() on the whole name string.
|
||||
*/
|
||||
if (namelen < 0)
|
||||
namelen = strlen(name);
|
||||
|
||||
/*
|
||||
* If path is non-empty, and name is equal to path or a
|
||||
* subdirectory of path, name should be excluded, because
|
||||
* it's inside a directory which is already known to be
|
||||
* excluded and was previously left in check->path.
|
||||
*/
|
||||
if (path->len &&
|
||||
path->len <= namelen &&
|
||||
!memcmp(name, path->buf, path->len) &&
|
||||
(!name[path->len] || name[path->len] == '/'))
|
||||
return check->exclude;
|
||||
|
||||
strbuf_setlen(path, 0);
|
||||
for (i = 0; name[i]; i++) {
|
||||
int ch = name[i];
|
||||
|
||||
if (ch == '/') {
|
||||
int dt = DT_DIR;
|
||||
exclude = last_exclude_matching(check->dir,
|
||||
path->buf, &dt);
|
||||
if (exclude) {
|
||||
check->exclude = exclude;
|
||||
return exclude;
|
||||
}
|
||||
}
|
||||
strbuf_addch(path, ch);
|
||||
}
|
||||
|
||||
/* An entry in the index; cannot be a directory with subentries */
|
||||
strbuf_setlen(path, 0);
|
||||
|
||||
return last_exclude_matching(check->dir, name, dtype);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is this name excluded? This is for a caller like show_files() that
|
||||
* do not honor directory hierarchy and iterate through paths that are
|
||||
* possibly in an ignored directory.
|
||||
*/
|
||||
int is_path_excluded(struct path_exclude_check *check,
|
||||
const char *name, int namelen, int *dtype)
|
||||
{
|
||||
struct exclude *exclude =
|
||||
last_exclude_matching_path(check, name, namelen, dtype);
|
||||
if (exclude)
|
||||
return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dir_entry *dir_entry_new(const char *pathname, int len)
|
||||
{
|
||||
struct dir_entry *ent;
|
||||
|
@ -941,8 +901,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
|
|||
|
||||
static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
|
||||
{
|
||||
if (!(dir->flags & DIR_SHOW_IGNORED) &&
|
||||
cache_name_exists(pathname, len, ignore_case))
|
||||
if (cache_name_exists(pathname, len, ignore_case))
|
||||
return NULL;
|
||||
|
||||
ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
|
||||
|
@ -1044,9 +1003,8 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
|
|||
* traversal routine.
|
||||
*
|
||||
* Case 1: If we *already* have entries in the index under that
|
||||
* directory name, we recurse into the directory to see all the files,
|
||||
* unless the directory is excluded and we want to show ignored
|
||||
* directories
|
||||
* directory name, we always recurse into the directory to see
|
||||
* all the files.
|
||||
*
|
||||
* Case 2: If we *already* have that directory name as a gitlink,
|
||||
* we always continue to see it as a gitlink, regardless of whether
|
||||
|
@ -1058,38 +1016,26 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
|
|||
*
|
||||
* (a) if "show_other_directories" is true, we show it as
|
||||
* just a directory, unless "hide_empty_directories" is
|
||||
* also true and the directory is empty, in which case
|
||||
* we just ignore it entirely.
|
||||
* if we are looking for ignored directories, look if it
|
||||
* contains only ignored files to decide if it must be shown as
|
||||
* ignored or not.
|
||||
* 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.
|
||||
* (c) otherwise, we recurse into it.
|
||||
*/
|
||||
enum directory_treatment {
|
||||
show_directory,
|
||||
ignore_directory,
|
||||
recurse_into_directory
|
||||
};
|
||||
|
||||
static enum directory_treatment treat_directory(struct dir_struct *dir,
|
||||
static enum path_treatment treat_directory(struct dir_struct *dir,
|
||||
const char *dirname, int len, int exclude,
|
||||
const struct path_simplify *simplify)
|
||||
{
|
||||
/* The "len-1" is to strip the final '/' */
|
||||
switch (directory_exists_in_index(dirname, len-1)) {
|
||||
case index_directory:
|
||||
if ((dir->flags & DIR_SHOW_OTHER_DIRECTORIES) && exclude)
|
||||
break;
|
||||
|
||||
return recurse_into_directory;
|
||||
return path_recurse;
|
||||
|
||||
case index_gitdir:
|
||||
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
|
||||
return ignore_directory;
|
||||
return show_directory;
|
||||
return path_none;
|
||||
return path_untracked;
|
||||
|
||||
case index_nonexistent:
|
||||
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
|
||||
|
@ -1097,72 +1043,17 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
|
|||
if (!(dir->flags & DIR_NO_GITLINKS)) {
|
||||
unsigned char sha1[20];
|
||||
if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
|
||||
return show_directory;
|
||||
return path_untracked;
|
||||
}
|
||||
return recurse_into_directory;
|
||||
return path_recurse;
|
||||
}
|
||||
|
||||
/* This is the "show_other_directories" case */
|
||||
|
||||
/*
|
||||
* We are looking for ignored files and our directory is not ignored,
|
||||
* check if it contains only ignored files
|
||||
*/
|
||||
if ((dir->flags & DIR_SHOW_IGNORED) && !exclude) {
|
||||
int ignored;
|
||||
dir->flags &= ~DIR_SHOW_IGNORED;
|
||||
dir->flags |= DIR_HIDE_EMPTY_DIRECTORIES;
|
||||
ignored = read_directory_recursive(dir, dirname, len, 1, simplify);
|
||||
dir->flags &= ~DIR_HIDE_EMPTY_DIRECTORIES;
|
||||
dir->flags |= DIR_SHOW_IGNORED;
|
||||
if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
|
||||
return exclude ? path_excluded : path_untracked;
|
||||
|
||||
return ignored ? ignore_directory : show_directory;
|
||||
}
|
||||
if (!(dir->flags & DIR_SHOW_IGNORED) &&
|
||||
!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
|
||||
return show_directory;
|
||||
if (!read_directory_recursive(dir, dirname, len, 1, simplify))
|
||||
return ignore_directory;
|
||||
return show_directory;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide what to do when we find a file while traversing the
|
||||
* filesystem. Mostly two cases:
|
||||
*
|
||||
* 1. We are looking for ignored files
|
||||
* (a) File is ignored, include it
|
||||
* (b) File is in ignored path, include it
|
||||
* (c) File is not ignored, exclude it
|
||||
*
|
||||
* 2. Other scenarios, include the file if not excluded
|
||||
*
|
||||
* Return 1 for exclude, 0 for include.
|
||||
*/
|
||||
static int treat_file(struct dir_struct *dir, struct strbuf *path, int exclude, int *dtype)
|
||||
{
|
||||
struct path_exclude_check check;
|
||||
int exclude_file = 0;
|
||||
|
||||
if (exclude)
|
||||
exclude_file = !(dir->flags & DIR_SHOW_IGNORED);
|
||||
else if (dir->flags & DIR_SHOW_IGNORED) {
|
||||
/* Always exclude indexed files */
|
||||
struct cache_entry *ce = index_name_exists(&the_index,
|
||||
path->buf, path->len, ignore_case);
|
||||
|
||||
if (ce)
|
||||
return 1;
|
||||
|
||||
path_exclude_check_init(&check, dir);
|
||||
|
||||
if (!is_path_excluded(&check, path->buf, path->len, dtype))
|
||||
exclude_file = 1;
|
||||
|
||||
path_exclude_check_clear(&check);
|
||||
}
|
||||
|
||||
return exclude_file;
|
||||
return read_directory_recursive(dir, dirname, len, 1, simplify);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1277,57 +1168,40 @@ static int get_dtype(struct dirent *de, const char *path, int len)
|
|||
return dtype;
|
||||
}
|
||||
|
||||
enum path_treatment {
|
||||
path_ignored,
|
||||
path_handled,
|
||||
path_recurse
|
||||
};
|
||||
|
||||
static enum path_treatment treat_one_path(struct dir_struct *dir,
|
||||
struct strbuf *path,
|
||||
const struct path_simplify *simplify,
|
||||
int dtype, struct dirent *de)
|
||||
{
|
||||
int exclude = is_excluded(dir, path->buf, &dtype);
|
||||
if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
|
||||
&& exclude_matches_pathspec(path->buf, path->len, simplify))
|
||||
dir_add_ignored(dir, path->buf, path->len);
|
||||
int exclude;
|
||||
if (dtype == DT_UNKNOWN)
|
||||
dtype = get_dtype(de, path->buf, path->len);
|
||||
|
||||
/* Always exclude indexed files */
|
||||
if (dtype != DT_DIR &&
|
||||
cache_name_exists(path->buf, path->len, ignore_case))
|
||||
return path_none;
|
||||
|
||||
exclude = is_excluded(dir, path->buf, &dtype);
|
||||
|
||||
/*
|
||||
* Excluded? If we don't explicitly want to show
|
||||
* ignored files, ignore it
|
||||
*/
|
||||
if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
|
||||
return path_ignored;
|
||||
|
||||
if (dtype == DT_UNKNOWN)
|
||||
dtype = get_dtype(de, path->buf, path->len);
|
||||
if (exclude && !(dir->flags & (DIR_SHOW_IGNORED|DIR_SHOW_IGNORED_TOO)))
|
||||
return path_excluded;
|
||||
|
||||
switch (dtype) {
|
||||
default:
|
||||
return path_ignored;
|
||||
return path_none;
|
||||
case DT_DIR:
|
||||
strbuf_addch(path, '/');
|
||||
|
||||
switch (treat_directory(dir, path->buf, path->len, exclude, simplify)) {
|
||||
case show_directory:
|
||||
break;
|
||||
case recurse_into_directory:
|
||||
return path_recurse;
|
||||
case ignore_directory:
|
||||
return path_ignored;
|
||||
}
|
||||
break;
|
||||
return treat_directory(dir, path->buf, path->len, exclude,
|
||||
simplify);
|
||||
case DT_REG:
|
||||
case DT_LNK:
|
||||
switch (treat_file(dir, path, exclude, &dtype)) {
|
||||
case 1:
|
||||
return path_ignored;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return exclude ? path_excluded : path_untracked;
|
||||
}
|
||||
return path_handled;
|
||||
}
|
||||
|
||||
static enum path_treatment treat_path(struct dir_struct *dir,
|
||||
|
@ -1339,11 +1213,11 @@ static enum path_treatment treat_path(struct dir_struct *dir,
|
|||
int dtype;
|
||||
|
||||
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
|
||||
return path_ignored;
|
||||
return path_none;
|
||||
strbuf_setlen(path, baselen);
|
||||
strbuf_addstr(path, de->d_name);
|
||||
if (simplify_away(path->buf, path->len, simplify))
|
||||
return path_ignored;
|
||||
return path_none;
|
||||
|
||||
dtype = DTYPE(de);
|
||||
return treat_one_path(dir, path, simplify, dtype, de);
|
||||
|
@ -1357,14 +1231,16 @@ static enum path_treatment treat_path(struct dir_struct *dir,
|
|||
*
|
||||
* Also, we ignore the name ".git" (even if it is not a directory).
|
||||
* That likely will not change.
|
||||
*
|
||||
* Returns the most significant path_treatment value encountered in the scan.
|
||||
*/
|
||||
static int read_directory_recursive(struct dir_struct *dir,
|
||||
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
||||
const char *base, int baselen,
|
||||
int check_only,
|
||||
const struct path_simplify *simplify)
|
||||
{
|
||||
DIR *fdir;
|
||||
int contents = 0;
|
||||
enum path_treatment state, subdir_state, dir_state = path_none;
|
||||
struct dirent *de;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
|
||||
|
@ -1375,27 +1251,53 @@ static int read_directory_recursive(struct dir_struct *dir,
|
|||
goto out;
|
||||
|
||||
while ((de = readdir(fdir)) != NULL) {
|
||||
switch (treat_path(dir, de, &path, baselen, simplify)) {
|
||||
case path_recurse:
|
||||
contents += read_directory_recursive(dir, path.buf,
|
||||
path.len, 0,
|
||||
simplify);
|
||||
/* check how the file or directory should be treated */
|
||||
state = treat_path(dir, de, &path, baselen, simplify);
|
||||
if (state > dir_state)
|
||||
dir_state = state;
|
||||
|
||||
/* recurse into subdir if instructed by treat_path */
|
||||
if (state == path_recurse) {
|
||||
subdir_state = read_directory_recursive(dir, path.buf,
|
||||
path.len, check_only, simplify);
|
||||
if (subdir_state > dir_state)
|
||||
dir_state = subdir_state;
|
||||
}
|
||||
|
||||
if (check_only) {
|
||||
/* abort early if maximum state has been reached */
|
||||
if (dir_state == path_untracked)
|
||||
break;
|
||||
/* skip the dir_add_* part */
|
||||
continue;
|
||||
case path_ignored:
|
||||
continue;
|
||||
case path_handled:
|
||||
}
|
||||
|
||||
/* add the path to the appropriate result list */
|
||||
switch (state) {
|
||||
case path_excluded:
|
||||
if (dir->flags & DIR_SHOW_IGNORED)
|
||||
dir_add_name(dir, path.buf, path.len);
|
||||
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
|
||||
((dir->flags & DIR_COLLECT_IGNORED) &&
|
||||
exclude_matches_pathspec(path.buf, path.len,
|
||||
simplify)))
|
||||
dir_add_ignored(dir, path.buf, path.len);
|
||||
break;
|
||||
|
||||
case path_untracked:
|
||||
if (!(dir->flags & DIR_SHOW_IGNORED))
|
||||
dir_add_name(dir, path.buf, path.len);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
contents++;
|
||||
if (check_only)
|
||||
break;
|
||||
dir_add_name(dir, path.buf, path.len);
|
||||
}
|
||||
closedir(fdir);
|
||||
out:
|
||||
strbuf_release(&path);
|
||||
|
||||
return contents;
|
||||
return dir_state;
|
||||
}
|
||||
|
||||
static int cmp_name(const void *p1, const void *p2)
|
||||
|
@ -1444,12 +1346,14 @@ static int treat_leading_path(struct dir_struct *dir,
|
|||
struct strbuf sb = STRBUF_INIT;
|
||||
int baselen, rc = 0;
|
||||
const char *cp;
|
||||
int old_flags = dir->flags;
|
||||
|
||||
while (len && path[len - 1] == '/')
|
||||
len--;
|
||||
if (!len)
|
||||
return 1;
|
||||
baselen = 0;
|
||||
dir->flags &= ~DIR_SHOW_OTHER_DIRECTORIES;
|
||||
while (1) {
|
||||
cp = path + baselen + !!baselen;
|
||||
cp = memchr(cp, '/', path + len - cp);
|
||||
|
@ -1464,7 +1368,7 @@ static int treat_leading_path(struct dir_struct *dir,
|
|||
if (simplify_away(sb.buf, sb.len, simplify))
|
||||
break;
|
||||
if (treat_one_path(dir, &sb, simplify,
|
||||
DT_DIR, NULL) == path_ignored)
|
||||
DT_DIR, NULL) == path_none)
|
||||
break; /* do not recurse into it */
|
||||
if (len <= baselen) {
|
||||
rc = 1;
|
||||
|
@ -1472,6 +1376,7 @@ static int treat_leading_path(struct dir_struct *dir,
|
|||
}
|
||||
}
|
||||
strbuf_release(&sb);
|
||||
dir->flags = old_flags;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
25
dir.h
25
dir.h
|
@ -79,7 +79,8 @@ struct dir_struct {
|
|||
DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
|
||||
DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
|
||||
DIR_NO_GITLINKS = 1<<3,
|
||||
DIR_COLLECT_IGNORED = 1<<4
|
||||
DIR_COLLECT_IGNORED = 1<<4,
|
||||
DIR_SHOW_IGNORED_TOO = 1<<5
|
||||
} flags;
|
||||
struct dir_entry **entries;
|
||||
struct dir_entry **ignored;
|
||||
|
@ -110,9 +111,11 @@ struct dir_struct {
|
|||
*
|
||||
* exclude_stack points to the top of the exclude_stack, and
|
||||
* basebuf contains the full path to the current
|
||||
* (sub)directory in the traversal.
|
||||
* (sub)directory in the traversal. Exclude points to the
|
||||
* matching exclude struct if the directory is excluded.
|
||||
*/
|
||||
struct exclude_stack *exclude_stack;
|
||||
struct exclude *exclude;
|
||||
char basebuf[PATH_MAX];
|
||||
};
|
||||
|
||||
|
@ -149,22 +152,10 @@ extern int match_pathname(const char *, int,
|
|||
const char *, int,
|
||||
const char *, int, int, int);
|
||||
|
||||
/*
|
||||
* The is_excluded() API is meant for callers that check each level of leading
|
||||
* directory hierarchies with is_excluded() to avoid recursing into excluded
|
||||
* directories. Callers that do not do so should use this API instead.
|
||||
*/
|
||||
struct path_exclude_check {
|
||||
struct dir_struct *dir;
|
||||
struct exclude *exclude;
|
||||
struct strbuf path;
|
||||
};
|
||||
extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);
|
||||
extern void path_exclude_check_clear(struct path_exclude_check *);
|
||||
extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, const char *,
|
||||
int namelen, int *dtype);
|
||||
extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);
|
||||
extern struct exclude *last_exclude_matching(struct dir_struct *dir,
|
||||
const char *name, int *dtype);
|
||||
|
||||
extern int is_excluded(struct dir_struct *dir, const char *name, int *dtype);
|
||||
|
||||
extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
|
||||
int group_type, const char *src);
|
||||
|
|
|
@ -214,6 +214,55 @@ test_expect_success 'subdirectory ignore (l1)' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'show/hide empty ignored directory (setup)' '
|
||||
rm top/l1/l2/l1 &&
|
||||
rm top/l1/.gitignore
|
||||
'
|
||||
|
||||
test_expect_success 'show empty ignored directory with --directory' '
|
||||
(
|
||||
cd top &&
|
||||
git ls-files -o -i --exclude l1 --directory
|
||||
) >actual &&
|
||||
echo l1/ >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'hide empty ignored directory with --no-empty-directory' '
|
||||
(
|
||||
cd top &&
|
||||
git ls-files -o -i --exclude l1 --directory --no-empty-directory
|
||||
) >actual &&
|
||||
>expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'show/hide empty ignored sub-directory (setup)' '
|
||||
> top/l1/tracked &&
|
||||
(
|
||||
cd top &&
|
||||
git add -f l1/tracked
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'show empty ignored sub-directory with --directory' '
|
||||
(
|
||||
cd top &&
|
||||
git ls-files -o -i --exclude l1 --directory
|
||||
) >actual &&
|
||||
echo l1/l2/ >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'hide empty ignored sub-directory with --no-empty-directory' '
|
||||
(
|
||||
cd top &&
|
||||
git ls-files -o -i --exclude l1 --directory --no-empty-directory
|
||||
) >actual &&
|
||||
>expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'pattern matches prefix completely' '
|
||||
: >expect &&
|
||||
git ls-files -i -o --exclude "/three/a.3[abc]" >actual &&
|
||||
|
|
|
@ -32,6 +32,25 @@ test_expect_success 'status untracked directory with --ignored -u' '
|
|||
git status --porcelain --ignored -u >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
cat >expected <<\EOF
|
||||
?? untracked/uncommitted
|
||||
!! untracked/ignored
|
||||
EOF
|
||||
|
||||
test_expect_success 'status prefixed untracked directory with --ignored' '
|
||||
git status --porcelain --ignored untracked/ >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? untracked/uncommitted
|
||||
!! untracked/ignored
|
||||
EOF
|
||||
|
||||
test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
|
||||
git status --porcelain --ignored -u untracked/ >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
|
@ -60,6 +79,31 @@ test_expect_success 'status ignored directory with --ignore -u' '
|
|||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
EOF
|
||||
|
||||
test_expect_success 'status empty untracked directory with --ignore' '
|
||||
rm -rf ignored &&
|
||||
mkdir untracked-ignored &&
|
||||
mkdir untracked-ignored/test &&
|
||||
git status --porcelain --ignored >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
EOF
|
||||
|
||||
test_expect_success 'status empty untracked directory with --ignore -u' '
|
||||
git status --porcelain --ignored -u >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
|
@ -68,9 +112,6 @@ cat >expected <<\EOF
|
|||
EOF
|
||||
|
||||
test_expect_success 'status untracked directory with ignored files with --ignore' '
|
||||
rm -rf ignored &&
|
||||
mkdir untracked-ignored &&
|
||||
mkdir untracked-ignored/test &&
|
||||
: >untracked-ignored/ignored &&
|
||||
: >untracked-ignored/test/ignored &&
|
||||
git status --porcelain --ignored >actual &&
|
||||
|
@ -122,10 +163,34 @@ cat >expected <<\EOF
|
|||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
!! tracked/
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
|
||||
echo "committed" >>.gitignore &&
|
||||
git status --porcelain --ignored >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
|
||||
git status --porcelain --ignored -u >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
!! tracked/uncommitted
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory and uncommitted file with --ignore' '
|
||||
echo "tracked" >.gitignore &&
|
||||
: >tracked/uncommitted &&
|
||||
git status --porcelain --ignored >actual &&
|
||||
test_cmp expected actual
|
||||
|
@ -143,4 +208,58 @@ test_expect_success 'status ignored tracked directory and uncommitted file with
|
|||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
!! tracked/ignored/
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore' '
|
||||
rm -rf tracked/uncommitted &&
|
||||
mkdir tracked/ignored &&
|
||||
: >tracked/ignored/uncommitted &&
|
||||
git status --porcelain --ignored >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
!! tracked/ignored/uncommitted
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
|
||||
git status --porcelain --ignored -u >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
!! tracked/ignored/uncommitted
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore' '
|
||||
: >tracked/ignored/committed &&
|
||||
git add -f tracked/ignored/committed &&
|
||||
git commit -m. &&
|
||||
git status --porcelain --ignored >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
?? .gitignore
|
||||
?? actual
|
||||
?? expected
|
||||
!! tracked/ignored/uncommitted
|
||||
EOF
|
||||
|
||||
test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
|
||||
git status --porcelain --ignored -u >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -298,6 +298,23 @@ test_expect_success 'git clean -d -x' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'git clean -d -x with ignored tracked directory' '
|
||||
|
||||
mkdir -p build docs &&
|
||||
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
|
||||
git clean -d -x -e src &&
|
||||
test -f Makefile &&
|
||||
test -f README &&
|
||||
test -f src/part1.c &&
|
||||
test -f src/part2.c &&
|
||||
test ! -f a.out &&
|
||||
test -f src/part3.c &&
|
||||
test ! -d docs &&
|
||||
test ! -f obj.o &&
|
||||
test ! -d build
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git clean -X' '
|
||||
|
||||
mkdir -p build docs &&
|
||||
|
@ -332,6 +349,23 @@ test_expect_success 'git clean -d -X' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'git clean -d -X with ignored tracked directory' '
|
||||
|
||||
mkdir -p build docs &&
|
||||
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
|
||||
git clean -d -X -e src &&
|
||||
test -f Makefile &&
|
||||
test -f README &&
|
||||
test -f src/part1.c &&
|
||||
test -f src/part2.c &&
|
||||
test -f a.out &&
|
||||
test ! -f src/part3.c &&
|
||||
test -f docs/manual.txt &&
|
||||
test ! -f obj.o &&
|
||||
test ! -d build
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'clean.requireForce defaults to true' '
|
||||
|
||||
git config --unset clean.requireForce &&
|
||||
|
|
|
@ -1026,10 +1026,6 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
|||
o->el = ⪙
|
||||
}
|
||||
|
||||
if (o->dir) {
|
||||
o->path_exclude_check = xmalloc(sizeof(struct path_exclude_check));
|
||||
path_exclude_check_init(o->path_exclude_check, o->dir);
|
||||
}
|
||||
memset(&o->result, 0, sizeof(o->result));
|
||||
o->result.initialized = 1;
|
||||
o->result.timestamp.sec = o->src_index->timestamp.sec;
|
||||
|
@ -1155,10 +1151,6 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
|||
|
||||
done:
|
||||
clear_exclude_list(&el);
|
||||
if (o->path_exclude_check) {
|
||||
path_exclude_check_clear(o->path_exclude_check);
|
||||
free(o->path_exclude_check);
|
||||
}
|
||||
return ret;
|
||||
|
||||
return_failed:
|
||||
|
@ -1375,7 +1367,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
|
|||
return 0;
|
||||
|
||||
if (o->dir &&
|
||||
is_path_excluded(o->path_exclude_check, name, -1, &dtype))
|
||||
is_excluded(o->dir, name, &dtype))
|
||||
/*
|
||||
* ce->name is explicitly excluded, so it is Ok to
|
||||
* overwrite it.
|
||||
|
|
|
@ -52,7 +52,6 @@ struct unpack_trees_options {
|
|||
const char *prefix;
|
||||
int cache_bottom;
|
||||
struct dir_struct *dir;
|
||||
struct path_exclude_check *path_exclude_check;
|
||||
struct pathspec *pathspec;
|
||||
merge_fn_t fn;
|
||||
const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
|
||||
|
|
24
wt-status.c
24
wt-status.c
|
@ -511,9 +511,12 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
|||
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
|
||||
dir.flags |=
|
||||
DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
|
||||
if (s->show_ignored_files)
|
||||
dir.flags |= DIR_SHOW_IGNORED_TOO;
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
fill_directory(&dir, s->pathspec);
|
||||
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
struct dir_entry *ent = dir.entries[i];
|
||||
if (cache_name_is_other(ent->name, ent->len) &&
|
||||
|
@ -522,22 +525,17 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
|||
free(ent);
|
||||
}
|
||||
|
||||
if (s->show_ignored_files) {
|
||||
dir.nr = 0;
|
||||
dir.flags = DIR_SHOW_IGNORED;
|
||||
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
|
||||
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
|
||||
fill_directory(&dir, s->pathspec);
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
struct dir_entry *ent = dir.entries[i];
|
||||
if (cache_name_is_other(ent->name, ent->len) &&
|
||||
match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
|
||||
string_list_insert(&s->ignored, ent->name);
|
||||
free(ent);
|
||||
}
|
||||
for (i = 0; i < dir.ignored_nr; i++) {
|
||||
struct dir_entry *ent = dir.ignored[i];
|
||||
if (cache_name_is_other(ent->name, ent->len) &&
|
||||
match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
|
||||
string_list_insert(&s->ignored, ent->name);
|
||||
free(ent);
|
||||
}
|
||||
|
||||
free(dir.entries);
|
||||
free(dir.ignored);
|
||||
clear_directory(&dir);
|
||||
|
||||
if (advice_status_u_option) {
|
||||
struct timeval t_end;
|
||||
|
|
Загрузка…
Ссылка в новой задаче