Merge branch 'jc/ls-files-i-dir'

"git ls-files --exclude=t -i" did not consider anything under t/
as excluded, as it did not pay attention to exclusion of leading
paths while walking the index.  Other two users of excluded() are
also updated.

* jc/ls-files-i-dir:
  dir.c: make excluded() file scope static
  unpack-trees.c: use path_excluded() in check_ok_to_remove()
  builtin/add.c: use path_excluded()
  path_excluded(): update API to less cache-entry centric
  ls-files -i: micro-optimize path_excluded()
  ls-files -i: pay attention to exclusion of leading paths
This commit is contained in:
Junio C Hamano 2012-06-21 14:42:06 -07:00
commit 1966babf6e
6 changed files with 109 additions and 10 deletions

View File

@ -443,6 +443,9 @@ 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_used_pathspec(pathspec);
for (i = 0; pathspec[i]; i++) {
@ -450,7 +453,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
&& !file_exists(pathspec[i])) {
if (ignore_missing) {
int dtype = DT_UNKNOWN;
if (excluded(&dir, pathspec[i], &dtype))
if (path_excluded(&check, pathspec[i], -1, &dtype))
dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
} else
die(_("pathspec '%s' did not match any files"),
@ -458,6 +461,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
}
free(seen);
path_exclude_check_clear(&check);
}
plug_bulk_checkin();

View File

@ -200,9 +200,19 @@ static void show_ru_info(void)
}
}
static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)
{
int dtype = ce_to_dtype(ce);
return path_excluded(check, ce->name, ce_namelen(ce), &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) {
@ -215,9 +225,8 @@ static void show_files(struct dir_struct *dir)
if (show_cached | show_stage) {
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
int dtype = ce_to_dtype(ce);
if (dir->flags & DIR_SHOW_IGNORED &&
!excluded(dir, ce->name, &dtype))
if ((dir->flags & DIR_SHOW_IGNORED) &&
!ce_excluded(&check, ce))
continue;
if (show_unmerged && !ce_stage(ce))
continue;
@ -232,9 +241,8 @@ static void show_files(struct dir_struct *dir)
struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
int dtype = ce_to_dtype(ce);
if (dir->flags & DIR_SHOW_IGNORED &&
!excluded(dir, ce->name, &dtype))
if ((dir->flags & DIR_SHOW_IGNORED) &&
!ce_excluded(&check, ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
@ -247,6 +255,9 @@ 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);
}
/*

60
dir.c
View File

@ -553,7 +553,7 @@ int excluded_from_list(const char *pathname,
return -1; /* undecided */
}
int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
{
int pathlen = strlen(pathname);
int st;
@ -573,6 +573,64 @@ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
return 0;
}
void path_exclude_check_init(struct path_exclude_check *check,
struct dir_struct *dir)
{
check->dir = dir;
strbuf_init(&check->path, 256);
}
void path_exclude_check_clear(struct path_exclude_check *check)
{
strbuf_release(&check->path);
}
/*
* 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.
*
* 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.
*/
int path_excluded(struct path_exclude_check *check,
const char *name, int namelen, int *dtype)
{
int i;
struct strbuf *path = &check->path;
/*
* we allow the caller to pass namelen as an optimization; it
* must match the length of the name, as we eventually call
* excluded() on the whole name string.
*/
if (namelen < 0)
namelen = strlen(name);
if (path->len &&
path->len <= namelen &&
!memcmp(name, path->buf, path->len) &&
(!name[path->len] || name[path->len] == '/'))
return 1;
strbuf_setlen(path, 0);
for (i = 0; name[i]; i++) {
int ch = name[i];
if (ch == '/') {
int dt = DT_DIR;
if (excluded(check->dir, path->buf, &dt))
return 1;
}
strbuf_addch(path, ch);
}
/* An entry in the index; cannot be a directory with subentries */
strbuf_setlen(path, 0);
return excluded(check->dir, name, dtype);
}
static struct dir_entry *dir_entry_new(const char *pathname, int len)
{
struct dir_entry *ent;

18
dir.h
View File

@ -1,6 +1,8 @@
#ifndef DIR_H
#define DIR_H
#include "strbuf.h"
struct dir_entry {
unsigned int len;
char name[FLEX_ARRAY]; /* more */
@ -76,8 +78,22 @@ extern int read_directory(struct dir_struct *, const char *path, int len, const
extern int excluded_from_list(const char *pathname, int pathlen, const char *basename,
int *dtype, struct exclude_list *el);
extern int excluded(struct dir_struct *, const char *, int *);
struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);
/*
* The excluded() API is meant for callers that check each level of leading
* directory hierarchies with 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 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 int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
char **buf_p, struct exclude_list *which, int check_index);
extern void add_excludes_from_file(struct dir_struct *, const char *fname);

View File

@ -1023,6 +1023,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
o->el = &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;
@ -1148,6 +1152,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
done:
free_excludes(&el);
if (o->path_exclude_check) {
path_exclude_check_clear(o->path_exclude_check);
free(o->path_exclude_check);
}
return ret;
return_failed:
@ -1363,7 +1371,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
if (ignore_case && icase_exists(o, name, len, st))
return 0;
if (o->dir && excluded(o->dir, name, &dtype))
if (o->dir &&
path_excluded(o->path_exclude_check, name, -1, &dtype))
/*
* ce->name is explicitly excluded, so it is Ok to
* overwrite it.

View File

@ -52,6 +52,7 @@ 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];