Skip to content

Commit d912b0e

Browse files
committed
Merge branch 'as/dir-c-cleanup'
Refactor and generally clean up the directory traversal API implementation. * as/dir-c-cleanup: dir.c: rename free_excludes() to clear_exclude_list() dir.c: refactor is_path_excluded() dir.c: refactor is_excluded() dir.c: refactor is_excluded_from_list() dir.c: rename excluded() to is_excluded() dir.c: rename excluded_from_list() to is_excluded_from_list() dir.c: rename path_excluded() to is_path_excluded() dir.c: rename cryptic 'which' variable to more consistent name Improve documentation and comments regarding directory traversal API api-directory-listing.txt: update to match code
2 parents 20e47e5 + f619881 commit d912b0e

File tree

7 files changed

+171
-62
lines changed

7 files changed

+171
-62
lines changed

Documentation/technical/api-directory-listing.txt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,40 @@ Data structure
99
--------------
1010

1111
`struct dir_struct` structure is used to pass directory traversal
12-
options to the library and to record the paths discovered. The notable
13-
options are:
12+
options to the library and to record the paths discovered. A single
13+
`struct dir_struct` is used regardless of whether or not the traversal
14+
recursively descends into subdirectories.
15+
16+
The notable options are:
1417

1518
`exclude_per_dir`::
1619

1720
The name of the file to be read in each directory for excluded
1821
files (typically `.gitignore`).
1922

20-
`collect_ignored`::
23+
`flags`::
2124

22-
Include paths that are to be excluded in the result.
25+
A bit-field of options:
2326

24-
`show_ignored`::
27+
`DIR_SHOW_IGNORED`:::
2528

2629
The traversal is for finding just ignored files, not unignored
2730
files.
2831

29-
`show_other_directories`::
32+
`DIR_SHOW_OTHER_DIRECTORIES`:::
3033

3134
Include a directory that is not tracked.
3235

33-
`hide_empty_directories`::
36+
`DIR_HIDE_EMPTY_DIRECTORIES`:::
3437

3538
Do not include a directory that is not tracked and is empty.
3639

37-
`no_gitlinks`::
40+
`DIR_NO_GITLINKS`:::
3841

3942
If set, recurse into a directory that looks like a git
4043
directory. Otherwise it is shown as a directory.
4144

42-
The result of the enumeration is left in these fields::
45+
The result of the enumeration is left in these fields:
4346

4447
`entries[]`::
4548

attr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
284284
* (reading the file from top to bottom), .gitattribute of the root
285285
* directory (again, reading the file from top to bottom) down to the
286286
* current directory, and then scan the list backwards to find the first match.
287-
* This is exactly the same as what excluded() does in dir.c to deal with
287+
* This is exactly the same as what is_excluded() does in dir.c to deal with
288288
* .gitignore
289289
*/
290290

builtin/add.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
454454
&& !file_exists(pathspec[i])) {
455455
if (ignore_missing) {
456456
int dtype = DT_UNKNOWN;
457-
if (path_excluded(&check, pathspec[i], -1, &dtype))
457+
if (is_path_excluded(&check, pathspec[i], -1, &dtype))
458458
dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
459459
} else
460460
die(_("pathspec '%s' did not match any files"),

builtin/ls-files.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ static void show_ru_info(void)
203203
static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)
204204
{
205205
int dtype = ce_to_dtype(ce);
206-
return path_excluded(check, ce->name, ce_namelen(ce), &dtype);
206+
return is_path_excluded(check, ce->name, ce_namelen(ce), &dtype);
207207
}
208208

209209
static void show_files(struct dir_struct *dir)

dir.c

Lines changed: 114 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* This handles recursive filename detection with exclude
33
* files, index knowledge etc..
44
*
5+
* See Documentation/technical/api-directory-listing.txt
6+
*
57
* Copyright (C) Linus Torvalds, 2005-2006
68
* Junio Hamano, 2005-2006
79
*/
@@ -377,7 +379,7 @@ void parse_exclude_pattern(const char **pattern,
377379
}
378380

379381
void add_exclude(const char *string, const char *base,
380-
int baselen, struct exclude_list *which)
382+
int baselen, struct exclude_list *el)
381383
{
382384
struct exclude *x;
383385
int patternlen;
@@ -401,8 +403,8 @@ void add_exclude(const char *string, const char *base,
401403
x->base = base;
402404
x->baselen = baselen;
403405
x->flags = flags;
404-
ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
405-
which->excludes[which->nr++] = x;
406+
ALLOC_GROW(el->excludes, el->nr + 1, el->alloc);
407+
el->excludes[el->nr++] = x;
406408
}
407409

408410
static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
@@ -428,7 +430,11 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
428430
return data;
429431
}
430432

