зеркало из https://github.com/microsoft/git.git
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:
Коммит
cf44c1e0a2
41
dir.c
41
dir.c
|
@ -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
2
dir.h
|
@ -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 *);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче