1
0
mirror of https://github.com/git/git synced 2024-07-07 19:39:27 +00:00

Merge branch 'nd/fix-untracked-cache-invalidation'

Some bugs around "untracked cache" feature have been fixed.

* nd/fix-untracked-cache-invalidation:
  dir.c: ignore paths containing .git when invalidating untracked cache
  dir.c: stop ignoring opendir() error in open_cached_dir()
  dir.c: fix missing dir invalidation in untracked code
  dir.c: avoid stat() in valid_cached_dir()
  status: add a failing test showing a core.untrackedCache bug
This commit is contained in:
Junio C Hamano 2018-02-27 10:33:49 -08:00
commit cf44c1e0a2
7 changed files with 157 additions and 18 deletions

41
dir.c
View File

@ -771,7 +771,16 @@ static void invalidate_directory(struct untracked_cache *uc,
struct untracked_cache_dir *dir)
{
int i;
uc->dir_invalidated++;
/*
* Invalidation increment here is just roughly correct. If
* untracked_nr or any of dirs[].recurse is non-zero, we
* should increment dir_invalidated too. But that's more
* expensive to do.
*/
if (dir->valid)
uc->dir_invalidated++;
dir->valid = 0;
dir->untracked_nr = 0;
for (i = 0; i < dir->dirs_nr; i++)
@ -1770,7 +1779,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
if (!de)
return treat_path_fast(dir, untracked, cdir, istate, path,
baselen, pathspec);
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
return path_none;
strbuf_setlen(path, baselen);
strbuf_addstr(path, de->d_name);
@ -1806,24 +1815,19 @@ static int valid_cached_dir(struct dir_struct *dir,
*/
refresh_fsmonitor(istate);
if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
if (stat(path->len ? path->buf : ".", &st)) {
invalidate_directory(dir->untracked, untracked);
if (lstat(path->len ? path->buf : ".", &st)) {
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
return 0;
}
if (!untracked->valid ||
match_stat_data_racy(istate, &untracked->stat_data, &st)) {
if (untracked->valid)
invalidate_directory(dir->untracked, untracked);
fill_stat_data(&untracked->stat_data, &st);
return 0;
}
}
if (untracked->check_only != !!check_only) {
invalidate_directory(dir->untracked, untracked);
if (untracked->check_only != !!check_only)
return 0;
}
/*
* prep_exclude will be called eventually on this directory,
@ -1850,13 +1854,20 @@ static int open_cached_dir(struct cached_dir *cdir,
struct strbuf *path,
int check_only)
{
const char *c_path;
memset(cdir, 0, sizeof(*cdir));
cdir->untracked = untracked;
if (valid_cached_dir(dir, untracked, istate, path, check_only))
return 0;
cdir->fdir = opendir(path->len ? path->buf : ".");
if (dir->untracked)
c_path = path->len ? path->buf : ".";
cdir->fdir = opendir(c_path);
if (!cdir->fdir)
warning_errno(_("could not open directory '%s'"), c_path);
if (dir->untracked) {
invalidate_directory(dir->untracked, untracked);
dir->untracked->dir_opened++;
}
if (!cdir->fdir)
return -1;
return 0;
@ -2966,10 +2977,12 @@ static int invalidate_one_component(struct untracked_cache *uc,
}
void untracked_cache_invalidate_path(struct index_state *istate,
const char *path)
const char *path, int safe_path)
{
if (!istate->untracked || !istate->untracked->root)
return;
if (!safe_path && !verify_path(path))
return;
invalidate_one_component(istate->untracked, istate->untracked->root,
path, strlen(path));
}
@ -2977,13 +2990,13 @@ void untracked_cache_invalidate_path(struct index_state *istate,
void untracked_cache_remove_from_index(struct index_state *istate,
const char *path)
{
untracked_cache_invalidate_path(istate, path);
untracked_cache_invalidate_path(istate, path, 1);
}
void untracked_cache_add_to_index(struct index_state *istate,
const char *path)
{
untracked_cache_invalidate_path(istate, path);
untracked_cache_invalidate_path(istate, path, 1);
}
/* Update gitfile and core.worktree setting to connect work tree and git dir */

2
dir.h
View File

@ -350,7 +350,7 @@ static inline int dir_path_match(const struct dir_entry *ent,
int cmp_dir_entry(const void *p1, const void *p2);
int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
void untracked_cache_invalidate_path(struct index_state *, const char *);
void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
void untracked_cache_remove_from_index(struct index_state *, const char *);
void untracked_cache_add_to_index(struct index_state *, const char *);

View File

@ -130,7 +130,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, const char *n
* as it could be a new untracked file.
*/
trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
untracked_cache_invalidate_path(istate, name);
untracked_cache_invalidate_path(istate, name, 0);
}
void refresh_fsmonitor(struct index_state *istate)

View File

@ -65,7 +65,7 @@ static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cac
{
if (core_fsmonitor) {
ce->ce_flags &= ~CE_FSMONITOR_VALID;
untracked_cache_invalidate_path(istate, ce->name);
untracked_cache_invalidate_path(istate, ce->name, 1);
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
}
}

View File

@ -22,6 +22,12 @@ avoid_racy() {
sleep 1
}
status_is_clean() {
>../status.expect &&
git status --porcelain >../status.actual &&
test_cmp ../status.expect ../status.actual
}
test_lazy_prereq UNTRACKED_CACHE '
{ git update-index --test-untracked-cache; ret=$?; } &&
test $ret -ne 1
@ -683,4 +689,85 @@ test_expect_success 'untracked cache survives a commit' '
test_cmp ../before ../after
'
test_expect_success 'teardown worktree' '
cd ..
'
test_expect_success SYMLINKS 'setup worktree for symlink test' '
git init worktree-symlink &&
cd worktree-symlink &&
git config core.untrackedCache true &&
mkdir one two &&
touch one/file two/file &&
git add one/file two/file &&
git commit -m"first commit" &&
git rm -rf one &&
ln -s two one &&
git add one &&
git commit -m"second commit"
'
test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=true' '
git checkout HEAD~ &&
status_is_clean &&
status_is_clean &&
git checkout master &&
avoid_racy &&
status_is_clean &&
status_is_clean
'
test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=false' '
git config core.untrackedCache false &&
git checkout HEAD~ &&
status_is_clean &&
status_is_clean &&
git checkout master &&
avoid_racy &&
status_is_clean &&
status_is_clean
'
test_expect_success 'setup worktree for non-symlink test' '
git init worktree-non-symlink &&
cd worktree-non-symlink &&
git config core.untrackedCache true &&
mkdir one two &&
touch one/file two/file &&
git add one/file two/file &&
git commit -m"first commit" &&
git rm -rf one &&
cp two/file one &&
git add one &&
git commit -m"second commit"
'
test_expect_success '"status" after file replacement should be clean with UC=true' '
git checkout HEAD~ &&
status_is_clean &&
status_is_clean &&
git checkout master &&
avoid_racy &&
status_is_clean &&
test-dump-untracked-cache >../actual &&
grep -F "recurse valid" ../actual >../actual.grep &&
cat >../expect.grep <<EOF &&
/ 0000000000000000000000000000000000000000 recurse valid
/two/ 0000000000000000000000000000000000000000 recurse valid
EOF
status_is_clean &&
test_cmp ../expect.grep ../actual.grep
'
test_expect_success '"status" after file replacement should be clean with UC=false' '
git config core.untrackedCache false &&
git checkout HEAD~ &&
status_is_clean &&
status_is_clean &&
git checkout master &&
avoid_racy &&
status_is_clean &&
status_is_clean
'
test_done

View File

@ -314,4 +314,43 @@ test_expect_success 'splitting the index results in the same state' '
test_cmp expect actual
'
test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR' '
test_create_repo dot-git &&
(
cd dot-git &&
mkdir -p .git/hooks &&
: >tracked &&
: >modified &&
mkdir dir1 &&
: >dir1/tracked &&
: >dir1/modified &&
mkdir dir2 &&
: >dir2/tracked &&
: >dir2/modified &&
write_integration_script &&
git config core.fsmonitor .git/hooks/fsmonitor-test &&
git update-index --untracked-cache &&
git update-index --fsmonitor &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-before" \
git status &&
test-dump-untracked-cache >../before
) &&
cat >>dot-git/.git/hooks/fsmonitor-test <<-\EOF &&
printf ".git\0"
printf ".git/index\0"
printf "dir1/.git\0"
printf "dir1/.git/index\0"
EOF
(
cd dot-git &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-after" \
git status &&
test-dump-untracked-cache >../after
) &&
grep "directory invalidation" trace-before >>before &&
grep "directory invalidation" trace-after >>after &&
# UNTR extension unchanged, dir invalidation count unchanged
test_cmp before after
'
test_done

View File

@ -1528,7 +1528,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
if (!ce)
return;
cache_tree_invalidate_path(o->src_index, ce->name);
untracked_cache_invalidate_path(o->src_index, ce->name);
untracked_cache_invalidate_path(o->src_index, ce->name, 1);
}
/*