From: Junio C Hamano Date: Thu, 17 Sep 2015 19:12:29 +0000 (-0700) Subject: Merge branch 'dt/untracked-subdir' into maint X-Git-Tag: v2.5.3~1 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/8833ccd7d020a6160240a4933be91370e6f59c48?ds=inline;hp=-c Merge branch 'dt/untracked-subdir' into maint The experimental untracked-cache feature were buggy when paths with a few levels of subdirectories are involved. * dt/untracked-subdir: untracked cache: fix entry invalidation untracked-cache: fix subdirectory handling t7063: use --force-untracked-cache to speed up a bit untracked-cache: support sparse checkout --- 8833ccd7d020a6160240a4933be91370e6f59c48 diff --combined dir.c index 1d42811cfb,c1edabf34d..a71331af18 --- a/dir.c +++ b/dir.c @@@ -1078,10 -1078,9 +1078,9 @@@ static void prep_exclude(struct dir_str (!untracked || !untracked->valid || /* * .. and .gitignore does not exist before - * (i.e. null exclude_sha1 and skip_worktree is - * not set). Then we can skip loading .gitignore, - * which would result in ENOENT anyway. - * skip_worktree is taken care in read_directory() + * (i.e. null exclude_sha1). Then we can skip + * loading .gitignore, which would result in + * ENOENT anyway. */ !is_null_sha1(untracked->exclude_sha1))) { /* @@@ -1298,7 -1297,7 +1297,7 @@@ static enum exist_status directory_exis */ static enum path_treatment treat_directory(struct dir_struct *dir, struct untracked_cache_dir *untracked, - const char *dirname, int len, int exclude, + const char *dirname, int len, int baselen, int exclude, const struct path_simplify *simplify) { /* The "len-1" is to strip the final '/' */ @@@ -1325,7 -1324,8 +1324,8 @@@ if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return exclude ? path_excluded : path_untracked; - untracked = lookup_untracked(dir->untracked, untracked, dirname, len); + untracked = lookup_untracked(dir->untracked, untracked, + dirname + baselen, len - baselen); return read_directory_recursive(dir, dirname, len, untracked, 1, simplify); } @@@ -1445,6 -1445,7 +1445,7 @@@ static int get_dtype(struct dirent *de static enum path_treatment treat_one_path(struct dir_struct *dir, struct untracked_cache_dir *untracked, struct strbuf *path, + int baselen, const struct path_simplify *simplify, int dtype, struct dirent *de) { @@@ -1496,8 -1497,8 +1497,8 @@@ return path_none; case DT_DIR: strbuf_addch(path, '/'); - return treat_directory(dir, untracked, path->buf, path->len, exclude, - simplify); + return treat_directory(dir, untracked, path->buf, path->len, + baselen, exclude, simplify); case DT_REG: case DT_LNK: return exclude ? path_excluded : path_untracked; @@@ -1558,7 -1559,7 +1559,7 @@@ static enum path_treatment treat_path(s return path_none; dtype = DTYPE(de); - return treat_one_path(dir, untracked, path, simplify, dtype, de); + return treat_one_path(dir, untracked, path, baselen, simplify, dtype, de); } static void add_untracked(struct untracked_cache_dir *dir, const char *name) @@@ -1828,7 -1829,7 +1829,7 @@@ static int treat_leading_path(struct di break; if (simplify_away(sb.buf, sb.len, simplify)) break; - if (treat_one_path(dir, NULL, &sb, simplify, + if (treat_one_path(dir, NULL, &sb, baselen, simplify, DT_DIR, NULL) == path_none) break; /* do not recurse into it */ if (len <= baselen) { @@@ -1848,7 -1849,7 +1849,7 @@@ static const char *get_ident_string(voi if (sb.len) return sb.buf; - if (uname(&uts)) + if (uname(&uts) < 0) die_errno(_("failed to get kernel name and information")); strbuf_addf(&sb, "Location %s, system %s %s %s", get_git_work_tree(), uts.sysname, uts.release, uts.version); @@@ -1880,7 -1881,6 +1881,6 @@@ static struct untracked_cache_dir *vali const struct pathspec *pathspec) { struct untracked_cache_dir *root; - int i; if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE")) return NULL; @@@ -1932,15 -1932,6 +1932,6 @@@ if (dir->exclude_list_group[EXC_CMDL].nr) return NULL; - /* - * An optimization in prep_exclude() does not play well with - * CE_SKIP_WORKTREE. It's a rare case anyway, if a single - * entry has that bit set, disable the whole untracked cache. - */ - for (i = 0; i < active_nr; i++) - if (ce_skip_worktree(active_cache[i])) - return NULL; - if (!ident_in_untracked(dir->untracked)) { warning(_("Untracked cache is disabled on this system.")); return NULL; @@@ -2625,23 -2616,67 +2616,67 @@@ done2 return uc; } + static void invalidate_one_directory(struct untracked_cache *uc, + struct untracked_cache_dir *ucd) + { + uc->dir_invalidated++; + ucd->valid = 0; + ucd->untracked_nr = 0; + } + + /* + * Normally when an entry is added or removed from a directory, + * invalidating that directory is enough. No need to touch its + * ancestors. When a directory is shown as "foo/bar/" in git-status + * however, deleting or adding an entry may have cascading effect. + * + * Say the "foo/bar/file" has become untracked, we need to tell the + * untracked_cache_dir of "foo" that "bar/" is not an untracked + * directory any more (because "bar" is managed by foo as an untracked + * "file"). + * + * Similarly, if "foo/bar/file" moves from untracked to tracked and it + * was the last untracked entry in the entire "foo", we should show + * "foo/" instead. Which means we have to invalidate past "bar" up to + * "foo". + * + * This function traverses all directories from root to leaf. If there + * is a chance of one of the above cases happening, we invalidate back + * to root. Otherwise we just invalidate the leaf. There may be a more + * sophisticated way than checking for SHOW_OTHER_DIRECTORIES to + * detect these cases and avoid unnecessary invalidation, for example, + * checking for the untracked entry named "bar/" in "foo", but for now + * stick to something safe and simple. + */ + static int invalidate_one_component(struct untracked_cache *uc, + struct untracked_cache_dir *dir, + const char *path, int len) + { + const char *rest = strchr(path, '/'); + + if (rest) { + int component_len = rest - path; + struct untracked_cache_dir *d = + lookup_untracked(uc, dir, path, component_len); + int ret = + invalidate_one_component(uc, d, rest + 1, + len - (component_len + 1)); + if (ret) + invalidate_one_directory(uc, dir); + return ret; + } + + invalidate_one_directory(uc, dir); + return uc->dir_flags & DIR_SHOW_OTHER_DIRECTORIES; + } + void untracked_cache_invalidate_path(struct index_state *istate, const char *path) { - const char *sep; - struct untracked_cache_dir *d; if (!istate->untracked || !istate->untracked->root) return; - sep = strrchr(path, '/'); - if (sep) - d = lookup_untracked(istate->untracked, - istate->untracked->root, - path, sep - path); - else - d = istate->untracked->root; - istate->untracked->dir_invalidated++; - d->valid = 0; - d->untracked_nr = 0; + invalidate_one_component(istate->untracked, istate->untracked->root, + path, strlen(path)); } void untracked_cache_remove_from_index(struct index_state *istate,