Skip to content

Commit c082df2

Browse files
aspiersgitster
authored andcommitted
dir.c: use a single struct exclude_list per source of excludes
Previously each exclude_list could potentially contain patterns from multiple sources. For example dir->exclude_list[EXC_FILE] would typically contain patterns from .git/info/exclude and core.excludesfile, and dir->exclude_list[EXC_DIRS] could contain patterns from multiple per-directory .gitignore files during directory traversal (i.e. when dir->exclude_stack was more than one item deep). We split these composite exclude_lists up into three groups of exclude_lists (EXC_CMDL / EXC_DIRS / EXC_FILE as before), so that each exclude_list now contains patterns from a single source. This will allow us to cleanly track the origin of each pattern simply by adding a src field to struct exclude_list, rather than to struct exclude, which would make memory management of the source string tricky in the EXC_DIRS case where its contents are dynamically generated. Similarly, by moving the filebuf member from struct exclude_stack to struct exclude_list, it allows us to track and subsequently free memory buffers allocated during the parsing of all exclude files, rather than only tracking buffers allocated for files in the EXC_DIRS group. Signed-off-by: Adam Spiers <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f619881 commit c082df2

File tree

6 files changed

+86
-39
lines changed

6 files changed

+86
-39
lines changed

Documentation/technical/api-directory-listing.txt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ marked. If you to exclude files, make sure you have loaded index first.
6767
* Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
6868
sizeof(dir))`.
6969

70-
* Call `add_exclude()` to add single exclude pattern,
71-
`add_excludes_from_file()` to add patterns from a file
72-
(e.g. `.git/info/exclude`), and/or set `dir.exclude_per_dir`. A
73-
short-hand function `setup_standard_excludes()` can be used to set up
74-
the standard set of exclude settings.
70+
* To add single exclude pattern, call `add_exclude_list()` and then
71+
`add_exclude()`.
72+
73+
* To add patterns from a file (e.g. `.git/info/exclude`), call
74+
`add_excludes_from_file()` , and/or set `dir.exclude_per_dir`. A
75+
short-hand function `setup_standard_excludes()` can be used to set
76+
up the standard set of exclude settings.
7577

7678
* Set options described in the Data Structure section above.
7779

builtin/clean.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,10 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
9797
if (!ignored)
9898
setup_standard_excludes(&dir);
9999

100+
add_exclude_list(&dir, EXC_CMDL);
100101
for (i = 0; i < exclude_list.nr; i++)
101102
add_exclude(exclude_list.items[i].string, "", 0,
102-
&dir.exclude_list[EXC_CMDL]);
103+
&dir.exclude_list_group[EXC_CMDL].el[0]);
103104

104105
pathspec = get_pathspec(prefix, argv);
105106

builtin/ls-files.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,10 @@ static int option_parse_z(const struct option *opt,
420420
static int option_parse_exclude(const struct option *opt,
421421
const char *arg, int unset)
422422
{
423-
struct exclude_list *list = opt->value;
423+
struct exclude_list_group *group = opt->value;
424424

425425
exc_given = 1;
426-
add_exclude(arg, "", 0, list);
426+
add_exclude(arg, "", 0, &group->el[0]);
427427

428428
return 0;
429429
}
@@ -488,7 +488,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
488488
"show unmerged files in the output"),
489489
OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
490490
"show resolve-undo information"),
491-
{ OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
491+
{ OPTION_CALLBACK, 'x', "exclude",
492+
&dir.exclude_list_group[EXC_CMDL], "pattern",
492493
"skip files matching pattern",
493494
0, option_parse_exclude },
494495
{ OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
@@ -523,6 +524,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
523524
if (read_cache() < 0)
524525
die("index file corrupt");
525526

527+
add_exclude_list(&dir, EXC_CMDL);
526528
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
527529
ls_files_usage, 0);
528530
if (show_tag || show_valid_bit) {

dir.c

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -411,15 +411,16 @@ void clear_exclude_list(struct exclude_list *el)
411411
for (i = 0; i < el->nr; i++)
412412
free(el->excludes[i]);
413413
free(el->excludes);
414+
free(el->filebuf);
414415

415416
el->nr = 0;
416417
el->excludes = NULL;
418+
el->filebuf = NULL;
417419
}
418420

419421
int add_excludes_from_file_to_list(const char *fname,
420422
const char *base,
421423
int baselen,
422-
char **buf_p,
423424
struct exclude_list *el,
424425
int check_index)
425426
{
@@ -460,8 +461,7 @@ int add_excludes_from_file_to_list(const char *fname,
460461
close(fd);
461462
}
462463

463-
if (buf_p)
464-
*buf_p = buf;
464+
el->filebuf = buf;
465465
entry = buf;
466466
for (i = 0; i < size; i++) {
467467
if (buf[i] == '\n') {
@@ -475,10 +475,26 @@ int add_excludes_from_file_to_list(const char *fname,
475475
return 0;
476476
}
477477

478+
struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type)
479+
{
480+
struct exclude_list *el;
481+
struct exclude_list_group *group;
482+
483+
group = &dir->exclude_list_group[group_type];
484+
ALLOC_GROW(group->el, group->nr + 1, group->alloc);
485+
el = &group->el[group->nr++];
486+
memset(el, 0, sizeof(*el));
487+
return el;
488+
}
489+
490+
/*
491+
* Used to set up core.excludesfile and .git/info/exclude lists.
492+
*/
478493
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
479494
{
480-
if (add_excludes_from_file_to_list(fname, "", 0, NULL,
481-
&dir->exclude_list[EXC_FILE], 0) < 0)
495+
struct exclude_list *el;
496+
el = add_exclude_list(dir, EXC_FILE);
497+
if (add_excludes_from_file_to_list(fname, "", 0, el, 0) < 0)
482498
die("cannot use %s as an exclude file", fname);
483499
}
484500

@@ -488,6 +504,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
488504
*/
489505
static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
490506
{
507+
struct exclude_list_group *group;
491508
struct exclude_list *el;
492509
struct exclude_stack *stk = NULL;
493510
int current;
@@ -496,17 +513,20 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
496513
(baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
497514
return; /* too long a path -- ignore */
498515

499-
/* Pop the directories that are not the prefix of the path being checked. */
500-
el = &dir->exclude_list[EXC_DIRS];
516+
group = &dir->exclude_list_group[EXC_DIRS];
517+
518+
/* Pop the exclude lists from the EXCL_DIRS exclude_list_group
519+
* which originate from directories not in the prefix of the
520+
* path being checked. */
501521
while ((stk = dir->exclude_stack) != NULL) {
502522
if (stk->baselen <= baselen &&
503523
!strncmp(dir->basebuf, base, stk->baselen))
504524
break;
525+
el = &group->el[dir->exclude_stack->exclude_ix];
505526
dir->exclude_stack = stk->prev;
506-
while (stk->exclude_ix < el->nr)
507-
free(el->excludes[--el->nr]);
508-
free(stk->filebuf);
527+
clear_exclude_list(el);
509528
free(stk);
529+
group->nr--;
510530
}
511531

512532
/* Read from the parent directories and push them down. */
@@ -527,13 +547,14 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
527547
}
528548
stk->prev = dir->exclude_stack;
529549
stk->baselen = cp - base;
530-
stk->exclude_ix = el->nr;
531550
memcpy(dir->basebuf + current, base + current,
532551
stk->baselen - current);
533552
strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
553+
el = add_exclude_list(dir, EXC_DIRS);
554+
stk->exclude_ix = group->nr - 1;
534555
add_excludes_from_file_to_list(dir->basebuf,
535556
dir->basebuf, stk->baselen,
536-
&stk->filebuf, el, 1);
557+
el, 1);
537558
dir->exclude_stack = stk;
538559
current = stk->baselen;
539560
}
@@ -679,18 +700,23 @@ static struct exclude *last_exclude_matching(struct dir_struct *dir,
679700
int *dtype_p)
680701
{
681702
int pathlen = strlen(pathname);
682-
int st;
703+
int i, j;
704+
struct exclude_list_group *group;
683705
struct exclude *exclude;
684706
const char *basename = strrchr(pathname, '/');
685707
basename = (basename) ? basename+1 : pathname;
686708

687709
prep_exclude(dir, pathname, basename-pathname);
688-
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
689-
exclude = last_exclude_matching_from_list(
690-
pathname, pathlen, basename, dtype_p,
691-
&dir->exclude_list[st]);
692-
if (exclude)
693-
return exclude;
710+
711+
for (i = EXC_CMDL; i <= EXC_FILE; i++) {
712+
group = &dir->exclude_list_group[i];
713+
for (j = group->nr - 1; j >= 0; j--) {
714+
exclude = last_exclude_matching_from_list(
715+
pathname, pathlen, basename, dtype_p,
716+
&group->el[j]);
717+
if (exclude)
718+
return exclude;
719+
}
694720
}
695721
return NULL;
696722
}

dir.h

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ struct dir_entry {
1616
#define EXC_FLAG_NEGATIVE 16
1717

1818
/*
19-
* Each .gitignore file will be parsed into patterns which are then
20-
* appended to the relevant exclude_list (either EXC_DIRS or
21-
* EXC_FILE). exclude_lists are also used to represent the list of
22-
* --exclude values passed via CLI args (EXC_CMDL).
19+
* Each excludes file will be parsed into a fresh exclude_list which
20+
* is appended to the relevant exclude_list_group (either EXC_DIRS or
21+
* EXC_FILE). An exclude_list within the EXC_CMDL exclude_list_group
22+
* can also be used to represent the list of --exclude values passed
23+
* via CLI args.
2324
*/
2425
struct exclude_list {
2526
int nr;
2627
int alloc;
28+
/* remember pointer to exclude file contents so we can free() */
29+
char *filebuf;
30+
2731
struct exclude {
2832
const char *pattern;
2933
int patternlen;
@@ -42,9 +46,13 @@ struct exclude_list {
4246
*/
4347
struct exclude_stack {
4448
struct exclude_stack *prev; /* the struct exclude_stack for the parent directory */
45-
char *filebuf; /* remember pointer to per-directory exclude file contents so we can free() */
4649
int baselen;
47-
int exclude_ix;
50+
int exclude_ix; /* index of exclude_list within EXC_DIRS exclude_list_group */
51+
};
52+
53+
struct exclude_list_group {
54+
int nr, alloc;
55+
struct exclude_list *el;
4856
};
4957

5058
struct dir_struct {
@@ -62,16 +70,23 @@ struct dir_struct {
6270

6371
/* Exclude info */
6472
const char *exclude_per_dir;
65-
struct exclude_list exclude_list[3];
73+
6674
/*
67-
* We maintain three exclude pattern lists:
75+
* We maintain three groups of exclude pattern lists:
76+
*
6877
* EXC_CMDL lists patterns explicitly given on the command line.
6978
* EXC_DIRS lists patterns obtained from per-directory ignore files.
70-
* EXC_FILE lists patterns from fallback ignore files.
79+
* EXC_FILE lists patterns from fallback ignore files, e.g.
80+
* - .git/info/exclude
81+
* - core.excludesfile
82+
*
83+
* Each group contains multiple exclude lists, a single list
84+
* per source.
7185
*/
7286
#define EXC_CMDL 0
7387
#define EXC_DIRS 1
7488
#define EXC_FILE 2
89+
struct exclude_list_group exclude_list_group[3];
7590

7691
/*
7792
* Temporary variables which are used during loading of the
@@ -129,8 +144,9 @@ extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, c
129144
extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);
130145

131146

147+
extern struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type);
132148
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
133-
char **buf_p, struct exclude_list *el, int check_index);
149+
struct exclude_list *el, int check_index);
134150
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
135151
extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);
136152
extern void add_exclude(const char *string, const char *base,

unpack-trees.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
10191019
if (!core_apply_sparse_checkout || !o->update)
10201020
o->skip_sparse_checkout = 1;
10211021
if (!o->skip_sparse_checkout) {
1022-
if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, NULL, &el, 0) < 0)
1022+
if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, &el, 0) < 0)
10231023
o->skip_sparse_checkout = 1;
10241024
else
10251025
o->el = &el;

0 commit comments

Comments
 (0)