From c082df24539329c2e75395cf378f0a3fe187c028 Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Sun, 6 Jan 2013 16:58:03 +0000 Subject: 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 Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'builtin/ls-files.c') diff --git a/builtin/ls-files.c b/builtin/ls-files.c index ef7f99a9ed..0ca9d8e787 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -420,10 +420,10 @@ static int option_parse_z(const struct option *opt, static int option_parse_exclude(const struct option *opt, const char *arg, int unset) { - struct exclude_list *list = opt->value; + struct exclude_list_group *group = opt->value; exc_given = 1; - add_exclude(arg, "", 0, list); + add_exclude(arg, "", 0, &group->el[0]); return 0; } @@ -488,7 +488,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) "show unmerged files in the output"), OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo, "show resolve-undo information"), - { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern", + { OPTION_CALLBACK, 'x', "exclude", + &dir.exclude_list_group[EXC_CMDL], "pattern", "skip files matching pattern", 0, option_parse_exclude }, { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file", @@ -523,6 +524,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (read_cache() < 0) die("index file corrupt"); + add_exclude_list(&dir, EXC_CMDL); argc = parse_options(argc, argv, prefix, builtin_ls_files_options, ls_files_usage, 0); if (show_tag || show_valid_bit) { -- cgit v1.2.3 From c04318e46aae79b8b8df059e2118519d83dfee12 Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Sun, 6 Jan 2013 16:58:04 +0000 Subject: dir.c: keep track of where patterns came from For exclude patterns read in from files, the filename is stored in the exclude list, and the originating line number is stored in the individual exclude (counting starting at 1). For exclude patterns provided on the command line, a string describing the source of the patterns is stored in the exclude list, and the sequence number assigned to each exclude pattern is negative, with counting starting at -1. So for example the 2nd pattern provided via --exclude would be numbered -2. This allows any future consumers of that data to easily distinguish between exclude patterns from files vs. from the CLI. Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- builtin/clean.c | 4 ++-- builtin/ls-files.c | 5 +++-- dir.c | 26 ++++++++++++++++++++------ dir.h | 21 +++++++++++++++++++-- 4 files changed, 44 insertions(+), 12 deletions(-) (limited to 'builtin/ls-files.c') diff --git a/builtin/clean.c b/builtin/clean.c index dd8973700a..b098288ad1 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -97,10 +97,10 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (!ignored) setup_standard_excludes(&dir); - add_exclude_list(&dir, EXC_CMDL); + add_exclude_list(&dir, EXC_CMDL, "--exclude option"); for (i = 0; i < exclude_list.nr; i++) add_exclude(exclude_list.items[i].string, "", 0, - &dir.exclude_list_group[EXC_CMDL].el[0]); + &dir.exclude_list_group[EXC_CMDL].el[0], -(i+1)); pathspec = get_pathspec(prefix, argv); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 0ca9d8e787..fa9ccb80dc 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -35,6 +35,7 @@ static int error_unmatch; static char *ps_matched; static const char *with_tree; static int exc_given; +static int exclude_args; static const char *tag_cached = ""; static const char *tag_unmerged = ""; @@ -423,7 +424,7 @@ static int option_parse_exclude(const struct option *opt, struct exclude_list_group *group = opt->value; exc_given = 1; - add_exclude(arg, "", 0, &group->el[0]); + add_exclude(arg, "", 0, &group->el[0], --exclude_args); return 0; } @@ -524,7 +525,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (read_cache() < 0) die("index file corrupt"); - add_exclude_list(&dir, EXC_CMDL); + add_exclude_list(&dir, EXC_CMDL, "--exclude option"); argc = parse_options(argc, argv, prefix, builtin_ls_files_options, ls_files_usage, 0); if (show_tag || show_valid_bit) { diff --git a/dir.c b/dir.c index 3a15cb9bc9..d3f462bd15 100644 --- a/dir.c +++ b/dir.c @@ -349,7 +349,7 @@ void parse_exclude_pattern(const char **pattern, } void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *el) + int baselen, struct exclude_list *el, int srcpos) { struct exclude *x; int patternlen; @@ -373,8 +373,10 @@ void add_exclude(const char *string, const char *base, x->base = base; x->baselen = baselen; x->flags = flags; + x->srcpos = srcpos; ALLOC_GROW(el->excludes, el->nr + 1, el->alloc); el->excludes[el->nr++] = x; + x->el = el; } static void *read_skip_worktree_file_from_index(const char *path, size_t *size) @@ -425,7 +427,7 @@ int add_excludes_from_file_to_list(const char *fname, int check_index) { struct stat st; - int fd, i; + int fd, i, lineno = 1; size_t size = 0; char *buf, *entry; @@ -467,15 +469,17 @@ int add_excludes_from_file_to_list(const char *fname, if (buf[i] == '\n') { if (entry != buf + i && entry[0] != '#') { buf[i - (i && buf[i-1] == '\r')] = 0; - add_exclude(entry, base, baselen, el); + add_exclude(entry, base, baselen, el, lineno); } + lineno++; entry = buf + i + 1; } } return 0; } -struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type) +struct exclude_list *add_exclude_list(struct dir_struct *dir, + int group_type, const char *src) { struct exclude_list *el; struct exclude_list_group *group; @@ -484,6 +488,7 @@ struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type) ALLOC_GROW(group->el, group->nr + 1, group->alloc); el = &group->el[group->nr++]; memset(el, 0, sizeof(*el)); + el->src = src; return el; } @@ -493,7 +498,7 @@ struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type) void add_excludes_from_file(struct dir_struct *dir, const char *fname) { struct exclude_list *el; - el = add_exclude_list(dir, EXC_FILE); + el = add_exclude_list(dir, EXC_FILE, fname); if (add_excludes_from_file_to_list(fname, "", 0, el, 0) < 0) die("cannot use %s as an exclude file", fname); } @@ -524,6 +529,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) break; el = &group->el[dir->exclude_stack->exclude_ix]; dir->exclude_stack = stk->prev; + free((char *)el->src); /* see strdup() below */ clear_exclude_list(el); free(stk); group->nr--; @@ -550,7 +556,15 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) memcpy(dir->basebuf + current, base + current, stk->baselen - current); strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir); - el = add_exclude_list(dir, EXC_DIRS); + /* + * dir->basebuf gets reused by the traversal, but we + * need fname to remain unchanged to ensure the src + * member of each struct exclude correctly + * back-references its source file. Other invocations + * of add_exclude_list provide stable strings, so we + * strdup() and free() here in the caller. + */ + el = add_exclude_list(dir, EXC_DIRS, strdup(dir->basebuf)); stk->exclude_ix = group->nr - 1; add_excludes_from_file_to_list(dir->basebuf, dir->basebuf, stk->baselen, diff --git a/dir.h b/dir.h index c4d88dbc58..64c410e132 100644 --- a/dir.h +++ b/dir.h @@ -25,16 +25,32 @@ struct dir_entry { struct exclude_list { int nr; int alloc; + /* remember pointer to exclude file contents so we can free() */ char *filebuf; + /* origin of list, e.g. path to filename, or descriptive string */ + const char *src; + struct exclude { + /* + * This allows callers of last_exclude_matching() etc. + * to determine the origin of the matching pattern. + */ + struct exclude_list *el; + const char *pattern; int patternlen; int nowildcardlen; const char *base; int baselen; int flags; + + /* + * Counting starts from 1 for line numbers in ignore files, + * and from -1 decrementing for patterns from CLI args. + */ + int srcpos; } **excludes; }; @@ -144,13 +160,14 @@ extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, c extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype); -extern struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type); +extern struct exclude_list *add_exclude_list(struct dir_struct *dir, + int group_type, const char *src); extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, struct exclude_list *el, int check_index); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen); extern void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *el); + int baselen, struct exclude_list *el, int srcpos); extern void clear_exclude_list(struct exclude_list *el); extern int file_exists(const char *); -- cgit v1.2.3 From 72aeb18772deeb386da7dd8997b969877bd29e41 Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Wed, 16 Jan 2013 13:25:58 +0000 Subject: clean.c, ls-files.c: respect encapsulation of exclude_list_groups Consumers of the dir.c traversal API should avoid assuming knowledge of the internal implementation of exclude_list_groups. Therefore when adding items to an exclude list, it should be accessed via the pointer returned from add_exclude_list(), rather than by referencing a location within dir.exclude_list_groups[EXC_CMDL]. Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- builtin/clean.c | 6 +++--- builtin/ls-files.c | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'builtin/ls-files.c') diff --git a/builtin/clean.c b/builtin/clean.c index b098288ad1..b9cb7ad4e0 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -45,6 +45,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) static const char **pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; + struct exclude_list *el; const char *qname; char *seen = NULL; struct option options[] = { @@ -97,10 +98,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (!ignored) setup_standard_excludes(&dir); - add_exclude_list(&dir, EXC_CMDL, "--exclude option"); + el = add_exclude_list(&dir, EXC_CMDL, "--exclude option"); for (i = 0; i < exclude_list.nr; i++) - add_exclude(exclude_list.items[i].string, "", 0, - &dir.exclude_list_group[EXC_CMDL].el[0], -(i+1)); + add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1)); pathspec = get_pathspec(prefix, argv); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index fa9ccb80dc..b4d8b017a8 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -421,10 +421,10 @@ static int option_parse_z(const struct option *opt, static int option_parse_exclude(const struct option *opt, const char *arg, int unset) { - struct exclude_list_group *group = opt->value; + struct string_list *exclude_list = opt->value; exc_given = 1; - add_exclude(arg, "", 0, &group->el[0], --exclude_args); + string_list_append(exclude_list, arg); return 0; } @@ -453,9 +453,11 @@ static int option_parse_exclude_standard(const struct option *opt, int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) { - int require_work_tree = 0, show_tag = 0; + int require_work_tree = 0, show_tag = 0, i; const char *max_prefix; struct dir_struct dir; + struct exclude_list *el; + struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct option builtin_ls_files_options[] = { { OPTION_CALLBACK, 'z', NULL, NULL, NULL, "paths are separated with NUL character", @@ -490,7 +492,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo, "show resolve-undo information"), { OPTION_CALLBACK, 'x', "exclude", - &dir.exclude_list_group[EXC_CMDL], "pattern", + &exclude_list, "pattern", "skip files matching pattern", 0, option_parse_exclude }, { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file", @@ -525,9 +527,12 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (read_cache() < 0) die("index file corrupt"); - add_exclude_list(&dir, EXC_CMDL, "--exclude option"); argc = parse_options(argc, argv, prefix, builtin_ls_files_options, ls_files_usage, 0); + el = add_exclude_list(&dir, EXC_CMDL, "--exclude option"); + for (i = 0; i < exclude_list.nr; i++) { + add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args); + } if (show_tag || show_valid_bit) { tag_cached = "H "; tag_unmerged = "M "; -- cgit v1.2.3