From: Junio C Hamano Date: Wed, 27 Apr 2011 18:36:41 +0000 (-0700) Subject: Merge branch 'ar/clean-rmdir-empty' X-Git-Tag: v1.7.6-rc0~156 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/1de0746d849c64e1f4d160f9915d87d39c0794ea?hp=-c Merge branch 'ar/clean-rmdir-empty' * ar/clean-rmdir-empty: clean: unreadable directory may still be rmdir-able if it is empty --- 1de0746d849c64e1f4d160f9915d87d39c0794ea diff --combined dir.c index 325fb56ad3,aa1a7181f6..532bcb65b5 --- a/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 7e1be44402,9b12681f97..800b5368a5 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@@ -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 && @@@ -125,7 -125,7 +125,7 @@@ } ' -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