Junio C Hamano <gitster@xxxxxxxxx> writes: > Jeff King <peff@xxxxxxxx> writes: > >> No, I don't think anybody is working on it at the moment (at least I >> do not see anything near the time of that old discussion, nor do I >> recall it being discussed since). >> >> +cc Clemens, in case he had any work-in-progress as a result that did >> not end up getting published. > > I think the way the codepath for -i feeds path to excluded() is wrong. > > The excluded() interface relies on the fact that the caller has > already checked foo/ before calling it for foo/bar; when asked to > see if "foo/bar" is excluded, it does not consider if "foo/" is > excluded---the caller should have already checked it. > > The loop for cached/stage case in builtin/ls-files.c::show_files() > needs to learn to be more careful when DIR_SHOW_IGNORED is set in > dir->flags. It needs to call a new helper function that notices > that the check is walking into a different directory from the path > it checked last, and feed leading directories to excluded(), or > something like that, instead of directly calling !excluded(). A naive and unoptimized implementation may look like this patch. The "path_exclude_check" structure can be enhanced to record the leading directory it has last checked to be known to be excluded so that path_excluded() can check if the ce->name[] is still inside that directory and return true early, but I'll leave it as an exercise for interested readers while I look at other topics for the upcoming release. -- >8 -- Subject: ls-files -i: pay attention to exclusion of leading paths "git ls-files --exclude=t/ -i" does not show paths in directory t/ that have been added to the index, but it should. The excluded() API was designed for callers who walk the tree from the top, checking each level of the directory hierarchy as it descends if it is excluded, and not even bothering to recurse into an excluded directory. This would allow us optimize for a common case by not having to check if the exclude pattern "foo/" matches when looking at "foo/bar", because the caller should have noticed that "foo" is excluded and did not even bother to read "foo/bar" out of opendir()/readdir() to call it. The code for "ls-files -i" however walks the index linearly, feeding paths without checking if the leading directory is already excluded. Introduce a helper function path_excluded() to let this caller properly call excluded() check for higher hierarchies as necessary. Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> --- builtin/ls-files.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 7cff175..40f8865 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -13,6 +13,7 @@ #include "parse-options.h" #include "resolve-undo.h" #include "string-list.h" +#include "strbuf.h" static int abbrev; static int show_deleted; @@ -200,9 +201,50 @@ static void show_ru_info(void) } } +struct path_exclude_check { + struct dir_struct *dir; + struct strbuf path; +}; + +static void path_exclude_check_init(struct path_exclude_check *check, + struct dir_struct *dir) +{ + check->dir = dir; + strbuf_init(&check->path, 256); +} + +static void path_exclude_check_clear(struct path_exclude_check *check) +{ + strbuf_release(&check->path); +} + +static int path_excluded(struct path_exclude_check *check, struct cache_entry *ce) +{ + int i, dtype; + struct strbuf *path = &check->path; + strbuf_setlen(path, 0); + + for (i = 0; ce->name[i]; i++) { + int ch = ce->name[i]; + + if (ch == '/') { + dtype = DT_DIR; + if (excluded(check->dir, path->buf, &dtype)) + return 1; + } + strbuf_addch(path, ch); + } + dtype = ce_to_dtype(ce); + return excluded(check->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) { @@ -215,9 +257,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) && + !path_excluded(&check, ce)) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -232,9 +273,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) && + !path_excluded(&check, ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; @@ -247,6 +287,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); } /* -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html