3 test_description='test untracked cache'
7 # On some filesystems (e.g. FreeBSD's ext2 and ufs) directory mtime
8 # is updated lazily after contents in the directory changes, which
9 # forces the untracked cache code to take the slow path. A test
10 # that wants to make sure that the fast path works correctly should
11 # call this helper to make mtime of the containing directory in sync
12 # with the reality before checking the fast path behaviour.
14 # See <20160803174522.5571-1-pclouds@gmail.com> if you want to know
17 GIT_FORCE_UNTRACKED_CACHE=true
18 export GIT_FORCE_UNTRACKED_CACHE
21 find . -type d -exec ls -ld {} + >/dev/null
29 git status --porcelain >../status.actual &&
30 test_must_be_empty ../status.actual
33 # Ignore_Untracked_Cache, abbreviated to 3 letters because then people can
34 # compare commands side-by-side, e.g.
35 # iuc status --porcelain >expect &&
36 # git status --porcelain >actual &&
37 # test_cmp expect actual
39 git ls-files -s >../current-index-entries
40 git ls-files -t | sed -ne s/^S.//p >../current-sparse-entries
42 GIT_INDEX_FILE=.git/tmp_index
44 git update-index --index-info <../current-index-entries
45 git update-index --skip-worktree $(cat ../current-sparse-entries)
47 git -c core.untrackedCache=false "$@"
50 rm ../current-index-entries
57 test_lazy_prereq UNTRACKED_CACHE '
58 { git update-index --test-untracked-cache; ret=$?; } &&
62 if ! test_have_prereq UNTRACKED_CACHE; then
63 skip_all='This system does not support untracked cache'
67 test_expect_success 'core.untrackedCache is unset' '
68 test_must_fail git config --get core.untrackedCache
71 test_expect_success 'setup' '
74 mkdir done dtwo dthree &&
75 touch one two three done/one dtwo/two dthree/three &&
76 git add one two done/one &&
77 : >.git/info/exclude &&
78 git update-index --untracked-cache &&
80 root sha1:e6fcc8f2ee31bae321d66afd183fcb7237afae6e
81 root sha256:b90c672088c015b9c83876e919da311bad4cd39639fb139f988af6a11493b974
83 exclude sha1:13263c0978fb9fad16b2d580fb800b6d811c3ff0
84 exclude sha256:fe4aaa1bbbbce4cb8f73426748a14c5ad6026b26f90505a0bf2494b165a5b76c
86 done sha1:1946f0437f90c5005533cbe1736a6451ca301714
87 done sha256:7f079501d79f665b3acc50f5e0e9e94509084d5032ac20113a37dd5029b757cc
91 test_expect_success 'untracked cache is empty' '
92 test-tool dump-untracked-cache >../actual &&
93 cat >../expect-empty <<EOF &&
94 info/exclude $ZERO_OID
95 core.excludesfile $ZERO_OID
96 exclude_per_dir .gitignore
99 test_cmp ../expect-empty ../actual
102 cat >../status.expect <<EOF &&
111 cat >../dump.expect <<EOF &&
112 info/exclude $EMPTY_BLOB
113 core.excludesfile $ZERO_OID
114 exclude_per_dir .gitignore
116 / $ZERO_OID recurse valid
120 /done/ $ZERO_OID recurse valid
121 /dthree/ $ZERO_OID recurse check_only valid
123 /dtwo/ $ZERO_OID recurse check_only valid
127 test_expect_success 'status first time (empty cache)' '
130 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
131 git status --porcelain >../actual &&
132 iuc status --porcelain >../status.iuc &&
133 test_cmp ../status.expect ../status.iuc &&
134 test_cmp ../status.expect ../actual &&
135 cat >../trace.expect <<EOF &&
137 gitignore invalidation: 1
138 directory invalidation: 0
141 test_cmp ../trace.expect ../trace
144 test_expect_success 'untracked cache after first status' '
145 test-tool dump-untracked-cache >../actual &&
146 test_cmp ../dump.expect ../actual
149 test_expect_success 'status second time (fully populated cache)' '
152 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
153 git status --porcelain >../actual &&
154 iuc status --porcelain >../status.iuc &&
155 test_cmp ../status.expect ../status.iuc &&
156 test_cmp ../status.expect ../actual &&
157 cat >../trace.expect <<EOF &&
159 gitignore invalidation: 0
160 directory invalidation: 0
163 test_cmp ../trace.expect ../trace
166 test_expect_success 'untracked cache after second status' '
167 test-tool dump-untracked-cache >../actual &&
168 test_cmp ../dump.expect ../actual
171 test_expect_success 'modify in root directory, one dir invalidation' '
175 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
176 git status --porcelain >../actual &&
177 iuc status --porcelain >../status.iuc &&
178 cat >../status.expect <<EOF &&
187 test_cmp ../status.expect ../status.iuc &&
188 test_cmp ../status.expect ../actual &&
189 cat >../trace.expect <<EOF &&
191 gitignore invalidation: 0
192 directory invalidation: 1
195 test_cmp ../trace.expect ../trace
199 test_expect_success 'verify untracked cache dump' '
200 test-tool dump-untracked-cache >../actual &&
201 cat >../expect <<EOF &&
202 info/exclude $EMPTY_BLOB
203 core.excludesfile $ZERO_OID
204 exclude_per_dir .gitignore
206 / $ZERO_OID recurse valid
211 /done/ $ZERO_OID recurse valid
212 /dthree/ $ZERO_OID recurse check_only valid
214 /dtwo/ $ZERO_OID recurse check_only valid
217 test_cmp ../expect ../actual
220 test_expect_success 'new .gitignore invalidates recursively' '
222 echo four >.gitignore &&
224 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
225 git status --porcelain >../actual &&
226 iuc status --porcelain >../status.iuc &&
227 cat >../status.expect <<EOF &&
236 test_cmp ../status.expect ../status.iuc &&
237 test_cmp ../status.expect ../actual &&
238 cat >../trace.expect <<EOF &&
240 gitignore invalidation: 1
241 directory invalidation: 1
244 test_cmp ../trace.expect ../trace
248 test_expect_success 'verify untracked cache dump' '
249 test-tool dump-untracked-cache >../actual &&
250 cat >../expect <<EOF &&
251 info/exclude $EMPTY_BLOB
252 core.excludesfile $ZERO_OID
253 exclude_per_dir .gitignore
255 / $(test_oid root) recurse valid
260 /done/ $ZERO_OID recurse valid
261 /dthree/ $ZERO_OID recurse check_only valid
263 /dtwo/ $ZERO_OID recurse check_only valid
266 test_cmp ../expect ../actual
269 test_expect_success 'new info/exclude invalidates everything' '
271 echo three >>.git/info/exclude &&
273 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
274 git status --porcelain >../actual &&
275 iuc status --porcelain >../status.iuc &&
276 cat >../status.expect <<EOF &&
283 test_cmp ../status.expect ../status.iuc &&
284 test_cmp ../status.expect ../actual &&
285 cat >../trace.expect <<EOF &&
287 gitignore invalidation: 1
288 directory invalidation: 0
291 test_cmp ../trace.expect ../trace
294 test_expect_success 'verify untracked cache dump' '
295 test-tool dump-untracked-cache >../actual &&
296 cat >../expect <<EOF &&
297 info/exclude $(test_oid exclude)
298 core.excludesfile $ZERO_OID
299 exclude_per_dir .gitignore
301 / $(test_oid root) recurse valid
304 /done/ $ZERO_OID recurse valid
305 /dthree/ $ZERO_OID recurse check_only valid
306 /dtwo/ $ZERO_OID recurse check_only valid
309 test_cmp ../expect ../actual
312 test_expect_success 'move two from tracked to untracked' '
313 git rm --cached two &&
314 test-tool dump-untracked-cache >../actual &&
315 cat >../expect <<EOF &&
316 info/exclude $(test_oid exclude)
317 core.excludesfile $ZERO_OID
318 exclude_per_dir .gitignore
320 / $(test_oid root) recurse
321 /done/ $ZERO_OID recurse valid
322 /dthree/ $ZERO_OID recurse check_only valid
323 /dtwo/ $ZERO_OID recurse check_only valid
326 test_cmp ../expect ../actual
329 test_expect_success 'status after the move' '
331 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
332 git status --porcelain >../actual &&
333 iuc status --porcelain >../status.iuc &&
334 cat >../status.expect <<EOF &&
341 test_cmp ../status.expect ../status.iuc &&
342 test_cmp ../status.expect ../actual &&
343 cat >../trace.expect <<EOF &&
345 gitignore invalidation: 0
346 directory invalidation: 0
349 test_cmp ../trace.expect ../trace
352 test_expect_success 'verify untracked cache dump' '
353 test-tool dump-untracked-cache >../actual &&
354 cat >../expect <<EOF &&
355 info/exclude $(test_oid exclude)
356 core.excludesfile $ZERO_OID
357 exclude_per_dir .gitignore
359 / $(test_oid root) recurse valid
363 /done/ $ZERO_OID recurse valid
364 /dthree/ $ZERO_OID recurse check_only valid
365 /dtwo/ $ZERO_OID recurse check_only valid
368 test_cmp ../expect ../actual
371 test_expect_success 'move two from untracked to tracked' '
373 test-tool dump-untracked-cache >../actual &&
374 cat >../expect <<EOF &&
375 info/exclude $(test_oid exclude)
376 core.excludesfile $ZERO_OID
377 exclude_per_dir .gitignore
379 / $(test_oid root) recurse
380 /done/ $ZERO_OID recurse valid
381 /dthree/ $ZERO_OID recurse check_only valid
382 /dtwo/ $ZERO_OID recurse check_only valid
385 test_cmp ../expect ../actual
388 test_expect_success 'status after the move' '
390 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
391 git status --porcelain >../actual &&
392 iuc status --porcelain >../status.iuc &&
393 cat >../status.expect <<EOF &&
400 test_cmp ../status.expect ../status.iuc &&
401 test_cmp ../status.expect ../actual &&
402 cat >../trace.expect <<EOF &&
404 gitignore invalidation: 0
405 directory invalidation: 0
408 test_cmp ../trace.expect ../trace
411 test_expect_success 'verify untracked cache dump' '
412 test-tool dump-untracked-cache >../actual &&
413 cat >../expect <<EOF &&
414 info/exclude $(test_oid exclude)
415 core.excludesfile $ZERO_OID
416 exclude_per_dir .gitignore
418 / $(test_oid root) recurse valid
421 /done/ $ZERO_OID recurse valid
422 /dthree/ $ZERO_OID recurse check_only valid
423 /dtwo/ $ZERO_OID recurse check_only valid
426 test_cmp ../expect ../actual
429 test_expect_success 'set up for sparse checkout testing' '
430 echo two >done/.gitignore &&
431 echo three >>done/.gitignore &&
432 echo two >done/two &&
433 git add -f done/two done/.gitignore &&
434 git commit -m "first commit"
437 test_expect_success 'status after commit' '
439 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
440 git status --porcelain >../actual &&
441 iuc status --porcelain >../status.iuc &&
442 cat >../status.expect <<EOF &&
446 test_cmp ../status.expect ../status.iuc &&
447 test_cmp ../status.expect ../actual &&
448 cat >../trace.expect <<EOF &&
450 gitignore invalidation: 0
451 directory invalidation: 0
454 test_cmp ../trace.expect ../trace
457 test_expect_success 'untracked cache correct after commit' '
458 test-tool dump-untracked-cache >../actual &&
459 cat >../expect <<EOF &&
460 info/exclude $(test_oid exclude)
461 core.excludesfile $ZERO_OID
462 exclude_per_dir .gitignore
464 / $(test_oid root) recurse valid
467 /done/ $ZERO_OID recurse valid
468 /dthree/ $ZERO_OID recurse check_only valid
469 /dtwo/ $ZERO_OID recurse check_only valid
472 test_cmp ../expect ../actual
475 test_expect_success 'set up sparse checkout' '
476 echo "done/[a-z]*" >.git/info/sparse-checkout &&
477 test_config core.sparsecheckout true &&
478 git checkout master &&
479 git update-index --force-untracked-cache &&
480 git status --porcelain >/dev/null && # prime the cache
481 test_path_is_missing done/.gitignore &&
482 test_path_is_file done/one
485 test_expect_success 'create/modify files, some of which are gitignored' '
486 echo two bis >done/two &&
487 echo three >done/three && # three is gitignored
488 echo four >done/four && # four is gitignored at a higher level
489 echo five >done/five && # five is not gitignored
490 echo test >base && #we need to ensure that the root dir is touched
495 test_expect_success 'test sparse status with untracked cache' '
498 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
499 git status --porcelain >../status.actual &&
500 iuc status --porcelain >../status.iuc &&
501 cat >../status.expect <<EOF &&
507 test_cmp ../status.expect ../status.iuc &&
508 test_cmp ../status.expect ../status.actual &&
509 cat >../trace.expect <<EOF &&
511 gitignore invalidation: 1
512 directory invalidation: 2
515 test_cmp ../trace.expect ../trace
518 test_expect_success 'untracked cache correct after status' '
519 test-tool dump-untracked-cache >../actual &&
520 cat >../expect <<EOF &&
521 info/exclude $(test_oid exclude)
522 core.excludesfile $ZERO_OID
523 exclude_per_dir .gitignore
525 / $(test_oid root) recurse valid
528 /done/ $(test_oid done) recurse valid
530 /dthree/ $ZERO_OID recurse check_only valid
531 /dtwo/ $ZERO_OID recurse check_only valid
534 test_cmp ../expect ../actual
537 test_expect_success 'test sparse status again with untracked cache' '
540 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
541 git status --porcelain >../status.actual &&
542 iuc status --porcelain >../status.iuc &&
543 cat >../status.expect <<EOF &&
549 test_cmp ../status.expect ../status.iuc &&
550 test_cmp ../status.expect ../status.actual &&
551 cat >../trace.expect <<EOF &&
553 gitignore invalidation: 0
554 directory invalidation: 0
557 test_cmp ../trace.expect ../trace
560 test_expect_success 'set up for test of subdir and sparse checkouts' '
562 mkdir done/sub/sub &&
563 echo "sub" > done/sub/sub/file
566 test_expect_success 'test sparse status with untracked cache and subdir' '
569 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
570 git status --porcelain >../status.actual &&
571 iuc status --porcelain >../status.iuc &&
572 cat >../status.expect <<EOF &&
579 test_cmp ../status.expect ../status.iuc &&
580 test_cmp ../status.expect ../status.actual &&
581 cat >../trace.expect <<EOF &&
583 gitignore invalidation: 0
584 directory invalidation: 1
587 test_cmp ../trace.expect ../trace
590 test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
591 test-tool dump-untracked-cache >../actual &&
592 cat >../expect-from-test-dump <<EOF &&
593 info/exclude $(test_oid exclude)
594 core.excludesfile $ZERO_OID
595 exclude_per_dir .gitignore
597 / $(test_oid root) recurse valid
600 /done/ $(test_oid done) recurse valid
603 /done/sub/ $ZERO_OID recurse check_only valid
605 /done/sub/sub/ $ZERO_OID recurse check_only valid
607 /dthree/ $ZERO_OID recurse check_only valid
608 /dtwo/ $ZERO_OID recurse check_only valid
611 test_cmp ../expect-from-test-dump ../actual
614 test_expect_success 'test sparse status again with untracked cache and subdir' '
617 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
618 git status --porcelain >../status.actual &&
619 iuc status --porcelain >../status.iuc &&
620 test_cmp ../status.expect ../status.iuc &&
621 test_cmp ../status.expect ../status.actual &&
622 cat >../trace.expect <<EOF &&
624 gitignore invalidation: 0
625 directory invalidation: 0
628 test_cmp ../trace.expect ../trace
631 test_expect_success 'move entry in subdir from untracked to cached' '
633 git status --porcelain >../status.actual &&
634 iuc status --porcelain >../status.iuc &&
635 cat >../status.expect <<EOF &&
642 test_cmp ../status.expect ../status.iuc &&
643 test_cmp ../status.expect ../status.actual
646 test_expect_success 'move entry in subdir from cached to untracked' '
647 git rm --cached dtwo/two &&
648 git status --porcelain >../status.actual &&
649 iuc status --porcelain >../status.iuc &&
650 cat >../status.expect <<EOF &&
657 test_cmp ../status.expect ../status.iuc &&
658 test_cmp ../status.expect ../status.actual
661 test_expect_success '--no-untracked-cache removes the cache' '
662 git update-index --no-untracked-cache &&
663 test-tool dump-untracked-cache >../actual &&
664 echo "no untracked cache" >../expect-no-uc &&
665 test_cmp ../expect-no-uc ../actual
668 test_expect_success 'git status does not change anything' '
670 test-tool dump-untracked-cache >../actual &&
671 test_cmp ../expect-no-uc ../actual
674 test_expect_success 'setting core.untrackedCache to true and using git status creates the cache' '
675 git config core.untrackedCache true &&
676 test-tool dump-untracked-cache >../actual &&
677 test_cmp ../expect-no-uc ../actual &&
679 test-tool dump-untracked-cache >../actual &&
680 test_cmp ../expect-from-test-dump ../actual
683 test_expect_success 'using --no-untracked-cache does not fail when core.untrackedCache is true' '
684 git update-index --no-untracked-cache &&
685 test-tool dump-untracked-cache >../actual &&
686 test_cmp ../expect-no-uc ../actual &&
687 git update-index --untracked-cache &&
688 test-tool dump-untracked-cache >../actual &&
689 test_cmp ../expect-empty ../actual
692 test_expect_success 'setting core.untrackedCache to false and using git status removes the cache' '
693 git config core.untrackedCache false &&
694 test-tool dump-untracked-cache >../actual &&
695 test_cmp ../expect-empty ../actual &&
697 test-tool dump-untracked-cache >../actual &&
698 test_cmp ../expect-no-uc ../actual
701 test_expect_success 'using --untracked-cache does not fail when core.untrackedCache is false' '
702 git update-index --untracked-cache &&
703 test-tool dump-untracked-cache >../actual &&
704 test_cmp ../expect-empty ../actual
707 test_expect_success 'setting core.untrackedCache to keep' '
708 git config core.untrackedCache keep &&
709 git update-index --untracked-cache &&
710 test-tool dump-untracked-cache >../actual &&
711 test_cmp ../expect-empty ../actual &&
713 test-tool dump-untracked-cache >../actual &&
714 test_cmp ../expect-from-test-dump ../actual &&
715 git update-index --no-untracked-cache &&
716 test-tool dump-untracked-cache >../actual &&
717 test_cmp ../expect-no-uc ../actual &&
718 git update-index --force-untracked-cache &&
719 test-tool dump-untracked-cache >../actual &&
720 test_cmp ../expect-empty ../actual &&
722 test-tool dump-untracked-cache >../actual &&
723 test_cmp ../expect-from-test-dump ../actual
726 test_expect_success 'test ident field is working' '
727 mkdir ../other_worktree &&
728 cp -R done dthree dtwo four three ../other_worktree &&
729 GIT_WORK_TREE=../other_worktree git status 2>../err &&
730 echo "warning: untracked cache is disabled on this system or location" >../expect &&
731 test_i18ncmp ../expect ../err
734 test_expect_success 'untracked cache survives a checkout' '
735 git commit --allow-empty -m empty &&
736 test-tool dump-untracked-cache >../before &&
737 test_when_finished "git checkout master" &&
738 git checkout -b other_branch &&
739 test-tool dump-untracked-cache >../after &&
740 test_cmp ../before ../after &&
742 test-tool dump-untracked-cache >../before &&
743 git checkout master &&
744 test-tool dump-untracked-cache >../after &&
745 test_cmp ../before ../after
748 test_expect_success 'untracked cache survives a commit' '
749 test-tool dump-untracked-cache >../before &&
751 git commit -m commit &&
752 test-tool dump-untracked-cache >../after &&
753 test_cmp ../before ../after
756 test_expect_success 'teardown worktree' '
760 test_expect_success SYMLINKS 'setup worktree for symlink test' '
761 git init worktree-symlink &&
762 cd worktree-symlink &&
763 git config core.untrackedCache true &&
765 touch one/file two/file &&
766 git add one/file two/file &&
767 git commit -m"first commit" &&
771 git commit -m"second commit"
774 test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=true' '
775 git checkout HEAD~ &&
778 git checkout master &&
784 test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=false' '
785 git config core.untrackedCache false &&
786 git checkout HEAD~ &&
789 git checkout master &&
795 test_expect_success 'setup worktree for non-symlink test' '
796 git init worktree-non-symlink &&
797 cd worktree-non-symlink &&
798 git config core.untrackedCache true &&
800 touch one/file two/file &&
801 git add one/file two/file &&
802 git commit -m"first commit" &&
806 git commit -m"second commit"
809 test_expect_success '"status" after file replacement should be clean with UC=true' '
810 git checkout HEAD~ &&
813 git checkout master &&
816 test-tool dump-untracked-cache >../actual &&
817 grep -F "recurse valid" ../actual >../actual.grep &&
818 cat >../expect.grep <<EOF &&
819 / $ZERO_OID recurse valid
820 /two/ $ZERO_OID recurse valid
823 test_cmp ../expect.grep ../actual.grep
826 test_expect_success '"status" after file replacement should be clean with UC=false' '
827 git config core.untrackedCache false &&
828 git checkout HEAD~ &&
831 git checkout master &&