mirror of
https://github.com/git/git
synced 2024-08-25 02:35:46 +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:
commit
cf44c1e0a2
41
dir.c
41
dir.c
|
@ -771,7 +771,16 @@ static void invalidate_directory(struct untracked_cache *uc,
|
||||||
struct untracked_cache_dir *dir)
|
struct untracked_cache_dir *dir)
|
||||||
{
|
{
|
||||||
int i;
|
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->valid = 0;
|
||||||
dir->untracked_nr = 0;
|
dir->untracked_nr = 0;
|
||||||
for (i = 0; i < dir->dirs_nr; i++)
|
for (i = 0; i < dir->dirs_nr; i++)
|
||||||
|
@ -1770,7 +1779,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
|
||||||
if (!de)
|
if (!de)
|
||||||
return treat_path_fast(dir, untracked, cdir, istate, path,
|
return treat_path_fast(dir, untracked, cdir, istate, path,
|
||||||
baselen, pathspec);
|
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;
|
return path_none;
|
||||||
strbuf_setlen(path, baselen);
|
strbuf_setlen(path, baselen);
|
||||||
strbuf_addstr(path, de->d_name);
|
strbuf_addstr(path, de->d_name);
|
||||||
|
@ -1806,24 +1815,19 @@ static int valid_cached_dir(struct dir_struct *dir,
|
||||||
*/
|
*/
|
||||||
refresh_fsmonitor(istate);
|
refresh_fsmonitor(istate);
|
||||||
if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
|
if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
|
||||||
if (stat(path->len ? path->buf : ".", &st)) {
|
if (lstat(path->len ? path->buf : ".", &st)) {
|
||||||
invalidate_directory(dir->untracked, untracked);
|
|
||||||
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
|
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!untracked->valid ||
|
if (!untracked->valid ||
|
||||||
match_stat_data_racy(istate, &untracked->stat_data, &st)) {
|
match_stat_data_racy(istate, &untracked->stat_data, &st)) {
|
||||||
if (untracked->valid)
|
|
||||||
invalidate_directory(dir->untracked, untracked);
|
|
||||||
fill_stat_data(&untracked->stat_data, &st);
|
fill_stat_data(&untracked->stat_data, &st);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (untracked->check_only != !!check_only) {
|
if (untracked->check_only != !!check_only)
|
||||||
invalidate_directory(dir->untracked, untracked);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prep_exclude will be called eventually on this directory,
|
* 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,
|
struct strbuf *path,
|
||||||
int check_only)
|
int check_only)
|
||||||
{
|
{
|
||||||
|
const char *c_path;
|
||||||
|
|
||||||
memset(cdir, 0, sizeof(*cdir));
|
memset(cdir, 0, sizeof(*cdir));
|
||||||
cdir->untracked = untracked;
|
cdir->untracked = untracked;
|
||||||
if (valid_cached_dir(dir, untracked, istate, path, check_only))
|
if (valid_cached_dir(dir, untracked, istate, path, check_only))
|
||||||
return 0;
|
return 0;
|
||||||
cdir->fdir = opendir(path->len ? path->buf : ".");
|
c_path = path->len ? path->buf : ".";
|
||||||
if (dir->untracked)
|
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++;
|
dir->untracked->dir_opened++;
|
||||||
|
}
|
||||||
if (!cdir->fdir)
|
if (!cdir->fdir)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2966,10 +2977,12 @@ static int invalidate_one_component(struct untracked_cache *uc,
|
||||||
}
|
}
|
||||||
|
|
||||||
void untracked_cache_invalidate_path(struct index_state *istate,
|
void untracked_cache_invalidate_path(struct index_state *istate,
|
||||||
const char *path)
|
const char *path, int safe_path)
|
||||||
{
|
{
|
||||||
if (!istate->untracked || !istate->untracked->root)
|
if (!istate->untracked || !istate->untracked->root)
|
||||||
return;
|
return;
|
||||||
|
if (!safe_path && !verify_path(path))
|
||||||
|
return;
|
||||||
invalidate_one_component(istate->untracked, istate->untracked->root,
|
invalidate_one_component(istate->untracked, istate->untracked->root,
|
||||||
path, strlen(path));
|
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,
|
void untracked_cache_remove_from_index(struct index_state *istate,
|
||||||
const char *path)
|
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,
|
void untracked_cache_add_to_index(struct index_state *istate,
|
||||||
const char *path)
|
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 */
|
/* 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 cmp_dir_entry(const void *p1, const void *p2);
|
||||||
int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
|
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_remove_from_index(struct index_state *, const char *);
|
||||||
void untracked_cache_add_to_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.
|
* as it could be a new untracked file.
|
||||||
*/
|
*/
|
||||||
trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
|
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)
|
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) {
|
if (core_fsmonitor) {
|
||||||
ce->ce_flags &= ~CE_FSMONITOR_VALID;
|
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);
|
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,12 @@ avoid_racy() {
|
||||||
sleep 1
|
sleep 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status_is_clean() {
|
||||||
|
>../status.expect &&
|
||||||
|
git status --porcelain >../status.actual &&
|
||||||
|
test_cmp ../status.expect ../status.actual
|
||||||
|
}
|
||||||
|
|
||||||
test_lazy_prereq UNTRACKED_CACHE '
|
test_lazy_prereq UNTRACKED_CACHE '
|
||||||
{ git update-index --test-untracked-cache; ret=$?; } &&
|
{ git update-index --test-untracked-cache; ret=$?; } &&
|
||||||
test $ret -ne 1
|
test $ret -ne 1
|
||||||
|
@ -683,4 +689,85 @@ test_expect_success 'untracked cache survives a commit' '
|
||||||
test_cmp ../before ../after
|
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
|
test_done
|
||||||
|
|
|
@ -314,4 +314,43 @@ test_expect_success 'splitting the index results in the same state' '
|
||||||
test_cmp expect actual
|
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
|
test_done
|
||||||
|
|
|
@ -1528,7 +1528,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
|
||||||
if (!ce)
|
if (!ce)
|
||||||
return;
|
return;
|
||||||
cache_tree_invalidate_path(o->src_index, ce->name);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue