git-commit-vandalism/t/t7063-status-untracked-cache.sh
Nguyễn Thái Ngọc Duy b640313110 dir.c: fix missing dir invalidation in untracked code
Let's start with how create a new directory cache after the last one
becomes invalid (e.g. because its dir mtime has changed...). In
open_cached_dir():

1. We start out with valid_cached_dir() returning false, which should
   call invalidate_directory() to put a directory state back to
   initial state, no untracked entries (untracked_nr zero), no sub
   directory traversal (dirs[].recurse zero).

2. Since the cache cannot be used, we go the slow path opendir() and
   go through items one by one via readdir(). All the directories on
   disk will be added back to the cache (if not already exist in
   dirs[]) and its flag "recurse" gets changed to one to note that
   it's part of the cached dir travesal next time.

3. By the time we reach close_cached_dir() we should have a good
   subdir list in dirs[]. Those with "recurse" flag set are the ones
   present in the on-disk directory. The directory is now marked
   "valid".

Next time read_directory() is called, since the directory is marked
valid, it will skip readdir(), go fast path and traverse through
dirs[] array instead.

Steps one and two need some tight cooperation. If a subdir is removed,
readdir() will not find it and of course we cannot examine/invalidate
it. To make sure removed directories on disk are gone from the cache,
step one must make sure recurse flag of all subdirs are zero.

But that's not true. If "valid" flag is already false, there is a
chance we go straight to the end of valid_cached_dir() without calling
invalidate_directory(). Or we fail to meet the "if (untracked-valid)"
condition and skip over the invalidate_directory().

After step 3, we mark the cache valid. Any stale subdir with incorrect
recurse flag becomes a real subdir next time we traverse the directory
using dirs[] array.

We could avoid this by making sure invalidate_directory() is always
called (therefore dirs[].recurse cleared) at the beginning of
open_cached_dir(). Which is what this patch does.

As to how we get into this situation, the key in the test is this
command

    git checkout master

where "one/file" is replaced with "one" in the index. This index
update triggers untracked_cache_invalidate_path(), which clears valid
flag of the root directory while keeping "recurse" flag on the subdir
"one" on. On the next git-status, we go through steps 1-3 above and
save an incorrect cache on disk. The second git-status blindly follows
the bad cache data and shows the problem.

This is arguably because of a bad design where "recurse" flag plays
double roles: whether a directory should be saved on disk, and whether
it is part of a directory traversal.

We need to keep recurse flag set at "checkout master" because of the
first role: we need to keep subdir caches (dir "two" for example has
not been touched at all, no reason to throw its cache away).

As long as we make sure to ignore/reset "recurse" flag at the
beginning of a directory traversal, we're good. But maybe eventually
we should separate these two roles.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-01-24 12:40:14 -08:00

774 lines
21 KiB
Bash
Executable File

