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:
Junio C Hamano 2008-02-16 17:57:06 -08:00
commit 987e315a6b
7 changed files with 106 additions and 15 deletions

View File

@ -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.

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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

View File

@ -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.