author | Junio C Hamano <gitster@pobox.com> | |
Sun, 17 Feb 2008 01:57:06 +0000 (17:57 -0800) | ||
committer | Junio C Hamano <gitster@pobox.com> | |
Sun, 17 Feb 2008 01:57:06 +0000 (17:57 -0800) |
* jc/gitignore-ends-with-slash:
gitignore: lazily find dtype
gitignore(5): Allow "foo/" in ignore list to match directory "foo"
gitignore: lazily find dtype
gitignore(5): Allow "foo/" in ignore list to match directory "foo"
index 08373f52bb7e5537049b12abff3e07c02769e7b6..e847b3ba63f3fae3d5497d6ede1ea0ee416a74de 100644 (file)
included again. If a negated pattern matches, this will
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
a shell glob pattern and checks for a match against the
pathname without leading directories.
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index d56e33e251036274dbe48e300fcebd8289624cbe..dc7eab89b34fed32dbb198a9aa9a7503fc162216 100644 (file)
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
if (show_cached | show_stage) {
for (i = 0; i < active_nr; 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;
if (show_unmerged && !ce_stage(ce))
continue;
struct cache_entry *ce = active_cache[i];
struct stat st;
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;
err = lstat(ce->name, &st);
if (show_deleted && err)
index efd31d8569c27ca18f2f7a7e599c115c8be9251a..18fe8447f3bdff2a772716c9293e44e357178a4c 100644 (file)
--- a/cache.h
+++ b/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);
}
+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) \
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
index 6543105b9622212430a9e5ed131a81074e019d9a..1f507daff2c278a70da768e3754a68891b946973 100644 (file)
--- a/dir.c
+++ b/dir.c
static int read_directory_recursive(struct dir_struct *dir,
const char *path, const char *base, int baselen,
int check_only, const struct path_simplify *simplify);
+static int get_dtype(struct dirent *de, const char *path);
int common_prefix(const char **pathspec)
{
void add_exclude(const char *string, const char *base,
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 == '!') {
- x->to_exclude = 0;
+ to_exclude = 0;
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->base = base;
x->baselen = baselen;
- x->flags = 0;
+ x->flags = flags;
if (!strchr(string, '/'))
x->flags |= EXC_FLAG_NODIR;
if (no_wildcard(string))
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
static int excluded_1(const char *pathname,
- int pathlen, const char *basename,
+ int pathlen, const char *basename, int *dtype,
struct exclude_list *el)
{
int i;
const char *exclude = x->pattern;
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) {
/* match basename */
if (x->flags & EXC_FLAG_NOWILDCARD) {
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 st;
prep_exclude(dir, pathname, basename-pathname);
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:
return 0;
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)
{
- int dtype = DTYPE(de);
+ int dtype = de ? DTYPE(de) : DT_UNKNOWN;
struct stat st;
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))
continue;
- exclude = excluded(dir, fullname);
+ dtype = DTYPE(de);
+ exclude = excluded(dir, fullname, &dtype);
if (exclude && dir->collect_ignored
&& in_pathspec(fullname, baselen + len, simplify))
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)
continue;
- dtype = get_dtype(de, fullname);
+ if (dtype == DT_UNKNOWN)
+ dtype = get_dtype(de, fullname);
/*
* Do we want to see just the ignored files?
index d8814dccb2dd57af21d75c91a52026e09fdf1b95..2df15defb6720a742282f24721233c4816deceb6 100644 (file)
--- a/dir.h
+++ b/dir.h
#define EXC_FLAG_NODIR 1
#define EXC_FLAG_NOWILDCARD 2
#define EXC_FLAG_ENDSWITH 4
+#define EXC_FLAG_MUSTBEDIR 8
struct exclude_list {
int nr;
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_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which);
index e25b25568337ae36a5ef1377a14df031d5dfaeb1..b4297bacf2bd3d6da0f7fd01fba65741cf568003 100755 (executable)
test_expect_success 'git-status honours core.excludesfile' \
'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
diff --git a/unpack-trees.c b/unpack-trees.c
index ff46fd62fdceeefc2ebd98ae1c9483964cb37bd5..ec558f9005fab372f9bf62d8c3df9ea34f5222bb 100644 (file)
--- a/unpack-trees.c
+++ b/unpack-trees.c
if (!lstat(ce->name, &st)) {
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
* overwrite it.