From 453ec4bdf403c2e89892266a0a660c21680d3f9d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 May 2006 19:02:14 -0700 Subject: libify git-ls-files directory traversal This moves the core directory traversal and filename exclusion logic into the general git library, making it available for other users directly. If we ever want to do "git commit" or "git add" as a built-in (and we do), we want to be able to handle most of git-ls-files as a library. NOTE! Not all of git-ls-files is libified by this. The index matching and pathspec prefix calculation is still in ls-files.c, but this is a big part of it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 dir.h (limited to 'dir.h') diff --git a/dir.h b/dir.h new file mode 100644 index 0000000000..e8fc441e7f --- /dev/null +++ b/dir.h @@ -0,0 +1,50 @@ +#ifndef DIR_H +#define DIR_H + +/* + * We maintain three exclude pattern lists: + * EXC_CMDL lists patterns explicitly given on the command line. + * EXC_DIRS lists patterns obtained from per-directory ignore files. + * EXC_FILE lists patterns from fallback ignore files. + */ +#define EXC_CMDL 0 +#define EXC_DIRS 1 +#define EXC_FILE 2 + + +struct dir_entry { + int len; + char name[FLEX_ARRAY]; /* more */ +}; + +struct exclude_list { + int nr; + int alloc; + struct exclude { + const char *pattern; + const char *base; + int baselen; + } **excludes; +}; + +struct dir_struct { + int nr, alloc; + unsigned int show_ignored:1, + show_other_directories:1, + hide_empty_directories:1; + struct dir_entry **entries; + + /* Exclude info */ + const char *exclude_per_dir; + struct exclude_list exclude_list[3]; +}; + +extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen); +extern int excluded(struct dir_struct *, const char *); +extern void add_excludes_from_file(struct dir_struct *, const char *fname); +extern void add_exclude(const char *string, const char *base, + int baselen, struct exclude_list *which); +extern int push_exclude_per_directory(struct dir_struct *, + const char *base, int baselen); + +#endif -- cgit v1.3 From b4189aa84873718f80c62846cd53e803b5f72362 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 May 2006 19:46:16 -0700 Subject: Clean up git-ls-file directory walking library interface This moves the code to add the per-directory ignore files for the base directory into the library routine. That not only allows us to turn the function push_exclude_per_directory() static again, it also simplifies the library interface a lot (the caller no longer needs to worry about any of the per-directory exclude files at all). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 28 +++++++++++++++++++++++++++- dir.h | 2 -- ls-files.c | 22 +--------------------- 3 files changed, 28 insertions(+), 24 deletions(-) (limited to 'dir.h') diff --git a/dir.c b/dir.c index 3f41a5dfea..d40b62e1c1 100644 --- a/dir.c +++ b/dir.c @@ -78,7 +78,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname) die("cannot use %s as an exclude file", fname); } -int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) { char exclude_file[PATH_MAX]; struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; @@ -289,6 +289,32 @@ static int cmp_name(const void *p1, const void *p2) int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen) { + /* + * Make sure to do the per-directory exclude for all the + * directories leading up to our base. + */ + if (baselen) { + if (dir->exclude_per_dir) { + char *p, *pp = xmalloc(baselen+1); + memcpy(pp, base, baselen+1); + p = pp; + while (1) { + char save = *p; + *p = 0; + push_exclude_per_directory(dir, pp, p-pp); + *p++ = save; + if (!save) + break; + p = strchr(p, '/'); + if (p) + p++; + else + p = pp + baselen; + } + free(pp); + } + } + read_directory_recursive(dir, path, base, baselen); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; diff --git a/dir.h b/dir.h index e8fc441e7f..4f65f57085 100644 --- a/dir.h +++ b/dir.h @@ -44,7 +44,5 @@ extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which); -extern int push_exclude_per_directory(struct dir_struct *, - const char *base, int baselen); #endif diff --git a/ls-files.c b/ls-files.c index 89941a3ff8..dfe1481a35 100644 --- a/ls-files.c +++ b/ls-files.c @@ -215,28 +215,8 @@ static void show_files(struct dir_struct *dir) const char *path = ".", *base = ""; int baselen = prefix_len; - if (baselen) { + if (baselen) path = base = prefix; - if (dir->exclude_per_dir) { - char *p, *pp = xmalloc(baselen+1); - memcpy(pp, prefix, baselen+1); - p = pp; - while (1) { - char save = *p; - *p = 0; - push_exclude_per_directory(dir, pp, p-pp); - *p++ = save; - if (!save) - break; - p = strchr(p, '/'); - if (p) - p++; - else - p = pp + baselen; - } - free(pp); - } - } read_directory(dir, path, base, baselen); if (show_others) show_other_files(dir); -- cgit v1.3 From 3c6a370b0ee78114a656acba04fddf306252b9d5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 May 2006 16:07:51 -0700 Subject: Move pathspec matching from builtin-add.c into dir.c I'll use it for builtin-rm.c too. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-add.c | 82 +---------------------------------------------------------- dir.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dir.h | 3 +++ 3 files changed, 84 insertions(+), 81 deletions(-) (limited to 'dir.h') diff --git a/builtin-add.c b/builtin-add.c index 37243f8c36..6166f66bce 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -12,86 +12,6 @@ static const char builtin_add_usage[] = "git-add [-n] [-v] ..."; -static int common_prefix(const char **pathspec) -{ - const char *path, *slash, *next; - int prefix; - - if (!pathspec) - return 0; - - path = *pathspec; - slash = strrchr(path, '/'); - if (!slash) - return 0; - - prefix = slash - path + 1; - while ((next = *++pathspec) != NULL) { - int len = strlen(next); - if (len >= prefix && !memcmp(path, next, len)) - continue; - for (;;) { - if (!len) - return 0; - if (next[--len] != '/') - continue; - if (memcmp(path, next, len+1)) - continue; - prefix = len + 1; - break; - } - } - return prefix; -} - -static int match_one(const char *match, const char *name, int namelen) -{ - int matchlen; - - /* If the match was just the prefix, we matched */ - matchlen = strlen(match); - if (!matchlen) - return 1; - - /* - * If we don't match the matchstring exactly, - * we need to match by fnmatch - */ - if (strncmp(match, name, matchlen)) - return !fnmatch(match, name, 0); - - /* - * If we did match the string exactly, we still - * need to make sure that it happened on a path - * component boundary (ie either the last character - * of the match was '/', or the next character of - * the name was '/' or the terminating NUL. - */ - return match[matchlen-1] == '/' || - name[matchlen] == '/' || - !name[matchlen]; -} - -static int match(const char **pathspec, const char *name, int namelen, int prefix, char *seen) -{ - int retval; - const char *match; - - name += prefix; - namelen -= prefix; - - for (retval = 0; (match = *pathspec++) != NULL; seen++) { - if (retval & *seen) - continue; - match += prefix; - if (match_one(match, name, namelen)) { - retval = 1; - *seen = 1; - } - } - return retval; -} - static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { char *seen; @@ -107,7 +27,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (!match(pathspec, entry->name, entry->len, prefix, seen)) { + if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) { free(entry); continue; } diff --git a/dir.c b/dir.c index d40b62e1c1..d778ecd890 100644 --- a/dir.c +++ b/dir.c @@ -11,6 +11,86 @@ #include "cache.h" #include "dir.h" +int common_prefix(const char **pathspec) +{ + const char *path, *slash, *next; + int prefix; + + if (!pathspec) + return 0; + + path = *pathspec; + slash = strrchr(path, '/'); + if (!slash) + return 0; + + prefix = slash - path + 1; + while ((next = *++pathspec) != NULL) { + int len = strlen(next); + if (len >= prefix && !memcmp(path, next, len)) + continue; + for (;;) { + if (!len) + return 0; + if (next[--len] != '/') + continue; + if (memcmp(path, next, len+1)) + continue; + prefix = len + 1; + break; + } + } + return prefix; +} + +static int match_one(const char *match, const char *name, int namelen) +{ + int matchlen; + + /* If the match was just the prefix, we matched */ + matchlen = strlen(match); + if (!matchlen) + return 1; + + /* + * If we don't match the matchstring exactly, + * we need to match by fnmatch + */ + if (strncmp(match, name, matchlen)) + return !fnmatch(match, name, 0); + + /* + * If we did match the string exactly, we still + * need to make sure that it happened on a path + * component boundary (ie either the last character + * of the match was '/', or the next character of + * the name was '/' or the terminating NUL. + */ + return match[matchlen-1] == '/' || + name[matchlen] == '/' || + !name[matchlen]; +} + +int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen) +{ + int retval; + const char *match; + + name += prefix; + namelen -= prefix; + + for (retval = 0; (match = *pathspec++) != NULL; seen++) { + if (retval & *seen) + continue; + match += prefix; + if (match_one(match, name, namelen)) { + retval = 1; + *seen = 1; + } + } + return retval; +} + void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which) { diff --git a/dir.h b/dir.h index 4f65f57085..56a1b7fce2 100644 --- a/dir.h +++ b/dir.h @@ -39,6 +39,9 @@ struct dir_struct { struct exclude_list exclude_list[3]; }; +extern int common_prefix(const char **pathspec); +extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); + extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen); extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); -- cgit v1.3