From: Junio C Hamano Date: Wed, 11 Jul 2012 19:44:34 +0000 (-0700) Subject: Merge branch 'jc/ls-files-i-dir' into maint X-Git-Tag: v1.7.11.2~12 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/cd733f4f713d46bc175fd57fc9a168ea4f846148?ds=inline;hp=-c Merge branch 'jc/ls-files-i-dir' into maint "git ls-files --exclude=t -i" did not consider anything under t/ as excluded, as it did not pay attention to exclusion of leading paths while walking the index. Other two users of excluded() are also updated. * jc/ls-files-i-dir: dir.c: make excluded() file scope static unpack-trees.c: use path_excluded() in check_ok_to_remove() builtin/add.c: use path_excluded() path_excluded(): update API to less cache-entry centric ls-files -i: micro-optimize path_excluded() ls-files -i: pay attention to exclusion of leading paths --- cd733f4f713d46bc175fd57fc9a168ea4f846148 diff --combined builtin/add.c index b79336d712,e5b40d9873..87446cf92a --- a/builtin/add.c +++ b/builtin/add.c @@@ -13,7 -13,6 +13,7 @@@ #include "diff.h" #include "diffcore.h" #include "revision.h" +#include "bulk-checkin.h" static const char * const builtin_add_usage[] = { "git add [options] [--] ...", @@@ -280,7 -279,6 +280,7 @@@ static int edit_patch(int argc, const c argc = setup_revisions(argc, argv, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_PATCH; + DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES); out = open(file, O_CREAT | O_WRONLY, 0644); if (out < 0) die (_("Could not open '%s' for writing."), file); @@@ -443,6 -441,9 +443,9 @@@ int cmd_add(int argc, const char **argv if (pathspec) { int i; + struct path_exclude_check check; + + path_exclude_check_init(&check, &dir); if (!seen) seen = find_used_pathspec(pathspec); for (i = 0; pathspec[i]; i++) { @@@ -450,7 -451,7 +453,7 @@@ && !file_exists(pathspec[i])) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (excluded(&dir, pathspec[i], &dtype)) + if (path_excluded(&check, pathspec[i], -1, &dtype)) dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); } else die(_("pathspec '%s' did not match any files"), @@@ -458,17 -459,14 +461,18 @@@ } } free(seen); + path_exclude_check_clear(&check); } + plug_bulk_checkin(); + exit_status |= add_files_to_cache(prefix, pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); + unplug_bulk_checkin(); + finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || diff --combined dir.c index ed1510fbc8,79e43377ad..2c02b312b7 --- a/dir.c +++ b/dir.c @@@ -74,6 -74,7 +74,6 @@@ char *common_prefix(const char **pathsp int fill_directory(struct dir_struct *dir, const char **pathspec) { - const char *path; size_t len; /* @@@ -81,9 -82,15 +81,9 @@@ * use that to optimize the directory walk */ len = common_prefix_len(pathspec); - path = ""; - - if (len) - path = xmemdupz(*pathspec, len); /* Read the directory and prune it */ - read_directory(dir, path, len, pathspec); - if (*path) - free((char *)path); + read_directory(dir, pathspec ? *pathspec : "", len, pathspec); return len; } @@@ -553,7 -560,7 +553,7 @@@ int excluded_from_list(const char *path return -1; /* undecided */ } - int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) + static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) { int pathlen = strlen(pathname); int st; @@@ -573,6 -580,64 +573,64 @@@ return 0; } + void path_exclude_check_init(struct path_exclude_check *check, + struct dir_struct *dir) + { + check->dir = dir; + strbuf_init(&check->path, 256); + } + + void path_exclude_check_clear(struct path_exclude_check *check) + { + strbuf_release(&check->path); + } + + /* + * 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. + * + * 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. + */ + int path_excluded(struct path_exclude_check *check, + const char *name, int namelen, int *dtype) + { + int i; + struct strbuf *path = &check->path; + + /* + * we allow the caller to pass namelen as an optimization; it + * must match the length of the name, as we eventually call + * excluded() on the whole name string. + */ + if (namelen < 0) + namelen = strlen(name); + + if (path->len && + path->len <= namelen && + !memcmp(name, path->buf, path->len) && + (!name[path->len] || name[path->len] == '/')) + return 1; + + strbuf_setlen(path, 0); + for (i = 0; name[i]; i++) { + int ch = name[i]; + + if (ch == '/') { + int dt = DT_DIR; + if (excluded(check->dir, path->buf, &dt)) + return 1; + } + strbuf_addch(path, ch); + } + + /* An entry in the index; cannot be a directory with subentries */ + strbuf_setlen(path, 0); + + return excluded(check->dir, name, dtype); + } + static struct dir_entry *dir_entry_new(const char *pathname, int len) { struct dir_entry *ent; @@@ -866,14 -931,14 +924,14 @@@ enum path_treatment }; static enum path_treatment treat_one_path(struct dir_struct *dir, - char *path, int *len, + struct strbuf *path, const struct path_simplify *simplify, int dtype, struct dirent *de) { - int exclude = excluded(dir, path, &dtype); + int exclude = excluded(dir, path->buf, &dtype); if (exclude && (dir->flags & DIR_COLLECT_IGNORED) - && exclude_matches_pathspec(path, *len, simplify)) - dir_add_ignored(dir, path, *len); + && exclude_matches_pathspec(path->buf, path->len, simplify)) + dir_add_ignored(dir, path->buf, path->len); /* * Excluded? If we don't explicitly want to show @@@ -883,7 -948,7 +941,7 @@@ return path_ignored; if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, path, *len); + dtype = get_dtype(de, path->buf, path->len); /* * Do we want to see just the ignored files? @@@ -900,8 -965,9 +958,8 @@@ default: return path_ignored; case DT_DIR: - memcpy(path + *len, "/", 2); - (*len)++; - switch (treat_directory(dir, path, *len, simplify)) { + strbuf_addch(path, '/'); + switch (treat_directory(dir, path->buf, path->len, simplify)) { case show_directory: if (exclude != !!(dir->flags & DIR_SHOW_IGNORED)) @@@ -922,21 -988,26 +980,21 @@@ static enum path_treatment treat_path(struct dir_struct *dir, struct dirent *de, - char *path, int path_max, + struct strbuf *path, int baselen, - const struct path_simplify *simplify, - int *len) + const struct path_simplify *simplify) { int dtype; if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git")) return path_ignored; - *len = strlen(de->d_name); - /* Ignore overly long pathnames! */ - if (*len + baselen + 8 > path_max) - return path_ignored; - memcpy(path + baselen, de->d_name, *len + 1); - *len += baselen; - if (simplify_away(path, *len, simplify)) + strbuf_setlen(path, baselen); + strbuf_addstr(path, de->d_name); + if (simplify_away(path->buf, path->len, simplify)) return path_ignored; dtype = DTYPE(de); - return treat_one_path(dir, path, len, simplify, dtype, de); + return treat_one_path(dir, path, simplify, dtype, de); } /* @@@ -953,23 -1024,22 +1011,23 @@@ static int read_directory_recursive(str int check_only, const struct path_simplify *simplify) { - DIR *fdir = opendir(*base ? base : "."); + DIR *fdir; int contents = 0; struct dirent *de; - char path[PATH_MAX + 1]; + struct strbuf path = STRBUF_INIT; - if (!fdir) - return 0; + strbuf_add(&path, base, baselen); - memcpy(path, base, baselen); + fdir = opendir(path.len ? path.buf : "."); + if (!fdir) + goto out; while ((de = readdir(fdir)) != NULL) { - int len; - switch (treat_path(dir, de, path, sizeof(path), - baselen, simplify, &len)) { + switch (treat_path(dir, de, &path, baselen, simplify)) { case path_recurse: - contents += read_directory_recursive(dir, path, len, 0, simplify); + contents += read_directory_recursive(dir, path.buf, + path.len, 0, + simplify); continue; case path_ignored: continue; @@@ -978,12 -1048,12 +1036,12 @@@ } contents++; if (check_only) - goto exit_early; - else - dir_add_name(dir, path, len); + break; + dir_add_name(dir, path.buf, path.len); } -exit_early: closedir(fdir); + out: + strbuf_release(&path); return contents; } @@@ -1046,8 -1116,8 +1104,8 @@@ static int treat_leading_path(struct di const char *path, int len, const struct path_simplify *simplify) { - char pathbuf[PATH_MAX]; - int baselen, blen; + struct strbuf sb = STRBUF_INIT; + int baselen, rc = 0; const char *cp; while (len && path[len - 1] == '/') @@@ -1062,22 -1132,19 +1120,22 @@@ baselen = len; else baselen = cp - path; - memcpy(pathbuf, path, baselen); - pathbuf[baselen] = '\0'; - if (!is_directory(pathbuf)) - return 0; - if (simplify_away(pathbuf, baselen, simplify)) - return 0; - blen = baselen; - if (treat_one_path(dir, pathbuf, &blen, simplify, + strbuf_setlen(&sb, 0); + strbuf_add(&sb, path, baselen); + if (!is_directory(sb.buf)) + break; + if (simplify_away(sb.buf, sb.len, simplify)) + break; + if (treat_one_path(dir, &sb, simplify, DT_DIR, NULL) == path_ignored) - return 0; /* do not recurse into it */ - if (len <= baselen) - return 1; /* finished checking */ + break; /* do not recurse into it */ + if (len <= baselen) { + rc = 1; + break; /* finished checking */ + } } + strbuf_release(&sb); + return rc; } int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec) @@@ -1163,32 -1230,22 +1221,32 @@@ int is_empty_dir(const char *path return ret; } -int remove_dir_recursively(struct strbuf *path, int flag) +static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up) { DIR *dir; struct dirent *e; - int ret = 0, original_len = path->len, len; + int ret = 0, original_len = path->len, len, kept_down = 0; int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY); + int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL); unsigned char submodule_head[20]; if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) && - !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) + !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) { /* Do not descend and nuke a nested git work tree. */ + if (kept_up) + *kept_up = 1; return 0; + } + flag &= ~REMOVE_DIR_KEEP_TOPLEVEL; dir = opendir(path->buf); - if (!dir) - return rmdir(path->buf); + if (!dir) { + /* an empty dir could be removed even if it is unreadble */ + if (!keep_toplevel) + return rmdir(path->buf); + else + return -1; + } if (path->buf[original_len - 1] != '/') strbuf_addch(path, '/'); @@@ -1203,7 -1260,7 +1261,7 @@@ if (lstat(path->buf, &st)) ; /* fall thru */ else if (S_ISDIR(st.st_mode)) { - if (!remove_dir_recursively(path, only_empty)) + if (!remove_dir_recurse(path, flag, &kept_down)) continue; /* happy */ } else if (!only_empty && !unlink(path->buf)) continue; /* happy, too */ @@@ -1215,22 -1272,11 +1273,22 @@@ closedir(dir); strbuf_setlen(path, original_len); - if (!ret) + if (!ret && !keep_toplevel && !kept_down) ret = rmdir(path->buf); + else if (kept_up) + /* + * report the uplevel that it is not an error that we + * did not rmdir() our directory. + */ + *kept_up = !ret; return ret; } +int remove_dir_recursively(struct strbuf *path, int flag) +{ + return remove_dir_recurse(path, flag, NULL); +} + void setup_standard_excludes(struct dir_struct *dir) { const char *path; diff --combined dir.h index 58b6fc7c86,1a88a7564d..6c73e4151d --- a/dir.h +++ b/dir.h @@@ -1,6 -1,8 +1,8 @@@ #ifndef DIR_H #define DIR_H + #include "strbuf.h" + struct dir_entry { unsigned int len; char name[FLEX_ARRAY]; /* more */ @@@ -76,8 -78,22 +78,22 @@@ extern int read_directory(struct dir_st extern int excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, struct exclude_list *el); - extern int excluded(struct dir_struct *, const char *, int *); struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len); + + /* + * The excluded() API is meant for callers that check each level of leading + * directory hierarchies with excluded() to avoid recursing into excluded + * directories. Callers that do not do so should use this API instead. + */ + struct path_exclude_check { + struct dir_struct *dir; + struct strbuf path; + }; + extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *); + extern void path_exclude_check_clear(struct path_exclude_check *); + extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype); + + extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, char **buf_p, struct exclude_list *which, int check_index); extern void add_excludes_from_file(struct dir_struct *, const char *fname); @@@ -102,7 -118,6 +118,7 @@@ extern void setup_standard_excludes(str #define REMOVE_DIR_EMPTY_ONLY 01 #define REMOVE_DIR_KEEP_NESTED_GIT 02 +#define REMOVE_DIR_KEEP_TOPLEVEL 04 extern int remove_dir_recursively(struct strbuf *path, int flag); /* tries to remove the path with empty directories along it, ignores ENOENT */ diff --combined unpack-trees.c index ad40109432,133f2c9d2f..33a581924e --- a/unpack-trees.c +++ b/unpack-trees.c @@@ -102,28 -102,21 +102,28 @@@ void setup_unpack_trees_porcelain(struc opts->unpack_rejects[i].strdup_strings = 1; } -static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, - unsigned int set, unsigned int clear) +static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce, + unsigned int set, unsigned int clear) { - unsigned int size = ce_size(ce); - struct cache_entry *new = xmalloc(size); - clear |= CE_HASHED | CE_UNHASHED; if (set & CE_REMOVE) set |= CE_WT_REMOVE; + ce->next = NULL; + ce->ce_flags = (ce->ce_flags & ~clear) | set; + add_index_entry(&o->result, ce, + ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); +} + +static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, + unsigned int set, unsigned int clear) +{ + unsigned int size = ce_size(ce); + struct cache_entry *new = xmalloc(size); + memcpy(new, ce, size); - new->next = NULL; - new->ce_flags = (new->ce_flags & ~clear) | set; - add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); + do_add_entry(o, new, set, clear); } /* @@@ -594,7 -587,7 +594,7 @@@ static int unpack_nondirectories(int n for (i = 0; i < n; i++) if (src[i] && src[i] != o->df_conflict_entry) - add_entry(o, src[i], 0, 0); + do_add_entry(o, src[i], 0, 0); return 0; } @@@ -779,7 -772,7 +779,7 @@@ static int unpack_callback(int n, unsig if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0) return -1; - if (src[0]) { + if (o->merge && src[0]) { if (ce_stage(src[0])) mark_ce_used_same_name(src[0], o); else @@@ -1023,11 -1016,14 +1023,15 @@@ int unpack_trees(unsigned len, struct t o->el = ⪙ } + if (o->dir) { + o->path_exclude_check = xmalloc(sizeof(struct path_exclude_check)); + path_exclude_check_init(o->path_exclude_check, o->dir); + } memset(&o->result, 0, sizeof(o->result)); o->result.initialized = 1; o->result.timestamp.sec = o->src_index->timestamp.sec; o->result.timestamp.nsec = o->src_index->timestamp.nsec; + o->result.version = o->src_index->version; o->merge_size = len; mark_all_ce_unused(o->src_index); @@@ -1148,6 -1144,10 +1152,10 @@@ done: free_excludes(&el); + if (o->path_exclude_check) { + path_exclude_check_clear(o->path_exclude_check); + free(o->path_exclude_check); + } return ret; return_failed: @@@ -1210,7 -1210,7 +1218,7 @@@ static int verify_uptodate_1(struct cac return 0; /* * NEEDSWORK: the current default policy is to allow - * submodule to be out of sync wrt the supermodule + * submodule to be out of sync wrt the superproject * index. This needs to be tightened later for * submodules that are marked to be automatically * checked out. @@@ -1363,7 -1363,8 +1371,8 @@@ static int check_ok_to_remove(const cha if (ignore_case && icase_exists(o, name, len, st)) return 0; - if (o->dir && excluded(o->dir, name, &dtype)) + if (o->dir && + path_excluded(o->path_exclude_check, name, -1, &dtype)) /* * ce->name is explicitly excluded, so it is Ok to * overwrite it. @@@ -1793,7 -1794,7 +1802,7 @@@ int bind_merge(struct cache_entry **src struct cache_entry *a = src[1]; if (o->merge_size != 1) - return error("Cannot do a bind merge of %d trees\n", + return error("Cannot do a bind merge of %d trees", o->merge_size); if (a && old) return o->gently ? -1 :