#!/bin/sh
test_description='test untracked cache'
. ./test-lib.sh
# On some filesystems (e.g. FreeBSD's ext2 and ufs) directory mtime
# is updated lazily after contents in the directory changes, which
# forces the untracked cache code to take the slow path. A test
# that wants to make sure that the fast path works correctly should
# call this helper to make mtime of the containing directory in sync
# with the reality before checking the fast path behaviour.
#
# See <20160803174522.5571-1-pclouds@gmail.com> if you want to know
# more.
sync_mtime () {
find . -type d -ls >/dev/null
}
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
'
if ! test_have_prereq UNTRACKED_CACHE; then
skip_all='This system does not support untracked cache'
test_done
fi
test_expect_success 'core.untrackedCache is unset' '
test_must_fail git config --get core.untrackedCache
'
test_expect_success 'setup' '
git init worktree &&
cd worktree &&
mkdir done dtwo dthree &&
touch one two three done/one dtwo/two dthree/three &&
git add one two done/one &&
: >.git/info/exclude &&
git update-index --untracked-cache
'
test_expect_success 'untracked cache is empty' '
test-dump-untracked-cache >../actual &&
cat >../expect-empty <<EOF &&
info/exclude 0000000000000000000000000000000000000000
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
EOF
test_cmp ../expect-empty ../actual
'
cat >../status.expect <<EOF &&
A done/one
A one
A two
?? dthree/
?? dtwo/
?? three
EOF
cat >../dump.expect <<EOF &&
info/exclude $EMPTY_BLOB
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ 0000000000000000000000000000000000000000 recurse valid
dthree/
dtwo/
three
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
three
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_expect_success 'status first time (empty cache)' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 3
gitignore invalidation: 1
directory invalidation: 0
opendir: 4
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'untracked cache after first status' '
test-dump-untracked-cache >../actual &&
test_cmp ../dump.expect ../actual
'
test_expect_success 'status second time (fully populated cache)' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 0
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'untracked cache after second status' '
test-dump-untracked-cache >../actual &&
test_cmp ../dump.expect ../actual
'
test_expect_success 'modify in root directory, one dir invalidation' '
avoid_racy &&
: >four &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
cat >../status.expect <<EOF &&
A done/one
A one
A two
?? dthree/
?? dtwo/
?? four
?? three
EOF
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 1
opendir: 1
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'verify untracked cache dump' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude $EMPTY_BLOB
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ 0000000000000000000000000000000000000000 recurse valid
dthree/
dtwo/
four
three
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
three
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'new .gitignore invalidates recursively' '
avoid_racy &&
echo four >.gitignore &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
cat >../status.expect <<EOF &&
A done/one
A one
A two
?? .gitignore
?? dthree/
?? dtwo/
?? three
EOF
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 1
directory invalidation: 1
opendir: 4
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'verify untracked cache dump' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude $EMPTY_BLOB
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dthree/
dtwo/
three
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
three
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'new info/exclude invalidates everything' '
avoid_racy &&
echo three >>.git/info/exclude &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
cat >../status.expect <<EOF &&
A done/one
A one
A two
?? .gitignore
?? dtwo/
EOF
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 1
directory invalidation: 0
opendir: 4
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'verify untracked cache dump' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dtwo/
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'move two from tracked to untracked' '
git rm --cached two &&
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'status after the move' '
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
cat >../status.expect <<EOF &&
A done/one
A one
?? .gitignore
?? dtwo/
?? two
EOF
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 1
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'verify untracked cache dump' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dtwo/
two
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'move two from untracked to tracked' '
git add two &&
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'status after the move' '
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
cat >../status.expect <<EOF &&
A done/one
A one
A two
?? .gitignore
?? dtwo/
EOF
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 1
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'verify untracked cache dump' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dtwo/
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'set up for sparse checkout testing' '
echo two >done/.gitignore &&
echo three >>done/.gitignore &&
echo two >done/two &&
git add -f done/two done/.gitignore &&
git commit -m "first commit"
'
test_expect_success 'status after commit' '
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../actual &&
cat >../status.expect <<EOF &&
?? .gitignore
?? dtwo/
EOF
test_cmp ../status.expect ../actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 2
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'untracked cache correct after commit' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dtwo/
/done/ 0000000000000000000000000000000000000000 recurse valid
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'set up sparse checkout' '
echo "done/[a-z]*" >.git/info/sparse-checkout &&
test_config core.sparsecheckout true &&
git checkout master &&
git update-index --force-untracked-cache &&
git status --porcelain >/dev/null && # prime the cache
test_path_is_missing done/.gitignore &&
test_path_is_file done/one
'
test_expect_success 'create/modify files, some of which are gitignored' '
echo two bis >done/two &&
echo three >done/three && # three is gitignored
echo four >done/four && # four is gitignored at a higher level
echo five >done/five && # five is not gitignored
echo test >base && #we need to ensure that the root dir is touched
rm base &&
sync_mtime
'
test_expect_success 'test sparse status with untracked cache' '
: >../trace &&
avoid_racy &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../status.actual &&
cat >../status.expect <<EOF &&
M done/two
?? .gitignore
?? done/five
?? dtwo/
EOF
test_cmp ../status.expect ../status.actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 1
directory invalidation: 2
opendir: 2
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'untracked cache correct after status' '
test-dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dtwo/
/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
five
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect ../actual
'
test_expect_success 'test sparse status again with untracked cache' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../status.actual &&
cat >../status.expect <<EOF &&
M done/two
?? .gitignore
?? done/five
?? dtwo/
EOF
test_cmp ../status.expect ../status.actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 0
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'set up for test of subdir and sparse checkouts' '
mkdir done/sub &&
mkdir done/sub/sub &&
echo "sub" > done/sub/sub/file
'
test_expect_success 'test sparse status with untracked cache and subdir' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../status.actual &&
cat >../status.expect <<EOF &&
M done/two
?? .gitignore
?? done/five
?? done/sub/
?? dtwo/
EOF
test_cmp ../status.expect ../status.actual &&
cat >../trace.expect <<EOF &&
node creation: 2
gitignore invalidation: 0
directory invalidation: 1
opendir: 3
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
test-dump-untracked-cache >../actual &&
cat >../expect-from-test-dump <<EOF &&
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
core.excludesfile 0000000000000000000000000000000000000000
exclude_per_dir .gitignore
flags 00000006
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
.gitignore
dtwo/
/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
five
sub/
/done/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
sub/
/done/sub/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
file
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
two
EOF
test_cmp ../expect-from-test-dump ../actual
'
test_expect_success 'test sparse status again with untracked cache and subdir' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
git status --porcelain >../status.actual &&
test_cmp ../status.expect ../status.actual &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 0
EOF
test_cmp ../trace.expect ../trace
'
test_expect_success 'move entry in subdir from untracked to cached' '
git add dtwo/two &&
git status --porcelain >../status.actual &&
cat >../status.expect <<EOF &&
M done/two
A dtwo/two
?? .gitignore
?? done/five
?? done/sub/
EOF
test_cmp ../status.expect ../status.actual
'
test_expect_success 'move entry in subdir from cached to untracked' '
git rm --cached dtwo/two &&
git status --porcelain >../status.actual &&
cat >../status.expect <<EOF &&
M done/two
?? .gitignore
?? done/five
?? done/sub/
?? dtwo/
EOF
test_cmp ../status.expect ../status.actual
'
test_expect_success '--no-untracked-cache removes the cache' '
git update-index --no-untracked-cache &&
test-dump-untracked-cache >../actual &&
echo "no untracked cache" >../expect-no-uc &&
test_cmp ../expect-no-uc ../actual
'
test_expect_success 'git status does not change anything' '
git status &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-no-uc ../actual
'
test_expect_success 'setting core.untrackedCache to true and using git status creates the cache' '
git config core.untrackedCache true &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-no-uc ../actual &&
git status &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-from-test-dump ../actual
'
test_expect_success 'using --no-untracked-cache does not fail when core.untrackedCache is true' '
git update-index --no-untracked-cache &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-no-uc ../actual &&
git update-index --untracked-cache &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-empty ../actual
'
test_expect_success 'setting core.untrackedCache to false and using git status removes the cache' '
git config core.untrackedCache false &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-empty ../actual &&
git status &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-no-uc ../actual
'
test_expect_success 'using --untracked-cache does not fail when core.untrackedCache is false' '
git update-index --untracked-cache &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-empty ../actual
'
test_expect_success 'setting core.untrackedCache to keep' '
git config core.untrackedCache keep &&
git update-index --untracked-cache &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-empty ../actual &&
git status &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-from-test-dump ../actual &&
git update-index --no-untracked-cache &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-no-uc ../actual &&
git update-index --force-untracked-cache &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-empty ../actual &&
git status &&
test-dump-untracked-cache >../actual &&
test_cmp ../expect-from-test-dump ../actual
'
test_expect_success 'test ident field is working' '
mkdir ../other_worktree &&
cp -R done dthree dtwo four three ../other_worktree &&
GIT_WORK_TREE=../other_worktree git status 2>../err &&
echo "warning: Untracked cache is disabled on this system or location." >../expect &&
test_i18ncmp ../expect ../err
'
test_expect_success 'untracked cache survives a checkout' '
git commit --allow-empty -m empty &&
test-dump-untracked-cache >../before &&
test_when_finished "git checkout master" &&
git checkout -b other_branch &&
test-dump-untracked-cache >../after &&
test_cmp ../before ../after &&
test_commit test &&
test-dump-untracked-cache >../before &&
git checkout master &&
test-dump-untracked-cache >../after &&
test_cmp ../before ../after
'
test_expect_success 'untracked cache survives a commit' '
test-dump-untracked-cache >../before &&
git add done/two &&
git commit -m commit &&
test-dump-untracked-cache >../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