diff options
Diffstat (limited to '')
-rwxr-xr-x | t/t7063-status-untracked-cache.sh | 994 |
1 files changed, 994 insertions, 0 deletions
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh new file mode 100755 index 0000000..8929ef4 --- /dev/null +++ b/t/t7063-status-untracked-cache.sh @@ -0,0 +1,994 @@ +#!/bin/sh + +test_description='test untracked cache' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./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. + +GIT_FORCE_UNTRACKED_CACHE=true +export GIT_FORCE_UNTRACKED_CACHE + +sync_mtime () { + find . -type d -exec ls -ld {} + >/dev/null +} + +avoid_racy() { + sleep 1 +} + +status_is_clean() { + git status --porcelain >../status.actual && + test_must_be_empty ../status.actual +} + +# Ignore_Untracked_Cache, abbreviated to 3 letters because then people can +# compare commands side-by-side, e.g. +# iuc status --porcelain >expect && +# git status --porcelain >actual && +# test_cmp expect actual +iuc () { + git ls-files -s >../current-index-entries + git ls-files -t | sed -ne s/^S.//p >../current-sparse-entries + + GIT_INDEX_FILE=.git/tmp_index + export GIT_INDEX_FILE + git update-index --index-info <../current-index-entries + git update-index --skip-worktree $(cat ../current-sparse-entries) + + git -c core.untrackedCache=false "$@" + ret=$? + + rm ../current-index-entries + rm $GIT_INDEX_FILE + unset GIT_INDEX_FILE + + return $ret +} + +get_relevant_traces () { + # From the GIT_TRACE2_PERF data of the form + # $TIME $FILE:$LINE | d0 | main | data | r1 | ? | ? | read_directo | $RELEVANT_STAT + # extract the $RELEVANT_STAT fields. We don't care about region_enter + # or region_leave, or stats for things outside read_directory. + INPUT_FILE=$1 + OUTPUT_FILE=$2 + grep data.*read_directo $INPUT_FILE | + cut -d "|" -f 9 | + grep -v visited \ + >"$OUTPUT_FILE" +} + + +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 --template= worktree && + cd worktree && + mkdir done dtwo dthree && + touch one two three done/one dtwo/two dthree/three && + test-tool chmtime =-300 one two three done/one dtwo/two dthree/three && + test-tool chmtime =-300 done dtwo dthree && + test-tool chmtime =-300 . && + git add one two done/one && + mkdir .git/info && + : >.git/info/exclude && + git update-index --untracked-cache && + test_oid_cache <<-EOF + root sha1:e6fcc8f2ee31bae321d66afd183fcb7237afae6e + root sha256:b90c672088c015b9c83876e919da311bad4cd39639fb139f988af6a11493b974 + + exclude sha1:13263c0978fb9fad16b2d580fb800b6d811c3ff0 + exclude sha256:fe4aaa1bbbbce4cb8f73426748a14c5ad6026b26f90505a0bf2494b165a5b76c + + done sha1:1946f0437f90c5005533cbe1736a6451ca301714 + done sha256:7f079501d79f665b3acc50f5e0e9e94509084d5032ac20113a37dd5029b757cc + EOF +' + +test_expect_success 'untracked cache is empty' ' + test-tool dump-untracked-cache >../actual && + cat >../expect-empty <<EOF && +info/exclude $ZERO_OID +core.excludesfile $ZERO_OID +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 $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $ZERO_OID recurse valid +dthree/ +dtwo/ +three +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +three +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + +test_expect_success 'status first time (empty cache)' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:3 + ....gitignore-invalidation:1 + ....directory-invalidation:0 + ....opendir:4 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'untracked cache after first status' ' + test-tool dump-untracked-cache >../actual && + test_cmp ../dump.expect ../actual +' + +test_expect_success 'status second time (fully populated cache)' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:0 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'untracked cache after second status' ' + test-tool dump-untracked-cache >../actual && + test_cmp ../dump.expect ../actual +' + +cat >../status_uall.expect <<EOF && +A done/one +A one +A two +?? dthree/three +?? dtwo/two +?? three +EOF + +# Bypassing the untracked cache here is not desirable from an +# end-user perspective, but is expected in the current design. +# The untracked cache data stored for a -unormal run cannot be +# correctly used in a -uall run - it would yield incorrect output. +test_expect_success 'untracked cache is bypassed with -uall' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status -uall --porcelain >../actual && + iuc status -uall --porcelain >../status.iuc && + test_cmp ../status_uall.expect ../status.iuc && + test_cmp ../status_uall.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'untracked cache remains after bypass' ' + test-tool dump-untracked-cache >../actual && + test_cmp ../dump.expect ../actual +' + +test_expect_success 'if -uall is configured, untracked cache gets populated by default' ' + test_config status.showuntrackedfiles all && + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + test_cmp ../status_uall.expect ../status.iuc && + test_cmp ../status_uall.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:3 + ....gitignore-invalidation:1 + ....directory-invalidation:0 + ....opendir:4 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +cat >../dump_uall.expect <<EOF && +info/exclude $EMPTY_BLOB +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000000 +/ $ZERO_OID recurse valid +three +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse valid +three +/dtwo/ $ZERO_OID recurse valid +two +EOF + +test_expect_success 'if -uall was configured, untracked cache is populated' ' + test-tool dump-untracked-cache >../actual && + test_cmp ../dump_uall.expect ../actual +' + +test_expect_success 'if -uall is configured, untracked cache is used by default' ' + test_config status.showuntrackedfiles all && + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + test_cmp ../status_uall.expect ../status.iuc && + test_cmp ../status_uall.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:0 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +# Bypassing the untracked cache here is not desirable from an +# end-user perspective, but is expected in the current design. +# The untracked cache data stored for a -all run cannot be +# correctly used in a -unormal run - it would yield incorrect +# output. +test_expect_success 'if -uall is configured, untracked cache is bypassed with -unormal' ' + test_config status.showuntrackedfiles all && + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status -unormal --porcelain >../actual && + iuc status -unormal --porcelain >../status.iuc && + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'repopulate untracked cache for -unormal' ' + git status --porcelain +' + +test_expect_success 'modify in root directory, one dir invalidation' ' + : >four && + test-tool chmtime =-240 four && + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && +A done/one +A one +A two +?? dthree/ +?? dtwo/ +?? four +?? three +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:1 + ....opendir:1 +EOF + test_cmp ../trace.expect ../trace.relevant + +' + +test_expect_success 'verify untracked cache dump' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $EMPTY_BLOB +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $ZERO_OID recurse valid +dthree/ +dtwo/ +four +three +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +three +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'new .gitignore invalidates recursively' ' + echo four >.gitignore && + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && +A done/one +A one +A two +?? .gitignore +?? dthree/ +?? dtwo/ +?? three +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:1 + ....directory-invalidation:1 + ....opendir:4 +EOF + test_cmp ../trace.expect ../trace.relevant + +' + +test_expect_success 'verify untracked cache dump' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $EMPTY_BLOB +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dthree/ +dtwo/ +three +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +three +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'new info/exclude invalidates everything' ' + echo three >>.git/info/exclude && + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && +A done/one +A one +A two +?? .gitignore +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:1 + ....directory-invalidation:0 + ....opendir:4 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'verify untracked cache dump' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dtwo/ +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'move two from tracked to untracked' ' + git rm --cached two && + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'status after the move' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && +A done/one +A one +?? .gitignore +?? dtwo/ +?? two +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:1 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'verify untracked cache dump' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dtwo/ +two +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'move two from untracked to tracked' ' + git add two && + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'status after the move' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && +A done/one +A one +A two +?? .gitignore +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:1 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'verify untracked cache dump' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dtwo/ +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID 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.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && +?? .gitignore +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:2 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'untracked cache correct after commit' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dtwo/ +/done/ $ZERO_OID recurse valid +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID 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 main && + 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 + test-tool chmtime =-180 done/two done/three done/four done/five done && + # we need to ensure that the root dir is touched (in the past); + test-tool chmtime =-180 . && + sync_mtime +' + +test_expect_success 'test sparse status with untracked cache' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../status.actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && + M done/two +?? .gitignore +?? done/five +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../status.actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:1 + ....directory-invalidation:2 + ....opendir:2 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'untracked cache correct after status' ' + test-tool dump-untracked-cache >../actual && + cat >../expect <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dtwo/ +/done/ $(test_oid done) recurse valid +five +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID recurse check_only valid +two +EOF + test_cmp ../expect ../actual +' + +test_expect_success 'test sparse status again with untracked cache' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../status.actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && + M done/two +?? .gitignore +?? done/five +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../status.actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:0 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +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-tool chmtime =-120 done/sub/sub/file done/sub/sub done/sub done +' + +test_expect_success 'test sparse status with untracked cache and subdir' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../status.actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && + M done/two +?? .gitignore +?? done/five +?? done/sub/ +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../status.actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:2 + ....gitignore-invalidation:0 + ....directory-invalidation:1 + ....opendir:3 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'verify untracked cache dump (sparse/subdirs)' ' + test-tool dump-untracked-cache >../actual && + cat >../expect-from-test-dump <<EOF && +info/exclude $(test_oid exclude) +core.excludesfile $ZERO_OID +exclude_per_dir .gitignore +flags 00000006 +/ $(test_oid root) recurse valid +.gitignore +dtwo/ +/done/ $(test_oid done) recurse valid +five +sub/ +/done/sub/ $ZERO_OID recurse check_only valid +sub/ +/done/sub/sub/ $ZERO_OID recurse check_only valid +file +/dthree/ $ZERO_OID recurse check_only valid +/dtwo/ $ZERO_OID 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' ' + : >../trace.output && + GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ + git status --porcelain >../status.actual && + iuc status --porcelain >../status.iuc && + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../status.actual && + get_relevant_traces ../trace.output ../trace.relevant && + cat >../trace.expect <<EOF && + ....path: + ....node-creation:0 + ....gitignore-invalidation:0 + ....directory-invalidation:0 + ....opendir:0 +EOF + test_cmp ../trace.expect ../trace.relevant +' + +test_expect_success 'move entry in subdir from untracked to cached' ' + git add dtwo/two && + git status --porcelain >../status.actual && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && + M done/two +A dtwo/two +?? .gitignore +?? done/five +?? done/sub/ +EOF + test_cmp ../status.expect ../status.iuc && + 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 && + iuc status --porcelain >../status.iuc && + cat >../status.expect <<EOF && + M done/two +?? .gitignore +?? done/five +?? done/sub/ +?? dtwo/ +EOF + test_cmp ../status.expect ../status.iuc && + test_cmp ../status.expect ../status.actual +' + +test_expect_success '--no-untracked-cache removes the cache' ' + git update-index --no-untracked-cache && + test-tool 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-tool 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-tool dump-untracked-cache >../actual && + test_cmp ../expect-no-uc ../actual && + git status && + test-tool 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-tool dump-untracked-cache >../actual && + test_cmp ../expect-no-uc ../actual && + git update-index --untracked-cache && + test-tool 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-tool dump-untracked-cache >../actual && + test_cmp ../expect-empty ../actual && + git status && + test-tool 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-tool 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-tool dump-untracked-cache >../actual && + test_cmp ../expect-empty ../actual && + git status && + test-tool dump-untracked-cache >../actual && + test_cmp ../expect-from-test-dump ../actual && + git update-index --no-untracked-cache && + test-tool dump-untracked-cache >../actual && + test_cmp ../expect-no-uc ../actual && + git update-index --force-untracked-cache && + test-tool dump-untracked-cache >../actual && + test_cmp ../expect-empty ../actual && + git status && + test-tool 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_cmp ../expect ../err +' + +test_expect_success 'untracked cache survives a checkout' ' + git commit --allow-empty -m empty && + test-tool dump-untracked-cache >../before && + test_when_finished "git checkout main" && + git checkout -b other_branch && + test-tool dump-untracked-cache >../after && + test_cmp ../before ../after && + test_commit test && + test-tool dump-untracked-cache >../before && + git checkout main && + test-tool dump-untracked-cache >../after && + test_cmp ../before ../after +' + +test_expect_success 'untracked cache survives a commit' ' + test-tool dump-untracked-cache >../before && + git add done/two && + git commit -m commit && + test-tool 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 main && + 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 main && + 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 main && + avoid_racy && + status_is_clean && + test-tool dump-untracked-cache >../actual && + grep -F "recurse valid" ../actual >../actual.grep && + cat >../expect.grep <<EOF && +/ $ZERO_OID recurse valid +/two/ $ZERO_OID 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 main && + avoid_racy && + status_is_clean && + status_is_clean +' + +test_expect_success 'empty repo (no index) and core.untrackedCache' ' + git init emptyrepo && + git -C emptyrepo -c core.untrackedCache=true write-tree +' + +test_done |