3 test_description='test untracked cache'
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
10 # On some filesystems (e.g. FreeBSD's ext2 and ufs) directory mtime
11 # is updated lazily after contents in the directory changes, which
12 # forces the untracked cache code to take the slow path. A test
13 # that wants to make sure that the fast path works correctly should
14 # call this helper to make mtime of the containing directory in sync
15 # with the reality before checking the fast path behaviour.
17 # See <20160803174522.5571-1-pclouds@gmail.com> if you want to know
20 GIT_FORCE_UNTRACKED_CACHE=true
21 export GIT_FORCE_UNTRACKED_CACHE
24 find . -type d -exec ls -ld {} + >/dev/null
32 git status --porcelain >../status.actual &&
33 test_must_be_empty ../status.actual
36 # Ignore_Untracked_Cache, abbreviated to 3 letters because then people can
37 # compare commands side-by-side, e.g.
38 # iuc status --porcelain >expect &&
39 # git status --porcelain >actual &&
40 # test_cmp expect actual
42 git ls-files -s >../current-index-entries
43 git ls-files -t | sed -ne s/^S.//p >../current-sparse-entries
45 GIT_INDEX_FILE=.git/tmp_index
47 git update-index --index-info <../current-index-entries
48 git update-index --skip-worktree $(cat ../current-sparse-entries)
50 git -c core.untrackedCache=false "$@"
53 rm ../current-index-entries
60 test_lazy_prereq UNTRACKED_CACHE '
61 { git update-index --test-untracked-cache; ret=$?; } &&
65 if ! test_have_prereq UNTRACKED_CACHE; then
66 skip_all='This system does not support untracked cache'
70 test_expect_success 'core.untrackedCache is unset' '
71 test_must_fail git config --get core.untrackedCache
74 test_expect_success 'setup' '
77 mkdir done dtwo dthree &&
78 touch one two three done/one dtwo/two dthree/three &&
79 git add one two done/one &&
80 : >.git/info/exclude &&
81 git update-index --untracked-cache &&
83 root sha1:e6fcc8f2ee31bae321d66afd183fcb7237afae6e
84 root sha256:b90c672088c015b9c83876e919da311bad4cd39639fb139f988af6a11493b974
86 exclude sha1:13263c0978fb9fad16b2d580fb800b6d811c3ff0
87 exclude sha256:fe4aaa1bbbbce4cb8f73426748a14c5ad6026b26f90505a0bf2494b165a5b76c
89 done sha1:1946f0437f90c5005533cbe1736a6451ca301714
90 done sha256:7f079501d79f665b3acc50f5e0e9e94509084d5032ac20113a37dd5029b757cc
94 test_expect_success 'untracked cache is empty' '
95 test-tool dump-untracked-cache >../actual &&
96 cat >../expect-empty <<EOF &&
97 info/exclude $ZERO_OID
98 core.excludesfile $ZERO_OID
99 exclude_per_dir .gitignore
102 test_cmp ../expect-empty ../actual
105 cat >../status.expect <<EOF &&
114 cat >../dump.expect <<EOF &&
115 info/exclude $EMPTY_BLOB
116 core.excludesfile $ZERO_OID
117 exclude_per_dir .gitignore
119 / $ZERO_OID recurse valid
123 /done/ $ZERO_OID recurse valid
124 /dthree/ $ZERO_OID recurse check_only valid
126 /dtwo/ $ZERO_OID recurse check_only valid
130 test_expect_success 'status first time (empty cache)' '
133 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
134 git status --porcelain >../actual &&
135 iuc status --porcelain >../status.iuc &&
136 test_cmp ../status.expect ../status.iuc &&
137 test_cmp ../status.expect ../actual &&
138 cat >../trace.expect <<EOF &&
140 gitignore invalidation: 1
141 directory invalidation: 0
144 test_cmp ../trace.expect ../trace
147 test_expect_success 'untracked cache after first status' '
148 test-tool dump-untracked-cache >../actual &&
149 test_cmp ../dump.expect ../actual
152 test_expect_success 'status second time (fully populated cache)' '
155 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
156 git status --porcelain >../actual &&
157 iuc status --porcelain >../status.iuc &&
158 test_cmp ../status.expect ../status.iuc &&
159 test_cmp ../status.expect ../actual &&
160 cat >../trace.expect <<EOF &&
162 gitignore invalidation: 0
163 directory invalidation: 0
166 test_cmp ../trace.expect ../trace
169 test_expect_success 'untracked cache after second status' '
170 test-tool dump-untracked-cache >../actual &&
171 test_cmp ../dump.expect ../actual
174 test_expect_success 'modify in root directory, one dir invalidation' '
178 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
179 git status --porcelain >../actual &&
180 iuc status --porcelain >../status.iuc &&
181 cat >../status.expect <<EOF &&
190 test_cmp ../status.expect ../status.iuc &&
191 test_cmp ../status.expect ../actual &&
192 cat >../trace.expect <<EOF &&
194 gitignore invalidation: 0
195 directory invalidation: 1
198 test_cmp ../trace.expect ../trace
202 test_expect_success 'verify untracked cache dump' '
203 test-tool dump-untracked-cache >../actual &&
204 cat >../expect <<EOF &&
205 info/exclude $EMPTY_BLOB
206 core.excludesfile $ZERO_OID
207 exclude_per_dir .gitignore
209 / $ZERO_OID recurse valid
214 /done/ $ZERO_OID recurse valid
215 /dthree/ $ZERO_OID recurse check_only valid
217 /dtwo/ $ZERO_OID recurse check_only valid
220 test_cmp ../expect ../actual
223 test_expect_success 'new .gitignore invalidates recursively' '
225 echo four >.gitignore &&
227 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
228 git status --porcelain >../actual &&
229 iuc status --porcelain >../status.iuc &&
230 cat >../status.expect <<EOF &&
239 test_cmp ../status.expect ../status.iuc &&
240 test_cmp ../status.expect ../actual &&
241 cat >../trace.expect <<EOF &&
243 gitignore invalidation: 1
244 directory invalidation: 1
247 test_cmp ../trace.expect ../trace
251 test_expect_success 'verify untracked cache dump' '
252 test-tool dump-untracked-cache >../actual &&
253 cat >../expect <<EOF &&
254 info/exclude $EMPTY_BLOB
255 core.excludesfile $ZERO_OID
256 exclude_per_dir .gitignore
258 / $(test_oid root) recurse valid
263 /done/ $ZERO_OID recurse valid
264 /dthree/ $ZERO_OID recurse check_only valid
266 /dtwo/ $ZERO_OID recurse check_only valid
269 test_cmp ../expect ../actual
272 test_expect_success 'new info/exclude invalidates everything' '
274 echo three >>.git/info/exclude &&
276 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
277 git status --porcelain >../actual &&
278 iuc status --porcelain >../status.iuc &&
279 cat >../status.expect <<EOF &&
286 test_cmp ../status.expect ../status.iuc &&
287 test_cmp ../status.expect ../actual &&
288 cat >../trace.expect <<EOF &&
290 gitignore invalidation: 1
291 directory invalidation: 0
294 test_cmp ../trace.expect ../trace
297 test_expect_success 'verify untracked cache dump' '
298 test-tool dump-untracked-cache >../actual &&
299 cat >../expect <<EOF &&
300 info/exclude $(test_oid exclude)
301 core.excludesfile $ZERO_OID
302 exclude_per_dir .gitignore
304 / $(test_oid root) recurse valid
307 /done/ $ZERO_OID recurse valid
308 /dthree/ $ZERO_OID recurse check_only valid
309 /dtwo/ $ZERO_OID recurse check_only valid
312 test_cmp ../expect ../actual
315 test_expect_success 'move two from tracked to untracked' '
316 git rm --cached two &&
317 test-tool dump-untracked-cache >../actual &&
318 cat >../expect <<EOF &&
319 info/exclude $(test_oid exclude)
320 core.excludesfile $ZERO_OID
321 exclude_per_dir .gitignore
323 / $(test_oid root) recurse
324 /done/ $ZERO_OID recurse valid
325 /dthree/ $ZERO_OID recurse check_only valid
326 /dtwo/ $ZERO_OID recurse check_only valid
329 test_cmp ../expect ../actual
332 test_expect_success 'status after the move' '
334 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
335 git status --porcelain >../actual &&
336 iuc status --porcelain >../status.iuc &&
337 cat >../status.expect <<EOF &&
344 test_cmp ../status.expect ../status.iuc &&
345 test_cmp ../status.expect ../actual &&
346 cat >../trace.expect <<EOF &&
348 gitignore invalidation: 0
349 directory invalidation: 0
352 test_cmp ../trace.expect ../trace
355 test_expect_success 'verify untracked cache dump' '
356 test-tool dump-untracked-cache >../actual &&
357 cat >../expect <<EOF &&
358 info/exclude $(test_oid exclude)
359 core.excludesfile $ZERO_OID
360 exclude_per_dir .gitignore
362 / $(test_oid root) recurse valid
366 /done/ $ZERO_OID recurse valid
367 /dthree/ $ZERO_OID recurse check_only valid
368 /dtwo/ $ZERO_OID recurse check_only valid
371 test_cmp ../expect ../actual
374 test_expect_success 'move two from untracked to tracked' '
376 test-tool dump-untracked-cache >../actual &&
377 cat >../expect <<EOF &&
378 info/exclude $(test_oid exclude)
379 core.excludesfile $ZERO_OID
380 exclude_per_dir .gitignore
382 / $(test_oid root) recurse
383 /done/ $ZERO_OID recurse valid
384 /dthree/ $ZERO_OID recurse check_only valid
385 /dtwo/ $ZERO_OID recurse check_only valid
388 test_cmp ../expect ../actual
391 test_expect_success 'status after the move' '
393 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
394 git status --porcelain >../actual &&
395 iuc status --porcelain >../status.iuc &&
396 cat >../status.expect <<EOF &&
403 test_cmp ../status.expect ../status.iuc &&
404 test_cmp ../status.expect ../actual &&
405 cat >../trace.expect <<EOF &&
407 gitignore invalidation: 0
408 directory invalidation: 0
411 test_cmp ../trace.expect ../trace
414 test_expect_success 'verify untracked cache dump' '
415 test-tool dump-untracked-cache >../actual &&
416 cat >../expect <<EOF &&
417 info/exclude $(test_oid exclude)
418 core.excludesfile $ZERO_OID
419 exclude_per_dir .gitignore
421 / $(test_oid root) recurse valid
424 /done/ $ZERO_OID recurse valid
425 /dthree/ $ZERO_OID recurse check_only valid
426 /dtwo/ $ZERO_OID recurse check_only valid
429 test_cmp ../expect ../actual
432 test_expect_success 'set up for sparse checkout testing' '
433 echo two >done/.gitignore &&
434 echo three >>done/.gitignore &&
435 echo two >done/two &&
436 git add -f done/two done/.gitignore &&
437 git commit -m "first commit"
440 test_expect_success 'status after commit' '
442 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
443 git status --porcelain >../actual &&
444 iuc status --porcelain >../status.iuc &&
445 cat >../status.expect <<EOF &&
449 test_cmp ../status.expect ../status.iuc &&
450 test_cmp ../status.expect ../actual &&
451 cat >../trace.expect <<EOF &&
453 gitignore invalidation: 0
454 directory invalidation: 0
457 test_cmp ../trace.expect ../trace
460 test_expect_success 'untracked cache correct after commit' '
461 test-tool dump-untracked-cache >../actual &&
462 cat >../expect <<EOF &&
463 info/exclude $(test_oid exclude)
464 core.excludesfile $ZERO_OID
465 exclude_per_dir .gitignore
467 / $(test_oid root) recurse valid
470 /done/ $ZERO_OID recurse valid
471 /dthree/ $ZERO_OID recurse check_only valid
472 /dtwo/ $ZERO_OID recurse check_only valid
475 test_cmp ../expect ../actual
478 test_expect_success 'set up sparse checkout' '
479 echo "done/[a-z]*" >.git/info/sparse-checkout &&
480 test_config core.sparsecheckout true &&
482 git update-index --force-untracked-cache &&
483 git status --porcelain >/dev/null && # prime the cache
484 test_path_is_missing done/.gitignore &&
485 test_path_is_file done/one
488 test_expect_success 'create/modify files, some of which are gitignored' '
489 echo two bis >done/two &&
490 echo three >done/three && # three is gitignored
491 echo four >done/four && # four is gitignored at a higher level
492 echo five >done/five && # five is not gitignored
493 echo test >base && #we need to ensure that the root dir is touched
498 test_expect_success 'test sparse status with untracked cache' '
501 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
502 git status --porcelain >../status.actual &&
503 iuc status --porcelain >../status.iuc &&
504 cat >../status.expect <<EOF &&
510 test_cmp ../status.expect ../status.iuc &&
511 test_cmp ../status.expect ../status.actual &&
512 cat >../trace.expect <<EOF &&
514 gitignore invalidation: 1
515 directory invalidation: 2
518 test_cmp ../trace.expect ../trace
521 test_expect_success 'untracked cache correct after status' '
522 test-tool dump-untracked-cache >../actual &&
523 cat >../expect <<EOF &&
524 info/exclude $(test_oid exclude)
525 core.excludesfile $ZERO_OID
526 exclude_per_dir .gitignore
528 / $(test_oid root) recurse valid
531 /done/ $(test_oid done) recurse valid
533 /dthree/ $ZERO_OID recurse check_only valid
534 /dtwo/ $ZERO_OID recurse check_only valid
537 test_cmp ../expect ../actual
540 test_expect_success 'test sparse status again with untracked cache' '
543 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
544 git status --porcelain >../status.actual &&
545 iuc status --porcelain >../status.iuc &&
546 cat >../status.expect <<EOF &&
552 test_cmp ../status.expect ../status.iuc &&
553 test_cmp ../status.expect ../status.actual &&
554 cat >../trace.expect <<EOF &&
556 gitignore invalidation: 0
557 directory invalidation: 0
560 test_cmp ../trace.expect ../trace
563 test_expect_success 'set up for test of subdir and sparse checkouts' '
565 mkdir done/sub/sub &&
566 echo "sub" > done/sub/sub/file
569 test_expect_success 'test sparse status with untracked cache and subdir' '
572 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
573 git status --porcelain >../status.actual &&
574 iuc status --porcelain >../status.iuc &&
575 cat >../status.expect <<EOF &&
582 test_cmp ../status.expect ../status.iuc &&
583 test_cmp ../status.expect ../status.actual &&
584 cat >../trace.expect <<EOF &&
586 gitignore invalidation: 0
587 directory invalidation: 1
590 test_cmp ../trace.expect ../trace
593 test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
594 test-tool dump-untracked-cache >../actual &&
595 cat >../expect-from-test-dump <<EOF &&
596 info/exclude $(test_oid exclude)
597 core.excludesfile $ZERO_OID
598 exclude_per_dir .gitignore
600 / $(test_oid root) recurse valid
603 /done/ $(test_oid done) recurse valid
606 /done/sub/ $ZERO_OID recurse check_only valid
608 /done/sub/sub/ $ZERO_OID recurse check_only valid
610 /dthree/ $ZERO_OID recurse check_only valid
611 /dtwo/ $ZERO_OID recurse check_only valid
614 test_cmp ../expect-from-test-dump ../actual
617 test_expect_success 'test sparse status again with untracked cache and subdir' '
620 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
621 git status --porcelain >../status.actual &&
622 iuc status --porcelain >../status.iuc &&
623 test_cmp ../status.expect ../status.iuc &&
624 test_cmp ../status.expect ../status.actual &&
625 cat >../trace.expect <<EOF &&
627 gitignore invalidation: 0
628 directory invalidation: 0
631 test_cmp ../trace.expect ../trace
634 test_expect_success 'move entry in subdir from untracked to cached' '
636 git status --porcelain >../status.actual &&
637 iuc status --porcelain >../status.iuc &&
638 cat >../status.expect <<EOF &&
645 test_cmp ../status.expect ../status.iuc &&
646 test_cmp ../status.expect ../status.actual
649 test_expect_success 'move entry in subdir from cached to untracked' '
650 git rm --cached dtwo/two &&
651 git status --porcelain >../status.actual &&
652 iuc status --porcelain >../status.iuc &&
653 cat >../status.expect <<EOF &&
660 test_cmp ../status.expect ../status.iuc &&
661 test_cmp ../status.expect ../status.actual
664 test_expect_success '--no-untracked-cache removes the cache' '
665 git update-index --no-untracked-cache &&
666 test-tool dump-untracked-cache >../actual &&
667 echo "no untracked cache" >../expect-no-uc &&
668 test_cmp ../expect-no-uc ../actual
671 test_expect_success 'git status does not change anything' '
673 test-tool dump-untracked-cache >../actual &&
674 test_cmp ../expect-no-uc ../actual
677 test_expect_success 'setting core.untrackedCache to true and using git status creates the cache' '
678 git config core.untrackedCache true &&
679 test-tool dump-untracked-cache >../actual &&
680 test_cmp ../expect-no-uc ../actual &&
682 test-tool dump-untracked-cache >../actual &&
683 test_cmp ../expect-from-test-dump ../actual
686 test_expect_success 'using --no-untracked-cache does not fail when core.untrackedCache is true' '
687 git update-index --no-untracked-cache &&
688 test-tool dump-untracked-cache >../actual &&
689 test_cmp ../expect-no-uc ../actual &&
690 git update-index --untracked-cache &&
691 test-tool dump-untracked-cache >../actual &&
692 test_cmp ../expect-empty ../actual
695 test_expect_success 'setting core.untrackedCache to false and using git status removes the cache' '
696 git config core.untrackedCache false &&
697 test-tool dump-untracked-cache >../actual &&
698 test_cmp ../expect-empty ../actual &&
700 test-tool dump-untracked-cache >../actual &&
701 test_cmp ../expect-no-uc ../actual
704 test_expect_success 'using --untracked-cache does not fail when core.untrackedCache is false' '
705 git update-index --untracked-cache &&
706 test-tool dump-untracked-cache >../actual &&
707 test_cmp ../expect-empty ../actual
710 test_expect_success 'setting core.untrackedCache to keep' '
711 git config core.untrackedCache keep &&
712 git update-index --untracked-cache &&
713 test-tool dump-untracked-cache >../actual &&
714 test_cmp ../expect-empty ../actual &&
716 test-tool dump-untracked-cache >../actual &&
717 test_cmp ../expect-from-test-dump ../actual &&
718 git update-index --no-untracked-cache &&
719 test-tool dump-untracked-cache >../actual &&
720 test_cmp ../expect-no-uc ../actual &&
721 git update-index --force-untracked-cache &&
722 test-tool dump-untracked-cache >../actual &&
723 test_cmp ../expect-empty ../actual &&
725 test-tool dump-untracked-cache >../actual &&
726 test_cmp ../expect-from-test-dump ../actual
729 test_expect_success 'test ident field is working' '
730 mkdir ../other_worktree &&
731 cp -R done dthree dtwo four three ../other_worktree &&
732 GIT_WORK_TREE=../other_worktree git status 2>../err &&
733 echo "warning: untracked cache is disabled on this system or location" >../expect &&
734 test_i18ncmp ../expect ../err
737 test_expect_success 'untracked cache survives a checkout' '
738 git commit --allow-empty -m empty &&
739 test-tool dump-untracked-cache >../before &&
740 test_when_finished "git checkout main" &&
741 git checkout -b other_branch &&
742 test-tool dump-untracked-cache >../after &&
743 test_cmp ../before ../after &&
745 test-tool dump-untracked-cache >../before &&
747 test-tool dump-untracked-cache >../after &&
748 test_cmp ../before ../after
751 test_expect_success 'untracked cache survives a commit' '
752 test-tool dump-untracked-cache >../before &&
754 git commit -m commit &&
755 test-tool dump-untracked-cache >../after &&
756 test_cmp ../before ../after
759 test_expect_success 'teardown worktree' '
763 test_expect_success SYMLINKS 'setup worktree for symlink test' '
764 git init worktree-symlink &&
765 cd worktree-symlink &&
766 git config core.untrackedCache true &&
768 touch one/file two/file &&
769 git add one/file two/file &&
770 git commit -m"first commit" &&
774 git commit -m"second commit"
777 test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=true' '
778 git checkout HEAD~ &&
787 test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=false' '
788 git config core.untrackedCache false &&
789 git checkout HEAD~ &&
798 test_expect_success 'setup worktree for non-symlink test' '
799 git init worktree-non-symlink &&
800 cd worktree-non-symlink &&
801 git config core.untrackedCache true &&
803 touch one/file two/file &&
804 git add one/file two/file &&
805 git commit -m"first commit" &&
809 git commit -m"second commit"
812 test_expect_success '"status" after file replacement should be clean with UC=true' '
813 git checkout HEAD~ &&
819 test-tool dump-untracked-cache >../actual &&
820 grep -F "recurse valid" ../actual >../actual.grep &&
821 cat >../expect.grep <<EOF &&
822 / $ZERO_OID recurse valid
823 /two/ $ZERO_OID recurse valid
826 test_cmp ../expect.grep ../actual.grep
829 test_expect_success '"status" after file replacement should be clean with UC=false' '
830 git config core.untrackedCache false &&
831 git checkout HEAD~ &&