Merge branch 'ar/clean-rmdir-empty'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Apr 2011 18:36:41 +0000 (11:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Apr 2011 18:36:41 +0000 (11:36 -0700)
* ar/clean-rmdir-empty:
clean: unreadable directory may still be rmdir-able if it is empty

1  2 
dir.c
t/t7300-clean.sh
diff --combined dir.c
index 325fb56ad395c9b47dcbdea47b2833a4287198bc,aa1a7181f69c2b8e45df2cc0b95686cb0c3a73fe..532bcb65b523223b66efd3f4e458f62fcbe0d6a5
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -87,21 -87,6 +87,21 @@@ int fill_directory(struct dir_struct *d
        return len;
  }
  
 +int within_depth(const char *name, int namelen,
 +                      int depth, int max_depth)
 +{
 +      const char *cp = name, *cpe = name + namelen;
 +
 +      while (cp < cpe) {
 +              if (*cp++ != '/')
 +                      continue;
 +              depth++;
 +              if (depth > max_depth)
 +                      return 0;
 +      }
 +      return 1;
 +}
 +
  /*
   * Does 'match' match the given name?
   * A match is found if
@@@ -199,95 -184,6 +199,95 @@@ int match_pathspec(const char **pathspe
        return retval;
  }
  
 +/*
 + * Does 'match' match the given name?
 + * A match is found if
 + *
 + * (1) the 'match' string is leading directory of 'name', or
 + * (2) the 'match' string is a wildcard and matches 'name', or
 + * (3) the 'match' string is exactly the same as 'name'.
 + *
 + * and the return value tells which case it was.
 + *
 + * It returns 0 when there is no match.
 + */
 +static int match_pathspec_item(const struct pathspec_item *item, int prefix,
 +                             const char *name, int namelen)
 +{
 +      /* name/namelen has prefix cut off by caller */
 +      const char *match = item->match + prefix;
 +      int matchlen = item->len - prefix;
 +
 +      /* If the match was just the prefix, we matched */
 +      if (!*match)
 +              return MATCHED_RECURSIVELY;
 +
 +      if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
 +              if (matchlen == namelen)
 +                      return MATCHED_EXACTLY;
 +
 +              if (match[matchlen-1] == '/' || name[matchlen] == '/')
 +                      return MATCHED_RECURSIVELY;
 +      }
 +
 +      if (item->has_wildcard && !fnmatch(match, name, 0))
 +              return MATCHED_FNMATCH;
 +
 +      return 0;
 +}
 +
 +/*
 + * Given a name and a list of pathspecs, see if the name matches
 + * any of the pathspecs.  The caller is also interested in seeing
 + * all pathspec matches some names it calls this function with
 + * (otherwise the user could have mistyped the unmatched pathspec),
 + * and a mark is left in seen[] array for pathspec element that
 + * actually matched anything.
 + */
 +int match_pathspec_depth(const struct pathspec *ps,
 +                       const char *name, int namelen,
 +                       int prefix, char *seen)
 +{
 +      int i, retval = 0;
 +
 +      if (!ps->nr) {
 +              if (!ps->recursive || ps->max_depth == -1)
 +                      return MATCHED_RECURSIVELY;
 +
 +              if (within_depth(name, namelen, 0, ps->max_depth))
 +                      return MATCHED_EXACTLY;
 +              else
 +                      return 0;
 +      }
 +
 +      name += prefix;
 +      namelen -= prefix;
 +
 +      for (i = ps->nr - 1; i >= 0; i--) {
 +              int how;
 +              if (seen && seen[i] == MATCHED_EXACTLY)
 +                      continue;
 +              how = match_pathspec_item(ps->items+i, prefix, name, namelen);
 +              if (ps->recursive && ps->max_depth != -1 &&
 +                  how && how != MATCHED_FNMATCH) {
 +                      int len = ps->items[i].len;
 +                      if (name[len] == '/')
 +                              len++;
 +                      if (within_depth(name+len, namelen-len, 0, ps->max_depth))
 +                              how = MATCHED_EXACTLY;
 +                      else
 +                              how = 0;
 +              }
 +              if (how) {
 +                      if (retval < how)
 +                              retval = how;
 +                      if (seen && seen[i] < how)
 +                              seen[i] = how;
 +              }
 +      }
 +      return retval;
 +}
 +
  static int no_wildcard(const char *string)
  {
        return string[strcspn(string, "*?[{\\")] == '\0';
@@@ -1128,7 -1024,7 +1128,7 @@@ char *get_relative_cwd(char *buffer, in
                die_errno("can't find the current directory");
  
        if (!is_absolute_path(dir))
 -              dir = make_absolute_path(dir);
 +              dir = real_path(dir);
  
        while (*dir && *dir == *cwd) {
                dir++;
@@@ -1192,7 -1088,7 +1192,7 @@@ int remove_dir_recursively(struct strbu
  
        dir = opendir(path->buf);
        if (!dir)
-               return -1;
+               return rmdir(path->buf);
        if (path->buf[original_len - 1] != '/')
                strbuf_addch(path, '/');
  
@@@ -1255,50 -1151,3 +1255,50 @@@ int remove_path(const char *name
        return 0;
  }
  
 +static int pathspec_item_cmp(const void *a_, const void *b_)
 +{
 +      struct pathspec_item *a, *b;
 +
 +      a = (struct pathspec_item *)a_;
 +      b = (struct pathspec_item *)b_;
 +      return strcmp(a->match, b->match);
 +}
 +
 +int init_pathspec(struct pathspec *pathspec, const char **paths)
 +{
 +      const char **p = paths;
 +      int i;
 +
 +      memset(pathspec, 0, sizeof(*pathspec));
 +      if (!p)
 +              return 0;
 +      while (*p)
 +              p++;
 +      pathspec->raw = paths;
 +      pathspec->nr = p - paths;
 +      if (!pathspec->nr)
 +              return 0;
 +
 +      pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
 +      for (i = 0; i < pathspec->nr; i++) {
 +              struct pathspec_item *item = pathspec->items+i;
 +              const char *path = paths[i];
 +
 +              item->match = path;
 +              item->len = strlen(path);
 +              item->has_wildcard = !no_wildcard(path);
 +              if (item->has_wildcard)
 +                      pathspec->has_wildcard = 1;
 +      }
 +
 +      qsort(pathspec->items, pathspec->nr,
 +            sizeof(struct pathspec_item), pathspec_item_cmp);
 +
 +      return 0;
 +}
 +
 +void free_pathspec(struct pathspec *pathspec)
 +{
 +      free(pathspec->items);
 +      pathspec->items = NULL;
 +}
diff --combined t/t7300-clean.sh
index 7e1be444027621e1ea94b0b9ed0e129fce922b02,9b12681f971f39fe30fc113fecf37bd219d9b65f..800b5368a5248835bb9817c0e0c8409131306b3c
@@@ -110,7 -110,7 +110,7 @@@ test_expect_success 'git clean with pre
  
  '
  
 -test_expect_success 'git clean with relative prefix' '
 +test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
  
        mkdir -p build docs &&
        touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
        }
  '
  
 -test_expect_success 'git clean with absolute path' '
 +test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
  
        mkdir -p build docs &&
        touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@@ -377,7 -377,7 +377,7 @@@ test_expect_success 'clean.requireForc
  
  '
  
 -test_expect_success 'core.excludesfile' '
 +test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '
  
        echo excludes >excludes &&
        echo included >included &&
@@@ -453,4 -453,11 +453,11 @@@ test_expect_success 'git clean -e' 
        )
  '
  
+ test_expect_success SANITY 'git clean -d with an unreadable empty directory' '
+       mkdir foo &&
+       chmod a= foo &&
+       git clean -dfx foo &&
+       ! test -d foo
+ '
  test_done