Merge branch 'nd/test-helpers' into maint
[git] / t / t7063-status-untracked-cache.sh
1 #!/bin/sh
2
3 test_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
17 sync_mtime () {
18         find . -type d -ls >/dev/null
19 }
20
21 avoid_racy() {
22         sleep 1
23 }
24
25 test_lazy_prereq UNTRACKED_CACHE '
26         { git update-index --test-untracked-cache; ret=$?; } &&
27         test $ret -ne 1
28 '
29
30 if ! test_have_prereq UNTRACKED_CACHE; then
31         skip_all='This system does not support untracked cache'
32         test_done
33 fi
34
35 test_expect_success 'core.untrackedCache is unset' '
36         test_must_fail git config --get core.untrackedCache
37 '
38
39 test_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
49 test_expect_success 'untracked cache is empty' '
50         test-dump-untracked-cache >../actual &&
51         cat >../expect-empty <<EOF &&
52 info/exclude 0000000000000000000000000000000000000000
53 core.excludesfile 0000000000000000000000000000000000000000
54 exclude_per_dir .gitignore
55 flags 00000006
56 EOF
57         test_cmp ../expect-empty ../actual
58 '
59
60 cat >../status.expect <<EOF &&
61 A  done/one
62 A  one
63 A  two
64 ?? dthree/
65 ?? dtwo/
66 ?? three
67 EOF
68
69 cat >../dump.expect <<EOF &&
70 info/exclude $EMPTY_BLOB
71 core.excludesfile 0000000000000000000000000000000000000000
72 exclude_per_dir .gitignore
73 flags 00000006
74 / 0000000000000000000000000000000000000000 recurse valid
75 dthree/
76 dtwo/
77 three
78 /done/ 0000000000000000000000000000000000000000 recurse valid
79 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
80 three
81 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
82 two
83 EOF
84
85 test_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 &&
92 node creation: 3
93 gitignore invalidation: 1
94 directory invalidation: 0
95 opendir: 4
96 EOF
97         test_cmp ../trace.expect ../trace
98 '
99
100 test_expect_success 'untracked cache after first status' '
101         test-dump-untracked-cache >../actual &&
102         test_cmp ../dump.expect ../actual
103 '
104
105 test_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 &&
112 node creation: 0
113 gitignore invalidation: 0
114 directory invalidation: 0
115 opendir: 0
116 EOF
117         test_cmp ../trace.expect ../trace
118 '
119
120 test_expect_success 'untracked cache after second status' '
121         test-dump-untracked-cache >../actual &&
122         test_cmp ../dump.expect ../actual
123 '
124
125 test_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 &&
132 A  done/one
133 A  one
134 A  two
135 ?? dthree/
136 ?? dtwo/
137 ?? four
138 ?? three
139 EOF
140         test_cmp ../status.expect ../actual &&
141         cat >../trace.expect <<EOF &&
142 node creation: 0
143 gitignore invalidation: 0
144 directory invalidation: 1
145 opendir: 1
146 EOF
147         test_cmp ../trace.expect ../trace
148
149 '
150
151 test_expect_success 'verify untracked cache dump' '
152         test-dump-untracked-cache >../actual &&
153         cat >../expect <<EOF &&
154 info/exclude $EMPTY_BLOB
155 core.excludesfile 0000000000000000000000000000000000000000
156 exclude_per_dir .gitignore
157 flags 00000006
158 / 0000000000000000000000000000000000000000 recurse valid
159 dthree/
160 dtwo/
161 four
162 three
163 /done/ 0000000000000000000000000000000000000000 recurse valid
164 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
165 three
166 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
167 two
168 EOF
169         test_cmp ../expect ../actual
170 '
171
172 test_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 &&
179 A  done/one
180 A  one
181 A  two
182 ?? .gitignore
183 ?? dthree/
184 ?? dtwo/
185 ?? three
186 EOF
187         test_cmp ../status.expect ../actual &&
188         cat >../trace.expect <<EOF &&
189 node creation: 0
190 gitignore invalidation: 1
191 directory invalidation: 1
192 opendir: 4
193 EOF
194         test_cmp ../trace.expect ../trace
195
196 '
197
198 test_expect_success 'verify untracked cache dump' '
199         test-dump-untracked-cache >../actual &&
200         cat >../expect <<EOF &&
201 info/exclude $EMPTY_BLOB
202 core.excludesfile 0000000000000000000000000000000000000000
203 exclude_per_dir .gitignore
204 flags 00000006
205 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
206 .gitignore
207 dthree/
208 dtwo/
209 three
210 /done/ 0000000000000000000000000000000000000000 recurse valid
211 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
212 three
213 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
214 two
215 EOF
216         test_cmp ../expect ../actual
217 '
218
219 test_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 &&
226 A  done/one
227 A  one
228 A  two
229 ?? .gitignore
230 ?? dtwo/
231 EOF
232         test_cmp ../status.expect ../actual &&
233         cat >../trace.expect <<EOF &&
234 node creation: 0
235 gitignore invalidation: 1
236 directory invalidation: 0
237 opendir: 4
238 EOF
239         test_cmp ../trace.expect ../trace
240 '
241
242 test_expect_success 'verify untracked cache dump' '
243         test-dump-untracked-cache >../actual &&
244         cat >../expect <<EOF &&
245 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
246 core.excludesfile 0000000000000000000000000000000000000000
247 exclude_per_dir .gitignore
248 flags 00000006
249 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
250 .gitignore
251 dtwo/
252 /done/ 0000000000000000000000000000000000000000 recurse valid
253 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
254 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
255 two
256 EOF
257         test_cmp ../expect ../actual
258 '
259
260 test_expect_success 'move two from tracked to untracked' '
261         git rm --cached two &&
262         test-dump-untracked-cache >../actual &&
263         cat >../expect <<EOF &&
264 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
265 core.excludesfile 0000000000000000000000000000000000000000
266 exclude_per_dir .gitignore
267 flags 00000006
268 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
269 /done/ 0000000000000000000000000000000000000000 recurse valid
270 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
271 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
272 two
273 EOF
274         test_cmp ../expect ../actual
275 '
276
277 test_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 &&
282 A  done/one
283 A  one
284 ?? .gitignore
285 ?? dtwo/
286 ?? two
287 EOF
288         test_cmp ../status.expect ../actual &&
289         cat >../trace.expect <<EOF &&
290 node creation: 0
291 gitignore invalidation: 0
292 directory invalidation: 0
293 opendir: 1
294 EOF
295         test_cmp ../trace.expect ../trace
296 '
297
298 test_expect_success 'verify untracked cache dump' '
299         test-dump-untracked-cache >../actual &&
300         cat >../expect <<EOF &&
301 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
302 core.excludesfile 0000000000000000000000000000000000000000
303 exclude_per_dir .gitignore
304 flags 00000006
305 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
306 .gitignore
307 dtwo/
308 two
309 /done/ 0000000000000000000000000000000000000000 recurse valid
310 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
311 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
312 two
313 EOF
314         test_cmp ../expect ../actual
315 '
316
317 test_expect_success 'move two from untracked to tracked' '
318         git add two &&
319         test-dump-untracked-cache >../actual &&
320         cat >../expect <<EOF &&
321 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
322 core.excludesfile 0000000000000000000000000000000000000000
323 exclude_per_dir .gitignore
324 flags 00000006
325 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
326 /done/ 0000000000000000000000000000000000000000 recurse valid
327 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
328 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
329 two
330 EOF
331         test_cmp ../expect ../actual
332 '
333
334 test_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 &&
339 A  done/one
340 A  one
341 A  two
342 ?? .gitignore
343 ?? dtwo/
344 EOF
345         test_cmp ../status.expect ../actual &&
346         cat >../trace.expect <<EOF &&
347 node creation: 0
348 gitignore invalidation: 0
349 directory invalidation: 0
350 opendir: 1
351 EOF
352         test_cmp ../trace.expect ../trace
353 '
354
355 test_expect_success 'verify untracked cache dump' '
356         test-dump-untracked-cache >../actual &&
357         cat >../expect <<EOF &&
358 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
359 core.excludesfile 0000000000000000000000000000000000000000
360 exclude_per_dir .gitignore
361 flags 00000006
362 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
363 .gitignore
364 dtwo/
365 /done/ 0000000000000000000000000000000000000000 recurse valid
366 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
367 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
368 two
369 EOF
370         test_cmp ../expect ../actual
371 '
372
373 test_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
381 test_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/
388 EOF
389         test_cmp ../status.expect ../actual &&
390         cat >../trace.expect <<EOF &&
391 node creation: 0
392 gitignore invalidation: 0
393 directory invalidation: 0
394 opendir: 2
395 EOF
396         test_cmp ../trace.expect ../trace
397 '
398
399 test_expect_success 'untracked cache correct after commit' '
400         test-dump-untracked-cache >../actual &&
401         cat >../expect <<EOF &&
402 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
403 core.excludesfile 0000000000000000000000000000000000000000
404 exclude_per_dir .gitignore
405 flags 00000006
406 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
407 .gitignore
408 dtwo/
409 /done/ 0000000000000000000000000000000000000000 recurse valid
410 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
411 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
412 two
413 EOF
414         test_cmp ../expect ../actual
415 '
416
417 test_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
427 test_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
437 test_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/
447 EOF
448         test_cmp ../status.expect ../status.actual &&
449         cat >../trace.expect <<EOF &&
450 node creation: 0
451 gitignore invalidation: 1
452 directory invalidation: 2
453 opendir: 2
454 EOF
455         test_cmp ../trace.expect ../trace
456 '
457
458 test_expect_success 'untracked cache correct after status' '
459         test-dump-untracked-cache >../actual &&
460         cat >../expect <<EOF &&
461 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
462 core.excludesfile 0000000000000000000000000000000000000000
463 exclude_per_dir .gitignore
464 flags 00000006
465 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
466 .gitignore
467 dtwo/
468 /done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
469 five
470 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
471 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
472 two
473 EOF
474         test_cmp ../expect ../actual
475 '
476
477 test_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/
487 EOF
488         test_cmp ../status.expect ../status.actual &&
489         cat >../trace.expect <<EOF &&
490 node creation: 0
491 gitignore invalidation: 0
492 directory invalidation: 0
493 opendir: 0
494 EOF
495         test_cmp ../trace.expect ../trace
496 '
497
498 test_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
504 test_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/
515 EOF
516         test_cmp ../status.expect ../status.actual &&
517         cat >../trace.expect <<EOF &&
518 node creation: 2
519 gitignore invalidation: 0
520 directory invalidation: 1
521 opendir: 3
522 EOF
523         test_cmp ../trace.expect ../trace
524 '
525
526 test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
527         test-dump-untracked-cache >../actual &&
528         cat >../expect-from-test-dump <<EOF &&
529 info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
530 core.excludesfile 0000000000000000000000000000000000000000
531 exclude_per_dir .gitignore
532 flags 00000006
533 / e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
534 .gitignore
535 dtwo/
536 /done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
537 five
538 sub/
539 /done/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
540 sub/
541 /done/sub/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
542 file
543 /dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
544 /dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
545 two
546 EOF
547         test_cmp ../expect-from-test-dump ../actual
548 '
549
550 test_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 &&
557 node creation: 0
558 gitignore invalidation: 0
559 directory invalidation: 0
560 opendir: 0
561 EOF
562         test_cmp ../trace.expect ../trace
563 '
564
565 test_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
570 A  dtwo/two
571 ?? .gitignore
572 ?? done/five
573 ?? done/sub/
574 EOF
575         test_cmp ../status.expect ../status.actual
576 '
577
578 test_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/
587 EOF
588         test_cmp ../status.expect ../status.actual
589 '
590
591 test_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
598 test_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
604 test_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
613 test_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
622 test_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
631 test_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
637 test_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
656 test_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
664 test_done