Merge branch 'jn/config-ignore-inaccessible'
authorJunio C Hamano <gitster@pobox.com>
Wed, 29 May 2013 21:30:10 +0000 (14:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 May 2013 21:30:10 +0000 (14:30 -0700)
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

1  2 
dir.c
git-compat-util.h
diff --combined dir.c
index a5926fbd1aeafd468860da7dbd3d8a5d5999a650,e2ea111680a259e51c953cc72f09074fbee11d70..ee4d04dc8b378dab223ea8a8589dca7a58eeea48
--- 1/dir.c
--- 2/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--) {
  }
  
  /*
 - * 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
   *
   *  (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)
                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,
        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);
   *
   * 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;
  
                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);
                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;
                }
        }
        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 e955bb5e8b3101cc8c753cf541beabf5cd037b39,fc1ef23519b8e89c870349f2343619cf8ee1458f..c1f8a477fb96730612e1ddd06294270a4651649f
@@@ -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 <winsock2.h>
  #include <windows.h>
  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 <grp.h>
@@@ -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);