1#!/bin/sh
2
3test_description='test untracked cache'
4
5. ./test-lib.sh
6
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.
13#
14# See <20160803174522.5571-1-pclouds@gmail.com> if you want to know
15# more.
16
17sync_mtime () {
18 find . -type d -ls >/dev/null
19}
20
21avoid_racy() {
22 sleep 1
23}
24
25test_lazy_prereq UNTRACKED_CACHE '
26 { git update-index --test-untracked-cache; ret=$?; } &&
27 test $ret -ne 1
28'
29
30if ! test_have_prereq UNTRACKED_CACHE; then
31 skip_all='This system does not support untracked cache'
32 test_done
33fi
34
35test_expect_success 'core.untrackedCache is unset' '
36 test_must_fail git config --get core.untrackedCache
37'
38
39test_expect_success 'setup' '
40 git init worktree &&
41 cd worktree &&
42 mkdir done dtwo dthree &&
43 touch one two three done/one dtwo/two dthree/three &&
44 git add one two done/one &&
45 : >.git/info/exclude &&
46 git update-index --untracked-cache
47'
48
49test_expect_success 'untracked cache is empty' '
50 test-dump-untracked-cache >../actual &&
51 cat >../expect-empty <<EOF &&
52info/exclude 0000000000000000000000000000000000000000
53core.excludesfile 0000000000000000000000000000000000000000
54exclude_per_dir .gitignore
55flags 00000006
56EOF
57 test_cmp ../expect-empty ../actual
58'
59
60cat >../status.expect <<EOF &&
61A done/one
62A one
63A two
64?? dthree/
65?? dtwo/
66?? three
67EOF
68
69cat >../dump.expect <<EOF &&
70info/exclude $EMPTY_BLOB
71core.excludesfile 0000000000000000000000000000000000000000
72exclude_per_dir .gitignore
73flags 00000006
74/ 0000000000000000000000000000000000000000 recurse valid
75dthree/
76dtwo/
77three
78/done/ 0000000000000000000000000000000000000000 recurse valid
79/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
80three
81/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
82two
83EOF
84
85test_expect_success 'status first time (empty cache)' '
86 avoid_racy &&
87 : >../trace &&
88 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
89 git status --porcelain >../actual &&
90 test_cmp ../status.expect ../actual &&
91 cat >../trace.expect <<EOF &&
92node creation: 3
93gitignore invalidation: 1
94directory invalidation: 0
95opendir: 4
96EOF
97 test_cmp ../trace.expect ../trace
98'
99
100test_expect_success 'untracked cache after first status' '
101 test-dump-untracked-cache >../actual &&
102 test_cmp ../dump.expect ../actual
103'
104
105test_expect_success 'status second time (fully populated cache)' '
106 avoid_racy &&
107 : >../trace &&
108 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
109 git status --porcelain >../actual &&
110 test_cmp ../status.expect ../actual &&
111 cat >../trace.expect <<EOF &&
112node creation: 0
113gitignore invalidation: 0
114directory invalidation: 0
115opendir: 0
116EOF
117 test_cmp ../trace.expect ../trace
118'
119
120test_expect_success 'untracked cache after second status' '
121 test-dump-untracked-cache >../actual &&
122 test_cmp ../dump.expect ../actual
123'
124
125test_expect_success 'modify in root directory, one dir invalidation' '
126 avoid_racy &&
127 : >four &&
128 : >../trace &&
129 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
130 git status --porcelain >../actual &&
131 cat >../status.expect <<EOF &&
132A done/one
133A one
134A two
135?? dthree/
136?? dtwo/
137?? four
138?? three
139EOF
140 test_cmp ../status.expect ../actual &&
141 cat >../trace.expect <<EOF &&
142node creation: 0
143gitignore invalidation: 0
144directory invalidation: 1
145opendir: 1
146EOF
147 test_cmp ../trace.expect ../trace
148
149'
150
151test_expect_success 'verify untracked cache dump' '
152 test-dump-untracked-cache >../actual &&
153 cat >../expect <<EOF &&
154info/exclude $EMPTY_BLOB
155core.excludesfile 0000000000000000000000000000000000000000
156exclude_per_dir .gitignore
157flags 00000006
158/ 0000000000000000000000000000000000000000 recurse valid
159dthree/
160dtwo/
161four
162three
163/done/ 0000000000000000000000000000000000000000 recurse valid
164/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
165three
166/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
167two
168EOF
169 test_cmp ../expect ../actual
170'
171
172test_expect_success 'new .gitignore invalidates recursively' '
173 avoid_racy &&
174 echo four >.gitignore &&
175 : >../trace &&
176 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
177 git status --porcelain >../actual &&
178 cat >../status.expect <<EOF &&
179A done/one
180A one
181A two
182?? .gitignore
183?? dthree/
184?? dtwo/
185?? three
186EOF
187 test_cmp ../status.expect ../actual &&
188 cat >../trace.expect <<EOF &&
189node creation: 0
190gitignore invalidation: 1
191directory invalidation: 1
192opendir: 4
193EOF
194 test_cmp ../trace.expect ../trace
195
196'
197
198test_expect_success 'verify untracked cache dump' '
199 test-dump-untracked-cache >../actual &&
200 cat >../expect <<EOF &&
201info/exclude $EMPTY_BLOB
202core.excludesfile 0000000000000000000000000000000000000000
203exclude_per_dir .gitignore
204flags 00000006
205/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
206.gitignore
207dthree/
208dtwo/
209three
210/done/ 0000000000000000000000000000000000000000 recurse valid
211/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
212three
213/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
214two
215EOF
216 test_cmp ../expect ../actual
217'
218
219test_expect_success 'new info/exclude invalidates everything' '
220 avoid_racy &&
221 echo three >>.git/info/exclude &&
222 : >../trace &&
223 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
224 git status --porcelain >../actual &&
225 cat >../status.expect <<EOF &&
226A done/one
227A one
228A two
229?? .gitignore
230?? dtwo/
231EOF
232 test_cmp ../status.expect ../actual &&
233 cat >../trace.expect <<EOF &&
234node creation: 0
235gitignore invalidation: 1
236directory invalidation: 0
237opendir: 4
238EOF
239 test_cmp ../trace.expect ../trace
240'
241
242test_expect_success 'verify untracked cache dump' '
243 test-dump-untracked-cache >../actual &&
244 cat >../expect <<EOF &&
245info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
246core.excludesfile 0000000000000000000000000000000000000000
247exclude_per_dir .gitignore
248flags 00000006
249/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
250.gitignore
251dtwo/
252/done/ 0000000000000000000000000000000000000000 recurse valid
253/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
254/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
255two
256EOF
257 test_cmp ../expect ../actual
258'
259
260test_expect_success 'move two from tracked to untracked' '
261 git rm --cached two &&
262 test-dump-untracked-cache >../actual &&
263 cat >../expect <<EOF &&
264info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
265core.excludesfile 0000000000000000000000000000000000000000
266exclude_per_dir .gitignore
267flags 00000006
268/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
269/done/ 0000000000000000000000000000000000000000 recurse valid
270/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
271/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
272two
273EOF
274 test_cmp ../expect ../actual
275'
276
277test_expect_success 'status after the move' '
278 : >../trace &&
279 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
280 git status --porcelain >../actual &&
281 cat >../status.expect <<EOF &&
282A done/one
283A one
284?? .gitignore
285?? dtwo/
286?? two
287EOF
288 test_cmp ../status.expect ../actual &&
289 cat >../trace.expect <<EOF &&
290node creation: 0
291gitignore invalidation: 0
292directory invalidation: 0
293opendir: 1
294EOF
295 test_cmp ../trace.expect ../trace
296'
297
298test_expect_success 'verify untracked cache dump' '
299 test-dump-untracked-cache >../actual &&
300 cat >../expect <<EOF &&
301info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
302core.excludesfile 0000000000000000000000000000000000000000
303exclude_per_dir .gitignore
304flags 00000006
305/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
306.gitignore
307dtwo/
308two
309/done/ 0000000000000000000000000000000000000000 recurse valid
310/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
311/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
312two
313EOF
314 test_cmp ../expect ../actual
315'
316
317test_expect_success 'move two from untracked to tracked' '
318 git add two &&
319 test-dump-untracked-cache >../actual &&
320 cat >../expect <<EOF &&
321info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
322core.excludesfile 0000000000000000000000000000000000000000
323exclude_per_dir .gitignore
324flags 00000006
325/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
326/done/ 0000000000000000000000000000000000000000 recurse valid
327/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
328/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
329two
330EOF
331 test_cmp ../expect ../actual
332'
333
334test_expect_success 'status after the move' '
335 : >../trace &&
336 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
337 git status --porcelain >../actual &&
338 cat >../status.expect <<EOF &&
339A done/one
340A one
341A two
342?? .gitignore
343?? dtwo/
344EOF
345 test_cmp ../status.expect ../actual &&
346 cat >../trace.expect <<EOF &&
347node creation: 0
348gitignore invalidation: 0
349directory invalidation: 0
350opendir: 1
351EOF
352 test_cmp ../trace.expect ../trace
353'
354
355test_expect_success 'verify untracked cache dump' '
356 test-dump-untracked-cache >../actual &&
357 cat >../expect <<EOF &&
358info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
359core.excludesfile 0000000000000000000000000000000000000000
360exclude_per_dir .gitignore
361flags 00000006
362/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
363.gitignore
364dtwo/
365/done/ 0000000000000000000000000000000000000000 recurse valid
366/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
367/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
368two
369EOF
370 test_cmp ../expect ../actual
371'
372
373test_expect_success 'set up for sparse checkout testing' '
374 echo two >done/.gitignore &&
375 echo three >>done/.gitignore &&
376 echo two >done/two &&
377 git add -f done/two done/.gitignore &&
378 git commit -m "first commit"
379'
380
381test_expect_success 'status after commit' '
382 : >../trace &&
383 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
384 git status --porcelain >../actual &&
385 cat >../status.expect <<EOF &&
386?? .gitignore
387?? dtwo/
388EOF
389 test_cmp ../status.expect ../actual &&
390 cat >../trace.expect <<EOF &&
391node creation: 0
392gitignore invalidation: 0
393directory invalidation: 0
394opendir: 2
395EOF
396 test_cmp ../trace.expect ../trace
397'
398
399test_expect_success 'untracked cache correct after commit' '
400 test-dump-untracked-cache >../actual &&
401 cat >../expect <<EOF &&
402info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
403core.excludesfile 0000000000000000000000000000000000000000
404exclude_per_dir .gitignore
405flags 00000006
406/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
407.gitignore
408dtwo/
409/done/ 0000000000000000000000000000000000000000 recurse valid
410/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
411/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
412two
413EOF
414 test_cmp ../expect ../actual
415'
416
417test_expect_success 'set up sparse checkout' '
418 echo "done/[a-z]*" >.git/info/sparse-checkout &&
419 test_config core.sparsecheckout true &&
420 git checkout master &&
421 git update-index --force-untracked-cache &&
422 git status --porcelain >/dev/null && # prime the cache
423 test_path_is_missing done/.gitignore &&
424 test_path_is_file done/one
425'
426
427test_expect_success 'create/modify files, some of which are gitignored' '
428 echo two bis >done/two &&
429 echo three >done/three && # three is gitignored
430 echo four >done/four && # four is gitignored at a higher level
431 echo five >done/five && # five is not gitignored
432 echo test >base && #we need to ensure that the root dir is touched
433 rm base &&
434 sync_mtime
435'
436
437test_expect_success 'test sparse status with untracked cache' '
438 : >../trace &&
439 avoid_racy &&
440 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
441 git status --porcelain >../status.actual &&
442 cat >../status.expect <<EOF &&
443 M done/two
444?? .gitignore
445?? done/five
446?? dtwo/
447EOF
448 test_cmp ../status.expect ../status.actual &&
449 cat >../trace.expect <<EOF &&
450node creation: 0
451gitignore invalidation: 1
452directory invalidation: 2
453opendir: 2
454EOF
455 test_cmp ../trace.expect ../trace
456'
457
458test_expect_success 'untracked cache correct after status' '
459 test-dump-untracked-cache >../actual &&
460 cat >../expect <<EOF &&
461info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
462core.excludesfile 0000000000000000000000000000000000000000
463exclude_per_dir .gitignore
464flags 00000006
465/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
466.gitignore
467dtwo/
468/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
469five
470/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
471/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
472two
473EOF
474 test_cmp ../expect ../actual
475'
476
477test_expect_success 'test sparse status again with untracked cache' '
478 avoid_racy &&
479 : >../trace &&
480 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
481 git status --porcelain >../status.actual &&
482 cat >../status.expect <<EOF &&
483 M done/two
484?? .gitignore
485?? done/five
486?? dtwo/
487EOF
488 test_cmp ../status.expect ../status.actual &&
489 cat >../trace.expect <<EOF &&
490node creation: 0
491gitignore invalidation: 0
492directory invalidation: 0
493opendir: 0
494EOF
495 test_cmp ../trace.expect ../trace
496'
497
498test_expect_success 'set up for test of subdir and sparse checkouts' '
499 mkdir done/sub &&
500 mkdir done/sub/sub &&
501 echo "sub" > done/sub/sub/file
502'
503
504test_expect_success 'test sparse status with untracked cache and subdir' '
505 avoid_racy &&
506 : >../trace &&
507 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
508 git status --porcelain >../status.actual &&
509 cat >../status.expect <<EOF &&
510 M done/two
511?? .gitignore
512?? done/five
513?? done/sub/
514?? dtwo/
515EOF
516 test_cmp ../status.expect ../status.actual &&
517 cat >../trace.expect <<EOF &&
518node creation: 2
519gitignore invalidation: 0
520directory invalidation: 1
521opendir: 3
522EOF
523 test_cmp ../trace.expect ../trace
524'
525
526test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
527 test-dump-untracked-cache >../actual &&
528 cat >../expect-from-test-dump <<EOF &&
529info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
530core.excludesfile 0000000000000000000000000000000000000000
531exclude_per_dir .gitignore
532flags 00000006
533/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
534.gitignore
535dtwo/
536/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
537five
538sub/
539/done/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
540sub/
541/done/sub/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
542file
543/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
544/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
545two
546EOF
547 test_cmp ../expect-from-test-dump ../actual
548'
549
550test_expect_success 'test sparse status again with untracked cache and subdir' '
551 avoid_racy &&
552 : >../trace &&
553 GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
554 git status --porcelain >../status.actual &&
555 test_cmp ../status.expect ../status.actual &&
556 cat >../trace.expect <<EOF &&
557node creation: 0
558gitignore invalidation: 0
559directory invalidation: 0
560opendir: 0
561EOF
562 test_cmp ../trace.expect ../trace
563'
564
565test_expect_success 'move entry in subdir from untracked to cached' '
566 git add dtwo/two &&
567 git status --porcelain >../status.actual &&
568 cat >../status.expect <<EOF &&
569 M done/two
570A dtwo/two
571?? .gitignore
572?? done/five
573?? done/sub/
574EOF
575 test_cmp ../status.expect ../status.actual
576'
577
578test_expect_success 'move entry in subdir from cached to untracked' '
579 git rm --cached dtwo/two &&
580 git status --porcelain >../status.actual &&
581 cat >../status.expect <<EOF &&
582 M done/two
583?? .gitignore
584?? done/five
585?? done/sub/
586?? dtwo/
587EOF
588 test_cmp ../status.expect ../status.actual
589'
590
591test_expect_success '--no-untracked-cache removes the cache' '
592 git update-index --no-untracked-cache &&
593 test-dump-untracked-cache >../actual &&
594 echo "no untracked cache" >../expect-no-uc &&
595 test_cmp ../expect-no-uc ../actual
596'
597
598test_expect_success 'git status does not change anything' '
599 git status &&
600 test-dump-untracked-cache >../actual &&
601 test_cmp ../expect-no-uc ../actual
602'
603
604test_expect_success 'setting core.untrackedCache to true and using git status creates the cache' '
605 git config core.untrackedCache true &&
606 test-dump-untracked-cache >../actual &&
607 test_cmp ../expect-no-uc ../actual &&
608 git status &&
609 test-dump-untracked-cache >../actual &&
610 test_cmp ../expect-from-test-dump ../actual
611'
612
613test_expect_success 'using --no-untracked-cache does not fail when core.untrackedCache is true' '
614 git update-index --no-untracked-cache &&
615 test-dump-untracked-cache >../actual &&
616 test_cmp ../expect-no-uc ../actual &&
617 git update-index --untracked-cache &&
618 test-dump-untracked-cache >../actual &&
619 test_cmp ../expect-empty ../actual
620'
621
622test_expect_success 'setting core.untrackedCache to false and using git status removes the cache' '
623 git config core.untrackedCache false &&
624 test-dump-untracked-cache >../actual &&
625 test_cmp ../expect-empty ../actual &&
626 git status &&
627 test-dump-untracked-cache >../actual &&
628 test_cmp ../expect-no-uc ../actual
629'
630
631test_expect_success 'using --untracked-cache does not fail when core.untrackedCache is false' '
632 git update-index --untracked-cache &&
633 test-dump-untracked-cache >../actual &&
634 test_cmp ../expect-empty ../actual
635'
636
637test_expect_success 'setting core.untrackedCache to keep' '
638 git config core.untrackedCache keep &&
639 git update-index --untracked-cache &&
640 test-dump-untracked-cache >../actual &&
641 test_cmp ../expect-empty ../actual &&
642 git status &&
643 test-dump-untracked-cache >../actual &&
644 test_cmp ../expect-from-test-dump ../actual &&
645 git update-index --no-untracked-cache &&
646 test-dump-untracked-cache >../actual &&
647 test_cmp ../expect-no-uc ../actual &&
648 git update-index --force-untracked-cache &&
649 test-dump-untracked-cache >../actual &&
650 test_cmp ../expect-empty ../actual &&
651 git status &&
652 test-dump-untracked-cache >../actual &&
653 test_cmp ../expect-from-test-dump ../actual
654'
655
656test_expect_success 'test ident field is working' '
657 mkdir ../other_worktree &&
658 cp -R done dthree dtwo four three ../other_worktree &&
659 GIT_WORK_TREE=../other_worktree git status 2>../err &&
660 echo "warning: Untracked cache is disabled on this system or location." >../expect &&
661 test_i18ncmp ../expect ../err
662'
663
664test_expect_success 'untracked cache survives a checkout' '
665 git commit --allow-empty -m empty &&
666 test-dump-untracked-cache >../before &&
667 test_when_finished "git checkout master" &&
668 git checkout -b other_branch &&
669 test-dump-untracked-cache >../after &&
670 test_cmp ../before ../after &&
671 test_commit test &&
672 test-dump-untracked-cache >../before &&
673 git checkout master &&
674 test-dump-untracked-cache >../after &&
675 test_cmp ../before ../after
676'
677
678test_expect_success 'untracked cache survives a commit' '
679 test-dump-untracked-cache >../before &&
680 git add done/two &&
681 git commit -m commit &&
682 test-dump-untracked-cache >../after &&
683 test_cmp ../before ../after
684'
685
686test_done