Merge branch 'jc/gitignore-ends-with-slash'
* jc/gitignore-ends-with-slash: gitignore: lazily find dtype gitignore(5): Allow "foo/" in ignore list to match directory "foo"
This commit is contained in:
commit
987e315a6b
@ -57,6 +57,13 @@ Patterns have the following format:
|
|||||||
included again. If a negated pattern matches, this will
|
included again. If a negated pattern matches, this will
|
||||||
override lower precedence patterns sources.
|
override lower precedence patterns sources.
|
||||||
|
|
||||||
|
- If the pattern ends with a slash, it is removed for the
|
||||||
|
purpose of the following description, but it would only find
|
||||||
|
a match with a directory. In other words, `foo/` will match a
|
||||||
|
directory `foo` and paths underneath it, but will not match a
|
||||||
|
regular file or a symbolic link `foo` (this is consistent
|
||||||
|
with the way how pathspec works in general in git).
|
||||||
|
|
||||||
- If the pattern does not contain a slash '/', git treats it as
|
- If the pattern does not contain a slash '/', git treats it as
|
||||||
a shell glob pattern and checks for a match against the
|
a shell glob pattern and checks for a match against the
|
||||||
pathname without leading directories.
|
pathname without leading directories.
|
||||||
|
@ -238,7 +238,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
|
|||||||
if (show_cached | show_stage) {
|
if (show_cached | show_stage) {
|
||||||
for (i = 0; i < active_nr; i++) {
|
for (i = 0; i < active_nr; i++) {
|
||||||
struct cache_entry *ce = active_cache[i];
|
struct cache_entry *ce = active_cache[i];
|
||||||
if (excluded(dir, ce->name) != dir->show_ignored)
|
int dtype = ce_to_dtype(ce);
|
||||||
|
if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
|
||||||
continue;
|
continue;
|
||||||
if (show_unmerged && !ce_stage(ce))
|
if (show_unmerged && !ce_stage(ce))
|
||||||
continue;
|
continue;
|
||||||
@ -252,7 +253,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
|
|||||||
struct cache_entry *ce = active_cache[i];
|
struct cache_entry *ce = active_cache[i];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int err;
|
int err;
|
||||||
if (excluded(dir, ce->name) != dir->show_ignored)
|
int dtype = ce_to_dtype(ce);
|
||||||
|
if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
|
||||||
continue;
|
continue;
|
||||||
err = lstat(ce->name, &st);
|
err = lstat(ce->name, &st);
|
||||||
if (show_deleted && err)
|
if (show_deleted && err)
|
||||||
|
12
cache.h
12
cache.h
@ -178,6 +178,18 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in
|
|||||||
}
|
}
|
||||||
return create_ce_mode(mode);
|
return create_ce_mode(mode);
|
||||||
}
|
}
|
||||||
|
static inline int ce_to_dtype(const struct cache_entry *ce)
|
||||||
|
{
|
||||||
|
unsigned ce_mode = ntohl(ce->ce_mode);
|
||||||
|
if (S_ISREG(ce_mode))
|
||||||
|
return DT_REG;
|
||||||
|
else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
|
||||||
|
return DT_DIR;
|
||||||
|
else if (S_ISLNK(ce_mode))
|
||||||
|
return DT_LNK;
|
||||||
|
else
|
||||||
|
return DT_UNKNOWN;
|
||||||
|
}
|
||||||
#define canon_mode(mode) \
|
#define canon_mode(mode) \
|
||||||
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
|
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
|
||||||
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
|
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
|
||||||
|
49
dir.c
49
dir.c
@ -17,6 +17,7 @@ struct path_simplify {
|
|||||||
static int read_directory_recursive(struct dir_struct *dir,
|
static int read_directory_recursive(struct dir_struct *dir,
|
||||||
const char *path, const char *base, int baselen,
|
const char *path, const char *base, int baselen,
|
||||||
int check_only, const struct path_simplify *simplify);
|
int check_only, const struct path_simplify *simplify);
|
||||||
|
static int get_dtype(struct dirent *de, const char *path);
|
||||||
|
|
||||||
int common_prefix(const char **pathspec)
|
int common_prefix(const char **pathspec)
|
||||||
{
|
{
|
||||||
@ -126,18 +127,34 @@ static int no_wildcard(const char *string)
|
|||||||
void add_exclude(const char *string, const char *base,
|
void add_exclude(const char *string, const char *base,
|
||||||
int baselen, struct exclude_list *which)
|
int baselen, struct exclude_list *which)
|
||||||
{
|
{
|
||||||
struct exclude *x = xmalloc(sizeof (*x));
|
struct exclude *x;
|
||||||
|
size_t len;
|
||||||
|
int to_exclude = 1;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
x->to_exclude = 1;
|
|
||||||
if (*string == '!') {
|
if (*string == '!') {
|
||||||
x->to_exclude = 0;
|
to_exclude = 0;
|
||||||
string++;
|
string++;
|
||||||
}
|
}
|
||||||
x->pattern = string;
|
len = strlen(string);
|
||||||
|
if (len && string[len - 1] == '/') {
|
||||||
|
char *s;
|
||||||
|
x = xmalloc(sizeof(*x) + len);
|
||||||
|
s = (char*)(x+1);
|
||||||
|
memcpy(s, string, len - 1);
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
string = s;
|
||||||
|
x->pattern = s;
|
||||||
|
flags = EXC_FLAG_MUSTBEDIR;
|
||||||
|
} else {
|
||||||
|
x = xmalloc(sizeof(*x));
|
||||||
|
x->pattern = string;
|
||||||
|
}
|
||||||
|
x->to_exclude = to_exclude;
|
||||||
x->patternlen = strlen(string);
|
x->patternlen = strlen(string);
|
||||||
x->base = base;
|
x->base = base;
|
||||||
x->baselen = baselen;
|
x->baselen = baselen;
|
||||||
x->flags = 0;
|
x->flags = flags;
|
||||||
if (!strchr(string, '/'))
|
if (!strchr(string, '/'))
|
||||||
x->flags |= EXC_FLAG_NODIR;
|
x->flags |= EXC_FLAG_NODIR;
|
||||||
if (no_wildcard(string))
|
if (no_wildcard(string))
|
||||||
@ -261,7 +278,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
|||||||
* Return 1 for exclude, 0 for include and -1 for undecided.
|
* Return 1 for exclude, 0 for include and -1 for undecided.
|
||||||
*/
|
*/
|
||||||
static int excluded_1(const char *pathname,
|
static int excluded_1(const char *pathname,
|
||||||
int pathlen, const char *basename,
|
int pathlen, const char *basename, int *dtype,
|
||||||
struct exclude_list *el)
|
struct exclude_list *el)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -272,6 +289,13 @@ static int excluded_1(const char *pathname,
|
|||||||
const char *exclude = x->pattern;
|
const char *exclude = x->pattern;
|
||||||
int to_exclude = x->to_exclude;
|
int to_exclude = x->to_exclude;
|
||||||
|
|
||||||
|
if (x->flags & EXC_FLAG_MUSTBEDIR) {
|
||||||
|
if (*dtype == DT_UNKNOWN)
|
||||||
|
*dtype = get_dtype(NULL, pathname);
|
||||||
|
if (*dtype != DT_DIR)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (x->flags & EXC_FLAG_NODIR) {
|
if (x->flags & EXC_FLAG_NODIR) {
|
||||||
/* match basename */
|
/* match basename */
|
||||||
if (x->flags & EXC_FLAG_NOWILDCARD) {
|
if (x->flags & EXC_FLAG_NOWILDCARD) {
|
||||||
@ -314,7 +338,7 @@ static int excluded_1(const char *pathname,
|
|||||||
return -1; /* undecided */
|
return -1; /* undecided */
|
||||||
}
|
}
|
||||||
|
|
||||||
int excluded(struct dir_struct *dir, const char *pathname)
|
int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
|
||||||
{
|
{
|
||||||
int pathlen = strlen(pathname);
|
int pathlen = strlen(pathname);
|
||||||
int st;
|
int st;
|
||||||
@ -323,7 +347,8 @@ int excluded(struct dir_struct *dir, const char *pathname)
|
|||||||
|
|
||||||
prep_exclude(dir, pathname, basename-pathname);
|
prep_exclude(dir, pathname, basename-pathname);
|
||||||
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
|
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
|
||||||
switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
|
switch (excluded_1(pathname, pathlen, basename,
|
||||||
|
dtype_p, &dir->exclude_list[st])) {
|
||||||
case 0:
|
case 0:
|
||||||
return 0;
|
return 0;
|
||||||
case 1:
|
case 1:
|
||||||
@ -508,7 +533,7 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
|
|||||||
|
|
||||||
static int get_dtype(struct dirent *de, const char *path)
|
static int get_dtype(struct dirent *de, const char *path)
|
||||||
{
|
{
|
||||||
int dtype = DTYPE(de);
|
int dtype = de ? DTYPE(de) : DT_UNKNOWN;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (dtype != DT_UNKNOWN)
|
if (dtype != DT_UNKNOWN)
|
||||||
@ -560,7 +585,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
|||||||
if (simplify_away(fullname, baselen + len, simplify))
|
if (simplify_away(fullname, baselen + len, simplify))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
exclude = excluded(dir, fullname);
|
dtype = DTYPE(de);
|
||||||
|
exclude = excluded(dir, fullname, &dtype);
|
||||||
if (exclude && dir->collect_ignored
|
if (exclude && dir->collect_ignored
|
||||||
&& in_pathspec(fullname, baselen + len, simplify))
|
&& in_pathspec(fullname, baselen + len, simplify))
|
||||||
dir_add_ignored(dir, fullname, baselen + len);
|
dir_add_ignored(dir, fullname, baselen + len);
|
||||||
@ -572,7 +598,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
|||||||
if (exclude && !dir->show_ignored)
|
if (exclude && !dir->show_ignored)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
dtype = get_dtype(de, fullname);
|
if (dtype == DT_UNKNOWN)
|
||||||
|
dtype = get_dtype(de, fullname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do we want to see just the ignored files?
|
* Do we want to see just the ignored files?
|
||||||
|
3
dir.h
3
dir.h
@ -9,6 +9,7 @@ struct dir_entry {
|
|||||||
#define EXC_FLAG_NODIR 1
|
#define EXC_FLAG_NODIR 1
|
||||||
#define EXC_FLAG_NOWILDCARD 2
|
#define EXC_FLAG_NOWILDCARD 2
|
||||||
#define EXC_FLAG_ENDSWITH 4
|
#define EXC_FLAG_ENDSWITH 4
|
||||||
|
#define EXC_FLAG_MUSTBEDIR 8
|
||||||
|
|
||||||
struct exclude_list {
|
struct exclude_list {
|
||||||
int nr;
|
int nr;
|
||||||
@ -67,7 +68,7 @@ extern int match_pathspec(const char **pathspec, const char *name, int namelen,
|
|||||||
|
|
||||||
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
|
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
|
||||||
|
|
||||||
extern int excluded(struct dir_struct *, const char *);
|
extern int excluded(struct dir_struct *, const char *, int *);
|
||||||
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
||||||
extern void add_exclude(const char *string, const char *base,
|
extern void add_exclude(const char *string, const char *base,
|
||||||
int baselen, struct exclude_list *which);
|
int baselen, struct exclude_list *which);
|
||||||
|
@ -99,4 +99,45 @@ EOF
|
|||||||
test_expect_success 'git-status honours core.excludesfile' \
|
test_expect_success 'git-status honours core.excludesfile' \
|
||||||
'diff -u expect output'
|
'diff -u expect output'
|
||||||
|
|
||||||
|
test_expect_success 'trailing slash in exclude allows directory match(1)' '
|
||||||
|
|
||||||
|
git ls-files --others --exclude=one/ >output &&
|
||||||
|
if grep "^one/" output
|
||||||
|
then
|
||||||
|
echo Ooops
|
||||||
|
false
|
||||||
|
else
|
||||||
|
: happy
|
||||||
|
fi
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'trailing slash in exclude allows directory match (2)' '
|
||||||
|
|
||||||
|
git ls-files --others --exclude=one/two/ >output &&
|
||||||
|
if grep "^one/two/" output
|
||||||
|
then
|
||||||
|
echo Ooops
|
||||||
|
false
|
||||||
|
else
|
||||||
|
: happy
|
||||||
|
fi
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'trailing slash in exclude forces directory match (1)' '
|
||||||
|
|
||||||
|
>two
|
||||||
|
git ls-files --others --exclude=two/ >output &&
|
||||||
|
grep "^two" output
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'trailing slash in exclude forces directory match (2)' '
|
||||||
|
|
||||||
|
git ls-files --others --exclude=one/a.1/ >output &&
|
||||||
|
grep "^one/a.1" output
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -521,8 +521,9 @@ static void verify_absent(struct cache_entry *ce, const char *action,
|
|||||||
|
|
||||||
if (!lstat(ce->name, &st)) {
|
if (!lstat(ce->name, &st)) {
|
||||||
int cnt;
|
int cnt;
|
||||||
|
int dtype = ce_to_dtype(ce);
|
||||||
|
|
||||||
if (o->dir && excluded(o->dir, ce->name))
|
if (o->dir && excluded(o->dir, ce->name, &dtype))
|
||||||
/*
|
/*
|
||||||
* ce->name is explicitly excluded, so it is Ok to
|
* ce->name is explicitly excluded, so it is Ok to
|
||||||
* overwrite it.
|
* overwrite it.
|
||||||
|
Loading…
Reference in New Issue
Block a user