From: Junio C Hamano Date: Wed, 29 May 2013 21:30:10 +0000 (-0700) Subject: Merge branch 'jn/config-ignore-inaccessible' X-Git-Tag: v1.8.4-rc0~246 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/7ebb906ddd69628fc9874b1266257ae2256b8f14?ds=inline;hp=-c Merge branch 'jn/config-ignore-inaccessible' When $HOME is misconfigured to point at an unreadable directory, we used to complain and die. This loosens the check. * jn/config-ignore-inaccessible: config: allow inaccessible configuration under $HOME --- 7ebb906ddd69628fc9874b1266257ae2256b8f14 diff --combined dir.c index a5926fbd1a,e2ea111680..ee4d04dc8b --- a/dir.c +++ b/dir.c @@@ -17,21 -17,7 +17,21 @@@ struct path_simplify const char *path; }; -static int read_directory_recursive(struct dir_struct *dir, const char *path, int len, +/* + * Tells read_directory_recursive how a file or directory should be treated. + * Values are ordered by significance, e.g. if a directory contains both + * excluded and untracked files, it is listed as untracked because + * path_untracked > path_excluded. + */ +enum path_treatment { + path_none = 0, + path_recurse, + path_excluded, + path_untracked +}; + +static enum path_treatment read_directory_recursive(struct dir_struct *dir, + const char *path, int len, int check_only, const struct path_simplify *simplify); static int get_dtype(struct dirent *de, const char *path, int len); @@@ -592,6 -578,78 +592,6 @@@ void add_excludes_from_file(struct dir_ die("cannot use %s as an exclude file", fname); } -/* - * Loads the per-directory exclude list for the substring of base - * which has a char length of baselen. - */ -static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) -{ - struct exclude_list_group *group; - struct exclude_list *el; - struct exclude_stack *stk = NULL; - int current; - - if ((!dir->exclude_per_dir) || - (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX)) - return; /* too long a path -- ignore */ - - group = &dir->exclude_list_group[EXC_DIRS]; - - /* Pop the exclude lists from the EXCL_DIRS exclude_list_group - * which originate from directories not in the prefix of the - * path being checked. */ - while ((stk = dir->exclude_stack) != NULL) { - if (stk->baselen <= baselen && - !strncmp(dir->basebuf, base, stk->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--; - } - - /* Read from the parent directories and push them down. */ - current = stk ? stk->baselen : -1; - while (current < baselen) { - struct exclude_stack *stk = xcalloc(1, sizeof(*stk)); - const char *cp; - - if (current < 0) { - cp = base; - current = 0; - } - else { - cp = strchr(base + current + 1, '/'); - if (!cp) - die("oops in prep_exclude"); - cp++; - } - stk->prev = dir->exclude_stack; - stk->baselen = cp - base; - memcpy(dir->basebuf + current, base + current, - stk->baselen - current); - strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir); - /* - * 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, - el, 1); - dir->exclude_stack = stk; - current = stk->baselen; - } - dir->basebuf[baselen] = '\0'; -} - int match_basename(const char *basename, int basenamelen, const char *pattern, int prefix, int patternlen, int flags) @@@ -737,13 -795,25 +737,13 @@@ int is_excluded_from_list(const char *p return -1; /* undecided */ } -/* - * Loads the exclude lists for the directory containing pathname, then - * scans all exclude lists to determine whether pathname is excluded. - * Returns the exclude_list element which matched, or NULL for - * undecided. - */ -static struct exclude *last_exclude_matching(struct dir_struct *dir, - const char *pathname, - int *dtype_p) +static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, + const char *pathname, int pathlen, const char *basename, + int *dtype_p) { - int pathlen = strlen(pathname); int i, j; struct exclude_list_group *group; struct exclude *exclude; - const char *basename = strrchr(pathname, '/'); - basename = (basename) ? basename+1 : pathname; - - prep_exclude(dir, pathname, basename-pathname); - for (i = EXC_CMDL; i <= EXC_FILE; i++) { group = &dir->exclude_list_group[i]; for (j = group->nr - 1; j >= 0; j--) { @@@ -758,131 -828,101 +758,131 @@@ } /* - * Loads the exclude lists for the directory containing pathname, then - * scans all exclude lists to determine whether pathname is excluded. - * Returns 1 if true, otherwise 0. + * Loads the per-directory exclude list for the substring of base + * which has a char length of baselen. */ -static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) -{ - struct exclude *exclude = - last_exclude_matching(dir, pathname, dtype_p); - if (exclude) - return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; - return 0; -} - -void path_exclude_check_init(struct path_exclude_check *check, - struct dir_struct *dir) +static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { - check->dir = dir; - check->exclude = NULL; - strbuf_init(&check->path, 256); -} + struct exclude_list_group *group; + struct exclude_list *el; + struct exclude_stack *stk = NULL; + int current; -void path_exclude_check_clear(struct path_exclude_check *check) -{ - strbuf_release(&check->path); -} + group = &dir->exclude_list_group[EXC_DIRS]; -/* - * For each subdirectory in name, starting with the top-most, checks - * to see if that subdirectory is excluded, and if so, returns the - * corresponding exclude structure. Otherwise, checks whether name - * itself (which is presumably a file) is excluded. - * - * A path to a directory known to be excluded is left in check->path to - * optimize for repeated checks for files in the same excluded directory. - */ -struct exclude *last_exclude_matching_path(struct path_exclude_check *check, - const char *name, int namelen, - int *dtype) -{ - int i; - struct strbuf *path = &check->path; - struct exclude *exclude; + /* Pop the exclude lists from the EXCL_DIRS exclude_list_group + * which originate from directories not in the prefix of the + * path being checked. */ + while ((stk = dir->exclude_stack) != NULL) { + if (stk->baselen <= baselen && + !strncmp(dir->basebuf, base, stk->baselen)) + break; + el = &group->el[dir->exclude_stack->exclude_ix]; + dir->exclude_stack = stk->prev; + dir->exclude = NULL; + free((char *)el->src); /* see strdup() below */ + clear_exclude_list(el); + free(stk); + group->nr--; + } - /* - * we allow the caller to pass namelen as an optimization; it - * must match the length of the name, as we eventually call - * is_excluded() on the whole name string. - */ - if (namelen < 0) - namelen = strlen(name); + /* Skip traversing into sub directories if the parent is excluded */ + if (dir->exclude) + return; - /* - * If path is non-empty, and name is equal to path or a - * subdirectory of path, name should be excluded, because - * it's inside a directory which is already known to be - * excluded and was previously left in check->path. - */ - if (path->len && - path->len <= namelen && - !memcmp(name, path->buf, path->len) && - (!name[path->len] || name[path->len] == '/')) - return check->exclude; + /* Read from the parent directories and push them down. */ + current = stk ? stk->baselen : -1; + while (current < baselen) { + struct exclude_stack *stk = xcalloc(1, sizeof(*stk)); + const char *cp; - strbuf_setlen(path, 0); - for (i = 0; name[i]; i++) { - int ch = name[i]; + if (current < 0) { + cp = base; + current = 0; + } + else { + cp = strchr(base + current + 1, '/'); + if (!cp) + die("oops in prep_exclude"); + cp++; + } + stk->prev = dir->exclude_stack; + stk->baselen = cp - base; + stk->exclude_ix = group->nr; + el = add_exclude_list(dir, EXC_DIRS, NULL); + memcpy(dir->basebuf + current, base + current, + stk->baselen - current); - if (ch == '/') { + /* Abort if the directory is excluded */ + if (stk->baselen) { int dt = DT_DIR; - exclude = last_exclude_matching(check->dir, - path->buf, &dt); - if (exclude) { - check->exclude = exclude; - return exclude; + dir->basebuf[stk->baselen - 1] = 0; + dir->exclude = last_exclude_matching_from_lists(dir, + dir->basebuf, stk->baselen - 1, + dir->basebuf + current, &dt); + dir->basebuf[stk->baselen - 1] = '/'; + if (dir->exclude) { + dir->basebuf[stk->baselen] = 0; + dir->exclude_stack = stk; + return; } } - strbuf_addch(path, ch); + + /* Try to read per-directory file unless path is too long */ + if (dir->exclude_per_dir && + stk->baselen + strlen(dir->exclude_per_dir) < PATH_MAX) { + strcpy(dir->basebuf + stk->baselen, + dir->exclude_per_dir); + /* + * 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->src = strdup(dir->basebuf); + add_excludes_from_file_to_list(dir->basebuf, + dir->basebuf, stk->baselen, el, 1); + } + dir->exclude_stack = stk; + current = stk->baselen; } + dir->basebuf[baselen] = '\0'; +} - /* An entry in the index; cannot be a directory with subentries */ - strbuf_setlen(path, 0); +/* + * Loads the exclude lists for the directory containing pathname, then + * scans all exclude lists to determine whether pathname is excluded. + * Returns the exclude_list element which matched, or NULL for + * undecided. + */ +struct exclude *last_exclude_matching(struct dir_struct *dir, + const char *pathname, + int *dtype_p) +{ + int pathlen = strlen(pathname); + const char *basename = strrchr(pathname, '/'); + basename = (basename) ? basename+1 : pathname; + + prep_exclude(dir, pathname, basename-pathname); - return last_exclude_matching(check->dir, name, dtype); + if (dir->exclude) + return dir->exclude; + + return last_exclude_matching_from_lists(dir, pathname, pathlen, + basename, dtype_p); } /* - * Is this name excluded? This is for a caller like show_files() that - * do not honor directory hierarchy and iterate through paths that are - * possibly in an ignored directory. + * Loads the exclude lists for the directory containing pathname, then + * scans all exclude lists to determine whether pathname is excluded. + * Returns 1 if true, otherwise 0. */ -int is_path_excluded(struct path_exclude_check *check, - const char *name, int namelen, int *dtype) +int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) { struct exclude *exclude = - last_exclude_matching_path(check, name, namelen, dtype); + last_exclude_matching(dir, pathname, dtype_p); if (exclude) return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return 0; @@@ -901,7 -941,8 +901,7 @@@ static struct dir_entry *dir_entry_new( static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) { - if (!(dir->flags & DIR_SHOW_IGNORED) && - cache_name_exists(pathname, len, ignore_case)) + if (cache_name_exists(pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); @@@ -1003,8 -1044,9 +1003,8 @@@ static enum exist_status directory_exis * traversal routine. * * Case 1: If we *already* have entries in the index under that - * directory name, we recurse into the directory to see all the files, - * unless the directory is excluded and we want to show ignored - * directories + * directory name, we always recurse into the directory to see + * all the files. * * Case 2: If we *already* have that directory name as a gitlink, * we always continue to see it as a gitlink, regardless of whether @@@ -1016,26 -1058,38 +1016,26 @@@ * * (a) if "show_other_directories" is true, we show it as * just a directory, unless "hide_empty_directories" is - * also true and the directory is empty, in which case - * we just ignore it entirely. - * if we are looking for ignored directories, look if it - * contains only ignored files to decide if it must be shown as - * ignored or not. + * also true, in which case we need to check if it contains any + * untracked and / or ignored files. * (b) if it looks like a git directory, and we don't have * 'no_gitlinks' set we treat it as a gitlink, and show it * as a directory. * (c) otherwise, we recurse into it. */ -enum directory_treatment { - show_directory, - ignore_directory, - recurse_into_directory -}; - -static enum directory_treatment treat_directory(struct dir_struct *dir, +static enum path_treatment treat_directory(struct dir_struct *dir, const char *dirname, int len, int exclude, const struct path_simplify *simplify) { /* The "len-1" is to strip the final '/' */ switch (directory_exists_in_index(dirname, len-1)) { case index_directory: - if ((dir->flags & DIR_SHOW_OTHER_DIRECTORIES) && exclude) - break; - - return recurse_into_directory; + return path_recurse; case index_gitdir: if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) - return ignore_directory; - return show_directory; + return path_none; + return path_untracked; case index_nonexistent: if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) @@@ -1043,17 -1097,72 +1043,17 @@@ if (!(dir->flags & DIR_NO_GITLINKS)) { unsigned char sha1[20]; if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0) - return show_directory; + return path_untracked; } - return recurse_into_directory; + return path_recurse; } /* This is the "show_other_directories" case */ - /* - * We are looking for ignored files and our directory is not ignored, - * check if it contains only ignored files - */ - if ((dir->flags & DIR_SHOW_IGNORED) && !exclude) { - int ignored; - dir->flags &= ~DIR_SHOW_IGNORED; - dir->flags |= DIR_HIDE_EMPTY_DIRECTORIES; - ignored = read_directory_recursive(dir, dirname, len, 1, simplify); - dir->flags &= ~DIR_HIDE_EMPTY_DIRECTORIES; - dir->flags |= DIR_SHOW_IGNORED; - - return ignored ? ignore_directory : show_directory; - } - if (!(dir->flags & DIR_SHOW_IGNORED) && - !(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) - return show_directory; - if (!read_directory_recursive(dir, dirname, len, 1, simplify)) - return ignore_directory; - return show_directory; -} - -/* - * Decide what to do when we find a file while traversing the - * filesystem. Mostly two cases: - * - * 1. We are looking for ignored files - * (a) File is ignored, include it - * (b) File is in ignored path, include it - * (c) File is not ignored, exclude it - * - * 2. Other scenarios, include the file if not excluded - * - * Return 1 for exclude, 0 for include. - */ -static int treat_file(struct dir_struct *dir, struct strbuf *path, int exclude, int *dtype) -{ - struct path_exclude_check check; - int exclude_file = 0; - - if (exclude) - exclude_file = !(dir->flags & DIR_SHOW_IGNORED); - else if (dir->flags & DIR_SHOW_IGNORED) { - /* Always exclude indexed files */ - struct cache_entry *ce = index_name_exists(&the_index, - path->buf, path->len, ignore_case); - - if (ce) - return 1; - - path_exclude_check_init(&check, dir); - - if (!is_path_excluded(&check, path->buf, path->len, dtype)) - exclude_file = 1; - - path_exclude_check_clear(&check); - } + if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) + return exclude ? path_excluded : path_untracked; - return exclude_file; + return read_directory_recursive(dir, dirname, len, 1, simplify); } /* @@@ -1168,40 -1277,57 +1168,40 @@@ static int get_dtype(struct dirent *de return dtype; } -enum path_treatment { - path_ignored, - path_handled, - path_recurse -}; - static enum path_treatment treat_one_path(struct dir_struct *dir, struct strbuf *path, const struct path_simplify *simplify, int dtype, struct dirent *de) { - int exclude = is_excluded(dir, path->buf, &dtype); - if (exclude && (dir->flags & DIR_COLLECT_IGNORED) - && exclude_matches_pathspec(path->buf, path->len, simplify)) - dir_add_ignored(dir, path->buf, path->len); + int exclude; + if (dtype == DT_UNKNOWN) + dtype = get_dtype(de, path->buf, path->len); + + /* Always exclude indexed files */ + if (dtype != DT_DIR && + cache_name_exists(path->buf, path->len, ignore_case)) + return path_none; + + exclude = is_excluded(dir, path->buf, &dtype); /* * Excluded? If we don't explicitly want to show * ignored files, ignore it */ - if (exclude && !(dir->flags & DIR_SHOW_IGNORED)) - return path_ignored; - - if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, path->buf, path->len); + if (exclude && !(dir->flags & (DIR_SHOW_IGNORED|DIR_SHOW_IGNORED_TOO))) + return path_excluded; switch (dtype) { default: - return path_ignored; + return path_none; case DT_DIR: strbuf_addch(path, '/'); - - switch (treat_directory(dir, path->buf, path->len, exclude, simplify)) { - case show_directory: - break; - case recurse_into_directory: - return path_recurse; - case ignore_directory: - return path_ignored; - } - break; + return treat_directory(dir, path->buf, path->len, exclude, + simplify); case DT_REG: case DT_LNK: - switch (treat_file(dir, path, exclude, &dtype)) { - case 1: - return path_ignored; - default: - break; - } + return exclude ? path_excluded : path_untracked; } - return path_handled; } static enum path_treatment treat_path(struct dir_struct *dir, @@@ -1213,11 -1339,11 +1213,11 @@@ int dtype; if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git")) - return path_ignored; + return path_none; strbuf_setlen(path, baselen); strbuf_addstr(path, de->d_name); if (simplify_away(path->buf, path->len, simplify)) - return path_ignored; + return path_none; dtype = DTYPE(de); return treat_one_path(dir, path, simplify, dtype, de); @@@ -1231,16 -1357,14 +1231,16 @@@ * * Also, we ignore the name ".git" (even if it is not a directory). * That likely will not change. + * + * Returns the most significant path_treatment value encountered in the scan. */ -static int read_directory_recursive(struct dir_struct *dir, +static enum path_treatment read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify) { DIR *fdir; - int contents = 0; + enum path_treatment state, subdir_state, dir_state = path_none; struct dirent *de; struct strbuf path = STRBUF_INIT; @@@ -1251,53 -1375,27 +1251,53 @@@ goto out; while ((de = readdir(fdir)) != NULL) { - switch (treat_path(dir, de, &path, baselen, simplify)) { - case path_recurse: - contents += read_directory_recursive(dir, path.buf, - path.len, 0, - simplify); - continue; - case path_ignored: + /* check how the file or directory should be treated */ + state = treat_path(dir, de, &path, baselen, simplify); + if (state > dir_state) + dir_state = state; + + /* recurse into subdir if instructed by treat_path */ + if (state == path_recurse) { + subdir_state = read_directory_recursive(dir, path.buf, + path.len, check_only, simplify); + if (subdir_state > dir_state) + dir_state = subdir_state; + } + + if (check_only) { + /* abort early if maximum state has been reached */ + if (dir_state == path_untracked) + break; + /* skip the dir_add_* part */ continue; - case path_handled: - break; } - contents++; - if (check_only) + + /* add the path to the appropriate result list */ + switch (state) { + case path_excluded: + if (dir->flags & DIR_SHOW_IGNORED) + dir_add_name(dir, path.buf, path.len); + else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || + ((dir->flags & DIR_COLLECT_IGNORED) && + exclude_matches_pathspec(path.buf, path.len, + simplify))) + dir_add_ignored(dir, path.buf, path.len); break; - dir_add_name(dir, path.buf, path.len); + + case path_untracked: + if (!(dir->flags & DIR_SHOW_IGNORED)) + dir_add_name(dir, path.buf, path.len); + break; + + default: + break; + } } closedir(fdir); out: strbuf_release(&path); - return contents; + return dir_state; } static int cmp_name(const void *p1, const void *p2) @@@ -1346,14 -1444,12 +1346,14 @@@ static int treat_leading_path(struct di struct strbuf sb = STRBUF_INIT; int baselen, rc = 0; const char *cp; + int old_flags = dir->flags; while (len && path[len - 1] == '/') len--; if (!len) return 1; baselen = 0; + dir->flags &= ~DIR_SHOW_OTHER_DIRECTORIES; while (1) { cp = path + baselen + !!baselen; cp = memchr(cp, '/', path + len - cp); @@@ -1368,7 -1464,7 +1368,7 @@@ if (simplify_away(sb.buf, sb.len, simplify)) break; if (treat_one_path(dir, &sb, simplify, - DT_DIR, NULL) == path_ignored) + DT_DIR, NULL) == path_none) break; /* do not recurse into it */ if (len <= baselen) { rc = 1; @@@ -1376,7 -1472,6 +1376,7 @@@ } } strbuf_release(&sb); + dir->flags = old_flags; return rc; } @@@ -1542,9 -1637,9 +1542,9 @@@ void setup_standard_excludes(struct dir home_config_paths(NULL, &xdg_path, "ignore"); excludes_file = xdg_path; } - if (!access_or_warn(path, R_OK)) + if (!access_or_warn(path, R_OK, 0)) add_excludes_from_file(dir, path); - if (excludes_file && !access_or_warn(excludes_file, R_OK)) + if (excludes_file && !access_or_warn(excludes_file, R_OK, 0)) add_excludes_from_file(dir, excludes_file); } @@@ -1552,7 -1647,7 +1552,7 @@@ int remove_path(const char *name { char *slash; - if (unlink(name) && errno != ENOENT) + if (unlink(name) && errno != ENOENT && errno != ENOTDIR) return -1; slash = strrchr(name, '/'); diff --combined git-compat-util.h index e955bb5e8b,fc1ef23519..c1f8a477fb --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -86,9 -86,6 +86,9 @@@ #define _SGI_SOURCE 1 #ifdef WIN32 /* Both MinGW and MSVC */ +# if defined (_MSC_VER) +# define _WIN32_WINNT 0x0502 +# endif #define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */ #include #include @@@ -163,7 -160,6 +163,7 @@@ typedef long intptr_t; typedef unsigned long uintptr_t; #endif +int get_st_mode_bits(const char *path, int *mode); #if defined(__CYGWIN__) #undef _XOPEN_SOURCE #include @@@ -331,7 -327,6 +331,7 @@@ extern void warning(const char *err, .. extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params)); extern void set_error_routine(void (*routine)(const char *err, va_list params)); +extern void set_die_is_recursing_routine(int (*routine)(void)); extern int prefixcmp(const char *str, const char *prefix); extern int suffixcmp(const char *str, const char *suffix); @@@ -692,8 -687,9 +692,9 @@@ int remove_or_warn(unsigned int mode, c * Call access(2), but warn for any error except "missing file" * (ENOENT or ENOTDIR). */ - int access_or_warn(const char *path, int mode); - int access_or_die(const char *path, int mode); + #define ACCESS_EACCES_OK (1U << 0) + int access_or_warn(const char *path, int mode, unsigned flag); + int access_or_die(const char *path, int mode, unsigned flag); /* Warn on an inaccessible file that ought to be accessible */ void warn_on_inaccessible(const char *path);