431-
void free_excludes(struct exclude_list *el)
433+
/*
434+
* Frees memory within el which was allocated for exclude patterns and
435+
* the file buffer. Does not free el itself.
436+
*/
437+
void clear_exclude_list(struct exclude_list *el)
432438
{
433439
int i;
434440

@@ -444,7 +450,7 @@ int add_excludes_from_file_to_list(const char *fname,
444450
const char *base,
445451
int baselen,
446452
char **buf_p,
447-
struct exclude_list *which,
453+
struct exclude_list *el,
448454
int check_index)
449455
{
450456
struct stat st;
@@ -493,7 +499,7 @@ int add_excludes_from_file_to_list(const char *fname,
493499
if (buf[i] == '\n') {
494500
if (entry != buf + i && entry[0] != '#') {
495501
buf[i - (i && buf[i-1] == '\r')] = 0;
496-
add_exclude(entry, base, baselen, which);
502+
add_exclude(entry, base, baselen, el);
497503
}
498504
entry = buf + i + 1;
499505
}
@@ -508,6 +514,10 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
508514
die("cannot use %s as an exclude file", fname);
509515
}
510516

517+
/*
518+
* Loads the per-directory exclude list for the substring of base
519+
* which has a char length of baselen.
520+
*/
511521
static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
512522
{
513523
struct exclude_list *el;
@@ -518,7 +528,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
518528
(baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
519529
return; /* too long a path -- ignore */
520530

521-
/* Pop the ones that are not the prefix of the path being checked. */
531+
/* Pop the directories that are not the prefix of the path being checked. */
522532
el = &dir->exclude_list[EXC_DIRS];
523533
while ((stk = dir->exclude_stack) != NULL) {
524534
if (stk->baselen <= baselen &&
@@ -629,22 +639,26 @@ int match_pathname(const char *pathname, int pathlen,
629639
ignore_case ? FNM_CASEFOLD : 0) == 0;
630640
}
631641

632-
/* Scan the list and let the last match determine the fate.
633-
* Return 1 for exclude, 0 for include and -1 for undecided.
642+
/*
643+
* Scan the given exclude list in reverse to see whether pathname
644+
* should be ignored. The first match (i.e. the last on the list), if
645+
* any, determines the fate. Returns the exclude_list element which
646+
* matched, or NULL for undecided.
634647
*/
635-
int excluded_from_list(const char *pathname,
636-
int pathlen, const char *basename, int *dtype,
637-
struct exclude_list *el)
648+
static struct exclude *last_exclude_matching_from_list(const char *pathname,
649+
int pathlen,
650+
const char *basename,
651+
int *dtype,
652+
struct exclude_list *el)
638653
{
639654
int i;
640655

641656
if (!el->nr)
642-
return -1; /* undefined */
657+
return NULL; /* undefined */
643658

644659
for (i = el->nr - 1; 0 <= i; i--) {
645660
struct exclude *x = el->excludes[i];
646661
const char *exclude = x->pattern;
647-
int to_exclude = x->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
648662
int prefix = x->nowildcardlen;
649663

650664
if (x->flags & EXC_FLAG_MUSTBEDIR) {
@@ -659,43 +673,80 @@ int excluded_from_list(const char *pathname,
659673
pathlen - (basename - pathname),
660674
exclude, prefix, x->patternlen,
661675
x->flags))
662-
return to_exclude;
676+
return x;
663677
continue;
664678
}
665679

666680
assert(x->baselen == 0 || x->base[x->baselen - 1] == '/');
667681
if (match_pathname(pathname, pathlen,
668682
x->base, x->baselen ? x->baselen - 1 : 0,
669683
exclude, prefix, x->patternlen, x->flags))
670-
return to_exclude;
684+
return x;
671685
}
686+
return NULL; /* undecided */
687+
}
688+
689+
/*
690+
* Scan the list and let the last match determine the fate.
691+
* Return 1 for exclude, 0 for include and -1 for undecided.
692+
*/
693+
int is_excluded_from_list(const char *pathname,
694+
int pathlen, const char *basename, int *dtype,
695+
struct exclude_list *el)
696+
{
697+
struct exclude *exclude;
698+
exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el);
699+
if (exclude)
700+
return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
672701
return -1; /* undecided */
673702
}
674703

675-
static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
704+
/*
705+
* Loads the exclude lists for the directory containing pathname, then
706+
* scans all exclude lists to determine whether pathname is excluded.
707+
* Returns the exclude_list element which matched, or NULL for
708+
* undecided.
709+
*/
710+
static struct exclude *last_exclude_matching(struct dir_struct *dir,
711+
const char *pathname,
712+
int *dtype_p)
676713
{
677714
int pathlen = strlen(pathname);
678715
int st;
716+
struct exclude *exclude;
679717
const char *basename = strrchr(pathname, '/');
680718
basename = (basename) ? basename+1 : pathname;
681719

682720
prep_exclude(dir, pathname, basename-pathname);
683721
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
684-
switch (excluded_from_list(pathname, pathlen, basename,
685-
dtype_p, &dir->exclude_list[st])) {
686-
case 0:
687-
return 0;
688-
case 1:
689-
return 1;
690-
}
722+
exclude = last_exclude_matching_from_list(
723+
pathname, pathlen, basename, dtype_p,
724+
&dir->exclude_list[st]);
725+
if (exclude)
726+
return exclude;
691727
}
728+
return NULL;
729+
}
730+
731+
/*
732+
* Loads the exclude lists for the directory containing pathname, then
733+
* scans all exclude lists to determine whether pathname is excluded.
734+
* Returns 1 if true, otherwise 0.
735+
*/
736+
static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
737+
{
738+
struct exclude *exclude =
739+
last_exclude_matching(dir, pathname, dtype_p);
740+
if (exclude)
741+
return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
692742
return 0;
693743
}
694744

695745
void path_exclude_check_init(struct path_exclude_check *check,
696746
struct dir_struct *dir)
697747
{
698748
check->dir = dir;
749+
check->exclude = NULL;
699750
strbuf_init(&check->path, 256);
700751
}
701752

@@ -705,49 +756,77 @@ void path_exclude_check_clear(struct path_exclude_check *check)
705756
}
706757

707758
/*
708-
* Is this name excluded? This is for a caller like show_files() that
709-
* do not honor directory hierarchy and iterate through paths that are
710-
* possibly in an ignored directory.
759+
* For each subdirectory in name, starting with the top-most, checks
760+
* to see if that subdirectory is excluded, and if so, returns the
761+
* corresponding exclude structure. Otherwise, checks whether name
762+
* itself (which is presumably a file) is excluded.
711763
*
712764
* A path to a directory known to be excluded is left in check->path to
713765
* optimize for repeated checks for files in the same excluded directory.
714766
*/
715-
int path_excluded(struct path_exclude_check *check,
716-
const char *name, int namelen, int *dtype)
767+
struct exclude *last_exclude_matching_path(struct path_exclude_check *check,
768+
const char *name, int namelen,
769+
int *dtype)
717770
{
718771
int i;
719772
struct strbuf *path = &check->path;
773+
struct exclude *exclude;
720774

721775
/*
722776
* we allow the caller to pass namelen as an optimization; it
723777
* must match the length of the name, as we eventually call
724-
* excluded() on the whole name string.
778+
* is_excluded() on the whole name string.
725779
*/
726780
if (namelen < 0)
727781
namelen = strlen(name);
728782

783+
/*
784+
* If path is non-empty, and name is equal to path or a
785+
* subdirectory of path, name should be excluded, because
786+
* it's inside a directory which is already known to be
787+
* excluded and was previously left in check->path.
788+
*/
729789
if (path->len &&
730790
path->len <= namelen &&
731791
!memcmp(name, path->buf, path->len) &&
732792
(!name[path->len] || name[path->len] == '/'))
733-
return 1;
793+
return check->exclude;
734794

735795
strbuf_setlen(path, 0);
736796
for (i = 0; name[i]; i++) {
737797
int ch = name[i];
738798

739799
if (ch == '/') {
740800
int dt = DT_DIR;
741-
if (excluded(check->dir, path->buf, &dt))
742-
return 1;
801+
exclude = last_exclude_matching(check->dir,
802+
path->buf, &dt);
803+
if (exclude) {
804+
check->exclude = exclude;
805+
return exclude;
806+
}
743807
}
744808
strbuf_addch(path, ch);
745809
}
746810

747811
/* An entry in the index; cannot be a directory with subentries */
748812
strbuf_setlen(path, 0);
749813

750-
return excluded(check->dir, name, dtype);
814+
return last_exclude_matching(check->dir, name, dtype);
815+
}
816+
817+
/*
818+
* Is this name excluded? This is for a caller like show_files() that
819+
* do not honor directory hierarchy and iterate through paths that are
820+
* possibly in an ignored directory.
821+
*/
822+
int is_path_excluded(struct path_exclude_check *check,
823+
const char *name, int namelen, int *dtype)
824+
{
825+
struct exclude *exclude =
826+
last_exclude_matching_path(check, name, namelen, dtype);
827+
if (exclude)
828+
return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
829+
return 0;
751830
}
752831

753832
static struct dir_entry *dir_entry_new(const char *pathname, int len)
@@ -1047,7 +1126,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
10471126
const struct path_simplify *simplify,
10481127
int dtype, struct dirent *de)
10491128
{
1050-
int exclude = excluded(dir, path->buf, &dtype);
1129+
int exclude = is_excluded(dir, path->buf, &dtype);
10511130
if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
10521131
&& exclude_matches_pathspec(path->buf, path->len, simplify))
10531132
dir_add_ignored(dir, path->buf, path->len);

0 commit comments

Comments
 (0)