Raw
1 #!/bin/sh
2
3 test_description='built-in file system watcher'
4
5 . ./test-lib.sh
6
7 if ! test_have_prereq FSMONITOR_DAEMON
8 then
9 skip_all="fsmonitor--daemon is not supported on this platform"
10 test_done
11 fi
12
13 # Verify that the filesystem delivers events to the daemon.
14 # On some configurations (e.g., overlayfs with older kernels),
15 # inotify watches succeed but events are never delivered. The
16 # cookie wait will time out and the daemon logs a trace message.
17 #
18 # Use "timeout" (if available) to guard each step against hangs.
19 maybe_timeout () {
20 if type timeout >/dev/null 2>&1
21 then
22 timeout "$@"
23 else
24 shift
25 "$@"
26 fi
27 }
28
29 test_lazy_prereq FSMONITOR_WORKS '
30 git init test_fsmonitor_smoke || return 1
31
32 GIT_TRACE_FSMONITOR="$PWD/smoke.trace" &&
33 export GIT_TRACE_FSMONITOR &&
34 maybe_timeout 30 \
35 git -C test_fsmonitor_smoke fsmonitor--daemon start \
36 --start-timeout=10
37 ret=$?
38 unset GIT_TRACE_FSMONITOR
39 if test $ret -ne 0
40 then
41 rm -rf test_fsmonitor_smoke smoke.trace
42 return 1
43 fi
44
45 maybe_timeout 10 \
46 test-tool -C test_fsmonitor_smoke fsmonitor-client query \
47 --token 0 >/dev/null 2>&1
48 maybe_timeout 5 \
49 git -C test_fsmonitor_smoke fsmonitor--daemon stop 2>/dev/null
50 ! grep -q "cookie_wait timed out" "$PWD/smoke.trace" 2>/dev/null
51 ret=$?
52 rm -rf test_fsmonitor_smoke smoke.trace
53 return $ret
54 '
55
56 if ! test_have_prereq FSMONITOR_WORKS
57 then
58 skip_all="filesystem does not deliver fsmonitor events (container/overlayfs?)"
59 test_done
60 fi
61
62 stop_daemon_delete_repo () {
63 r=$1 &&
64 { maybe_timeout 30 git -C $r fsmonitor--daemon stop 2>/dev/null || :; } &&
65 rm -rf $1
66 }
67
68 start_daemon () {
69 r= tf= t2= tk= &&
70
71 while test "$#" -ne 0
72 do
73 case "$1" in
74 -C)
75 r="-C ${2?}"
76 shift
77 ;;
78 --tf)
79 tf="${2?}"
80 shift
81 ;;
82 --t2)
83 t2="${2?}"
84 shift
85 ;;
86 --tk)
87 tk="${2?}"
88 shift
89 ;;
90 -*)
91 BUG "error: unknown option: '$1'"
92 ;;
93 *)
94 BUG "error: unbound argument: '$1'"
95 ;;
96 esac
97 shift
98 done &&
99
100 (
101 if test -n "$tf"
102 then
103 GIT_TRACE_FSMONITOR="$tf"
104 export GIT_TRACE_FSMONITOR
105 fi &&
106
107 if test -n "$t2"
108 then
109 GIT_TRACE2_PERF="$t2"
110 export GIT_TRACE2_PERF
111 fi &&
112
113 if test -n "$tk"
114 then
115 GIT_TEST_FSMONITOR_TOKEN="$tk"
116 export GIT_TEST_FSMONITOR_TOKEN
117 fi &&
118
119 git $r fsmonitor--daemon start --start-timeout=10 &&
120 git $r fsmonitor--daemon status
121 )
122 }
123
124 # Is a Trace2 data event present with the given catetory and key?
125 # We do not care what the value is.
126 #
127 have_t2_data_event () {
128 c=$1 &&
129 k=$2 &&
130
131 grep -e '"event":"data".*"category":"'"$c"'".*"key":"'"$k"'"'
132 }
133
134 test_expect_success 'explicit daemon start and stop' '
135 test_when_finished "stop_daemon_delete_repo test_explicit" &&
136
137 git init test_explicit &&
138 start_daemon -C test_explicit &&
139
140 git -C test_explicit fsmonitor--daemon stop &&
141 test_must_fail git -C test_explicit fsmonitor--daemon status
142 '
143
144 test_expect_success 'implicit daemon start' '
145 test_when_finished "stop_daemon_delete_repo test_implicit" &&
146
147 git init test_implicit &&
148 test_must_fail git -C test_implicit fsmonitor--daemon status &&
149
150 # query will implicitly start the daemon.
151 #
152 # for test-script simplicity, we send a V1 timestamp rather than
153 # a V2 token. either way, the daemon response to any query contains
154 # a new V2 token. (the daemon may complain that we sent a V1 request,
155 # but this test case is only concerned with whether the daemon was
156 # implicitly started.)
157
158 GIT_TRACE2_EVENT="$PWD/.git/trace" \
159 test-tool -C test_implicit fsmonitor-client query --token 0 >actual &&
160 nul_to_q <actual >actual.filtered &&
161 grep "builtin:" actual.filtered &&
162
163 # confirm that a daemon was started in the background.
164 #
165 # since the mechanism for starting the background daemon is platform
166 # dependent, just confirm that the foreground command received a
167 # response from the daemon.
168
169 have_t2_data_event fsm_client query/response-length <.git/trace &&
170
171 git -C test_implicit fsmonitor--daemon status &&
172 git -C test_implicit fsmonitor--daemon stop &&
173 test_must_fail git -C test_implicit fsmonitor--daemon status
174 '
175
176 # Verify that the daemon has shutdown. Spin a few seconds to
177 # make the test a little more robust during CI testing.
178 #
179 # We're looking for an implicit shutdown, such as when we delete or
180 # rename the ".git" directory. Our delete/rename will cause a file
181 # system event that the daemon will see and the daemon will
182 # auto-shutdown as soon as it sees it. But this is racy with our `git
183 # fsmonitor--daemon status` commands (and we cannot use a cookie file
184 # here to help us). So spin a little and give the daemon a chance to
185 # see the event. (This is primarily for underpowered CI build/test
186 # machines (where it might take a moment to wake and reschedule the
187 # daemon process) to avoid false alarms during test runs.)
188 #
189 IMPLICIT_TIMEOUT=5
190
191 verify_implicit_shutdown () {
192 r=$1 &&
193
194 k=0 &&
195 while test "$k" -lt $IMPLICIT_TIMEOUT
196 do
197 git -C $r fsmonitor--daemon status || return 0
198
199 sleep 1
200 k=$(( $k + 1 ))
201 done &&
202
203 return 1
204 }
205
206 test_expect_success 'implicit daemon stop (delete .git)' '
207 test_when_finished "stop_daemon_delete_repo test_implicit_1" &&
208
209 git init test_implicit_1 &&
210
211 start_daemon -C test_implicit_1 &&
212
213 # deleting the .git directory will implicitly stop the daemon.
214 rm -rf test_implicit_1/.git &&
215
216 # [1] Create an empty .git directory so that the following Git
217 # command will stay relative to the `-C` directory.
218 #
219 # Without this, the Git command will override the requested
220 # -C argument and crawl out to the containing Git source tree.
221 # This would make the test result dependent upon whether we
222 # were using fsmonitor on our development worktree.
223 #
224 mkdir test_implicit_1/.git &&
225
226 verify_implicit_shutdown test_implicit_1
227 '
228
229 test_expect_success 'implicit daemon stop (rename .git)' '
230 test_when_finished "stop_daemon_delete_repo test_implicit_2" &&
231
232 git init test_implicit_2 &&
233
234 start_daemon -C test_implicit_2 &&
235
236 # renaming the .git directory will implicitly stop the daemon.
237 mv test_implicit_2/.git test_implicit_2/.xxx &&
238
239 # See [1] above.
240 #
241 mkdir test_implicit_2/.git &&
242
243 verify_implicit_shutdown test_implicit_2
244 '
245
246 # File systems on Windows may or may not have shortnames.
247 # This is a volume-specific setting on modern systems.
248 # "C:/" drives are required to have them enabled. Other
249 # hard drives default to disabled.
250 #
251 # This is a crude test to see if shortnames are enabled
252 # on the volume containing the test directory. It is
253 # crude, but it does not require elevation like `fsutil`.
254 #
255 test_lazy_prereq SHORTNAMES '
256 mkdir .foo &&
257 test -d "FOO~1"
258 '
259
260 # Here we assume that the shortname of ".git" is "GIT~1".
261 test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~1)' '
262 test_when_finished "stop_daemon_delete_repo test_implicit_1s" &&
263
264 git init test_implicit_1s &&
265
266 start_daemon -C test_implicit_1s &&
267
268 # renaming the .git directory will implicitly stop the daemon.
269 # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}.
270 # the rename-from FS Event will contain the shortname.
271 #
272 mv test_implicit_1s/GIT~1 test_implicit_1s/.gitxyz &&
273
274 # See [1] above.
275 # this moves {.gitxyz, GITXYZ~1} to {.git, GIT~1}.
276 mv test_implicit_1s/.gitxyz test_implicit_1s/.git &&
277
278 verify_implicit_shutdown test_implicit_1s
279 '
280
281 # Here we first create a file with LONGNAME of "GIT~1" before
282 # we create the repo. This will cause the shortname of ".git"
283 # to be "GIT~2".
284 test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' '
285 test_when_finished "stop_daemon_delete_repo test_implicit_1s2" &&
286
287 mkdir test_implicit_1s2 &&
288 echo HELLO >test_implicit_1s2/GIT~1 &&
289 git init test_implicit_1s2 &&
290
291 test_path_is_file test_implicit_1s2/GIT~1 &&
292 test_path_is_dir test_implicit_1s2/GIT~2 &&
293
294 start_daemon -C test_implicit_1s2 &&
295
296 # renaming the .git directory will implicitly stop the daemon.
297 # the rename-from FS Event will contain the shortname.
298 #
299 mv test_implicit_1s2/GIT~2 test_implicit_1s2/.gitxyz &&
300
301 # See [1] above.
302 mv test_implicit_1s2/.gitxyz test_implicit_1s2/.git &&
303
304 verify_implicit_shutdown test_implicit_1s2
305 '
306
307 test_expect_success 'cannot start multiple daemons' '
308 test_when_finished "stop_daemon_delete_repo test_multiple" &&
309
310 git init test_multiple &&
311
312 start_daemon -C test_multiple &&
313
314 test_must_fail git -C test_multiple fsmonitor--daemon start 2>actual &&
315 grep "fsmonitor--daemon is already running" actual &&
316
317 git -C test_multiple fsmonitor--daemon stop &&
318 test_must_fail git -C test_multiple fsmonitor--daemon status
319 '
320
321 # These tests use the main repo in the trash directory
322
323 test_expect_success 'setup' '
324 >tracked &&
325 >modified &&
326 >delete &&
327 >rename &&
328 mkdir dir1 &&
329 >dir1/tracked &&
330 >dir1/modified &&
331 >dir1/delete &&
332 >dir1/rename &&
333 mkdir dir2 &&
334 >dir2/tracked &&
335 >dir2/modified &&
336 >dir2/delete &&
337 >dir2/rename &&
338 mkdir dirtorename &&
339 >dirtorename/a &&
340 >dirtorename/b &&
341
342 cat >.gitignore <<-\EOF &&
343 .gitignore
344 expect*
345 actual*
346 flush*
347 trace*
348 EOF
349
350 mkdir -p T1/T2/T3/T4 &&
351 echo 1 >T1/F1 &&
352 echo 1 >T1/T2/F1 &&
353 echo 1 >T1/T2/T3/F1 &&
354 echo 1 >T1/T2/T3/T4/F1 &&
355 echo 2 >T1/F2 &&
356 echo 2 >T1/T2/F2 &&
357 echo 2 >T1/T2/T3/F2 &&
358 echo 2 >T1/T2/T3/T4/F2 &&
359
360 git -c core.fsmonitor=false add . &&
361 test_tick &&
362 git -c core.fsmonitor=false commit -m initial &&
363
364 git config core.fsmonitor true
365 '
366
367 # The test already explicitly stopped (or tried to stop) the daemon.
368 # This is here in case something else fails first.
369 #
370 redundant_stop_daemon () {
371 test_might_fail git fsmonitor--daemon stop
372 }
373
374 test_expect_success 'update-index implicitly starts daemon' '
375 test_when_finished redundant_stop_daemon &&
376
377 test_must_fail git fsmonitor--daemon status &&
378
379 GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_1" \
380 git update-index --fsmonitor &&
381
382 git fsmonitor--daemon status &&
383 test_might_fail git fsmonitor--daemon stop &&
384
385 # Confirm that the trace2 log contains a record of the
386 # daemon starting.
387 test_subcommand git fsmonitor--daemon start <.git/trace_implicit_1
388 '
389
390 test_expect_success 'status implicitly starts daemon' '
391 test_when_finished redundant_stop_daemon &&
392
393 test_must_fail git fsmonitor--daemon status &&
394
395 GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_2" \
396 git status >actual &&
397
398 git fsmonitor--daemon status &&
399 test_might_fail git fsmonitor--daemon stop &&
400
401 # Confirm that the trace2 log contains a record of the
402 # daemon starting.
403 test_subcommand git fsmonitor--daemon start <.git/trace_implicit_2
404 '
405
406 edit_files () {
407 echo 1 >modified &&
408 echo 2 >dir1/modified &&
409 echo 3 >dir2/modified &&
410 >dir1/untracked
411 }
412
413 delete_files () {
414 rm -f delete &&
415 rm -f dir1/delete &&
416 rm -f dir2/delete
417 }
418
419 create_files () {
420 echo 1 >new &&
421 echo 2 >dir1/new &&
422 echo 3 >dir2/new
423 }
424
425 rename_files () {
426 mv rename renamed &&
427 mv dir1/rename dir1/renamed &&
428 mv dir2/rename dir2/renamed
429 }
430
431 file_to_directory () {
432 rm -f delete &&
433 mkdir delete &&
434 echo 1 >delete/new
435 }
436
437 directory_to_file () {
438 rm -rf dir1 &&
439 echo 1 >dir1
440 }
441
442 move_directory_contents_deeper() {
443 mkdir T1/_new_ &&
444 mv T1/[A-Z]* T1/_new_
445 }
446
447 move_directory_up() {
448 mv T1/T2/T3 T1
449 }
450
451 move_directory() {
452 mv T1/T2/T3 T1/T2/NewT3
453 }
454
455 # The next few test cases confirm that our fsmonitor daemon sees each type
456 # of OS filesystem notification that we care about. At this layer we just
457 # ensure we are getting the OS notifications and do not try to confirm what
458 # is reported by `git status`.
459 #
460 # We use retry_grep to handle races between the daemon writing events
461 # to the trace file and our check.
462 #
463 # We `reset` and `clean` at the bottom of each test (and before stopping the
464 # daemon) because these commands might implicitly restart the daemon.
465
466 clean_up_repo_and_stop_daemon () {
467 git reset --hard HEAD &&
468 git clean -fd &&
469 test_might_fail git fsmonitor--daemon stop &&
470 rm -f .git/trace
471 }
472
473 # Retry a grep up to RETRY_TIMEOUT times until it succeeds.
474 #
475 RETRY_TIMEOUT=5
476
477 retry_grep () {
478 nr_tries_left=$RETRY_TIMEOUT
479 until grep "$1" "$2" 2>/dev/null
480 do
481 if test $nr_tries_left -eq 0
482 then
483 grep "$1" "$2"
484 return
485 fi
486 nr_tries_left=$(($nr_tries_left - 1))
487 sleep 1
488 done
489 }
490
491 test_expect_success 'edit some files' '
492 test_when_finished clean_up_repo_and_stop_daemon &&
493
494 start_daemon --tf "$PWD/.git/trace" &&
495
496 edit_files &&
497
498 retry_grep "^event: dir1/modified$" .git/trace &&
499 retry_grep "^event: dir2/modified$" .git/trace &&
500 retry_grep "^event: modified$" .git/trace &&
501 retry_grep "^event: dir1/untracked$" .git/trace
502 '
503
504 test_expect_success 'create some files' '
505 test_when_finished clean_up_repo_and_stop_daemon &&
506
507 start_daemon --tf "$PWD/.git/trace" &&
508
509 create_files &&
510
511 retry_grep "^event: dir1/new$" .git/trace &&
512 retry_grep "^event: dir2/new$" .git/trace &&
513 retry_grep "^event: new$" .git/trace
514 '
515
516 test_expect_success 'delete some files' '
517 test_when_finished clean_up_repo_and_stop_daemon &&
518
519 start_daemon --tf "$PWD/.git/trace" &&
520
521 delete_files &&
522
523 retry_grep "^event: dir1/delete$" .git/trace &&
524 retry_grep "^event: dir2/delete$" .git/trace &&
525 retry_grep "^event: delete$" .git/trace
526 '
527
528 test_expect_success 'rename some files' '
529 test_when_finished clean_up_repo_and_stop_daemon &&
530
531 start_daemon --tf "$PWD/.git/trace" &&
532
533 rename_files &&
534
535 retry_grep "^event: dir1/rename$" .git/trace &&
536 retry_grep "^event: dir2/rename$" .git/trace &&
537 retry_grep "^event: rename$" .git/trace &&
538 retry_grep "^event: dir1/renamed$" .git/trace &&
539 retry_grep "^event: dir2/renamed$" .git/trace &&
540 retry_grep "^event: renamed$" .git/trace
541 '
542
543 test_expect_success 'rename directory' '
544 test_when_finished clean_up_repo_and_stop_daemon &&
545
546 start_daemon --tf "$PWD/.git/trace" &&
547
548 mv dirtorename dirrenamed &&
549
550 retry_grep "^event: dirtorename/*$" .git/trace &&
551 retry_grep "^event: dirrenamed/*$" .git/trace
552 '
553
554 test_expect_success 'file changes to directory' '
555 test_when_finished clean_up_repo_and_stop_daemon &&
556
557 start_daemon --tf "$PWD/.git/trace" &&
558
559 file_to_directory &&
560
561 retry_grep "^event: delete$" .git/trace &&
562 retry_grep "^event: delete/new$" .git/trace
563 '
564
565 test_expect_success 'directory changes to a file' '
566 test_when_finished clean_up_repo_and_stop_daemon &&
567
568 start_daemon --tf "$PWD/.git/trace" &&
569
570 directory_to_file &&
571
572 retry_grep "^event: dir1$" .git/trace
573 '
574
575 test_expect_success 'rapid nested directory creation' '
576 test_when_finished "git fsmonitor--daemon stop; rm -rf rapid" &&
577
578 start_daemon --tf "$PWD/.git/trace" &&
579
580 # Rapidly create nested directories to exercise race conditions
581 # where directory watches may be added concurrently during
582 # event processing and recursive scanning.
583 for i in $(test_seq 1 20)
584 do
585 mkdir -p "rapid/nested/dir$i/subdir/deep" || return 1
586 done &&
587
588 # Give the daemon time to process all events
589 sleep 1 &&
590
591 test-tool fsmonitor-client query --token 0 &&
592
593 # Verify daemon is still running (did not crash)
594 git fsmonitor--daemon status
595 '
596
597 # The next few test cases exercise the token-resync code. When filesystem
598 # drops events (because of filesystem velocity or because the daemon isn't
599 # polling fast enough), we need to discard the cached data (relative to the
600 # current token) and start collecting events under a new token.
601 #
602 # the 'test-tool fsmonitor-client flush' command can be used to send a
603 # "flush" message to a running daemon and ask it to do a flush/resync.
604
605 test_expect_success 'flush cached data' '
606 test_when_finished "stop_daemon_delete_repo test_flush" &&
607
608 git init test_flush &&
609
610 start_daemon -C test_flush --tf "$PWD/.git/trace_daemon" --tk true &&
611
612 # The daemon should have an initial token with no events in _0 and
613 # then a few (probably platform-specific number of) events in _1.
614 # These should both have the same <token_id>.
615
616 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_0 &&
617 nul_to_q <actual_0 >actual_q0 &&
618
619 >test_flush/file_1 &&
620 >test_flush/file_2 &&
621
622 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_1 &&
623 nul_to_q <actual_1 >actual_q1 &&
624
625 grep "file_1" actual_q1 &&
626
627 # Force a flush. This will change the <token_id>, reset the <seq_nr>, and
628 # flush the file data. Then create some events and ensure that the file
629 # again appears in the cache. It should have the new <token_id>.
630
631 test-tool -C test_flush fsmonitor-client flush >flush_0 &&
632 nul_to_q <flush_0 >flush_q0 &&
633 grep "^builtin:test_00000002:0Q/Q$" flush_q0 &&
634
635 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_2 &&
636 nul_to_q <actual_2 >actual_q2 &&
637
638 grep "^builtin:test_00000002:0Q$" actual_q2 &&
639
640 >test_flush/file_3 &&
641
642 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_3 &&
643 nul_to_q <actual_3 >actual_q3 &&
644
645 grep "file_3" actual_q3
646 '
647
648 # The next few test cases create repos where the .git directory is NOT
649 # inside the one of the working directory. That is, where .git is a file
650 # that points to a directory elsewhere. This happens for submodules and
651 # non-primary worktrees.
652
653 test_expect_success 'setup worktree base' '
654 git init wt-base &&
655 echo 1 >wt-base/file1 &&
656 git -C wt-base add file1 &&
657 git -C wt-base commit -m "c1"
658 '
659
660 test_expect_success 'worktree with .git file' '
661 git -C wt-base worktree add ../wt-secondary &&
662
663 start_daemon -C wt-secondary \
664 --tf "$PWD/trace_wt_secondary" \
665 --t2 "$PWD/trace2_wt_secondary" &&
666
667 git -C wt-secondary fsmonitor--daemon stop &&
668 test_must_fail git -C wt-secondary fsmonitor--daemon status
669 '
670
671 # NEEDSWORK: Repeat one of the "edit" tests on wt-secondary and
672 # confirm that we get the same events and behavior -- that is, that
673 # fsmonitor--daemon correctly watches BOTH the working directory and
674 # the external GITDIR directory and behaves the same as when ".git"
675 # is a directory inside the working directory.
676
677 test_expect_success 'cleanup worktrees' '
678 stop_daemon_delete_repo wt-secondary &&
679 stop_daemon_delete_repo wt-base
680 '
681
682 # The next few tests perform arbitrary/contrived file operations and
683 # confirm that status is correct. That is, that the data (or lack of
684 # data) from fsmonitor doesn't cause incorrect results. And doesn't
685 # cause incorrect results when the untracked-cache is enabled.
686
687 test_lazy_prereq UNTRACKED_CACHE '
688 git update-index --test-untracked-cache
689 '
690
691 test_expect_success 'Matrix: setup for untracked-cache,fsmonitor matrix' '
692 test_unconfig core.fsmonitor &&
693 git update-index --no-fsmonitor &&
694 test_might_fail git fsmonitor--daemon stop
695 '
696
697 matrix_clean_up_repo () {
698 git reset --hard HEAD &&
699 git clean -fd
700 }
701
702 matrix_try () {
703 uc=$1 &&
704 fsm=$2 &&
705 fn=$3 &&
706
707 if test $uc = true && test $fsm = false
708 then
709 # The untracked-cache is buggy when FSMonitor is
710 # DISABLED, so skip the tests for this matrix
711 # combination.
712 #
713 # We've observed random, occasional test failures on
714 # Windows and MacOS when the UC is turned on and FSM
715 # is turned off. These are rare, but they do happen
716 # indicating that it is probably a race condition within
717 # the untracked cache itself.
718 #
719 # It usually happens when a test does F/D trickery and
720 # then the NEXT test fails because of extra status
721 # output from stale UC data from the previous test.
722 #
723 # Since FSMonitor is not involved in the error, skip
724 # the tests for this matrix combination.
725 #
726 return 0
727 fi &&
728
729 test_expect_success "Matrix[uc:$uc][fsm:$fsm] $fn" '
730 matrix_clean_up_repo &&
731 $fn &&
732 if test $uc = false && test $fsm = false
733 then
734 git status --porcelain=v1 >.git/expect.$fn
735 else
736 git status --porcelain=v1 >.git/actual.$fn &&
737 test_cmp .git/expect.$fn .git/actual.$fn
738 fi
739 '
740 }
741
742 uc_values="false"
743 test_have_prereq UNTRACKED_CACHE && uc_values="false true"
744 for uc_val in $uc_values
745 do
746 if test $uc_val = false
747 then
748 test_expect_success "Matrix[uc:$uc_val] disable untracked cache" '
749 git config core.untrackedcache false &&
750 git update-index --no-untracked-cache
751 '
752 else
753 test_expect_success "Matrix[uc:$uc_val] enable untracked cache" '
754 git config core.untrackedcache true &&
755 git update-index --untracked-cache
756 '
757 fi
758
759 fsm_values="false true"
760 for fsm_val in $fsm_values
761 do
762 if test $fsm_val = false
763 then
764 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor" '
765 test_unconfig core.fsmonitor &&
766 git update-index --no-fsmonitor &&
767 test_might_fail git fsmonitor--daemon stop
768 '
769 else
770 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] enable fsmonitor" '
771 git config core.fsmonitor true &&
772 git fsmonitor--daemon start &&
773 git update-index --fsmonitor
774 '
775 fi
776
777 matrix_try $uc_val $fsm_val edit_files
778 matrix_try $uc_val $fsm_val delete_files
779 matrix_try $uc_val $fsm_val create_files
780 matrix_try $uc_val $fsm_val rename_files
781 matrix_try $uc_val $fsm_val file_to_directory
782 matrix_try $uc_val $fsm_val directory_to_file
783
784 matrix_try $uc_val $fsm_val move_directory_contents_deeper
785 matrix_try $uc_val $fsm_val move_directory_up
786 matrix_try $uc_val $fsm_val move_directory
787
788 if test $fsm_val = true
789 then
790 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
791 test_unconfig core.fsmonitor &&
792 git update-index --no-fsmonitor &&
793 test_might_fail git fsmonitor--daemon stop
794 '
795 fi
796 done
797 done
798
799 # Test Unicode UTF-8 characters in the pathname of the working
800 # directory root. Use of "*A()" routines rather than "*W()" routines
801 # on Windows can sometimes lead to odd failures.
802 #
803 u1=$(printf "u_c3_a6__\xC3\xA6")
804 u2=$(printf "u_e2_99_ab__\xE2\x99\xAB")
805 u_values="$u1 $u2"
806 for u in $u_values
807 do
808 test_expect_success "unicode in repo root path: $u" '
809 test_when_finished "stop_daemon_delete_repo $u" &&
810
811 git init "$u" &&
812 echo 1 >"$u"/file1 &&
813 git -C "$u" add file1 &&
814 git -C "$u" config core.fsmonitor true &&
815
816 start_daemon -C "$u" &&
817 git -C "$u" status >actual &&
818 grep "new file: file1" actual
819 '
820 done
821
822 # Test fsmonitor interaction with submodules.
823 #
824 # If we start the daemon in the super, it will see FS events for
825 # everything in the working directory cone and this includes any
826 # files/directories contained *within* the submodules.
827 #
828 # A `git status` at top level will get events for items within the
829 # submodule and ignore them, since they aren't named in the index
830 # of the super repo. This makes the fsmonitor response a little
831 # noisy, but it doesn't alter the correctness of the state of the
832 # super-proper.
833 #
834 # When we have submodules, `git status` normally does a recursive
835 # status on each of the submodules and adds a summary row for any
836 # dirty submodules. (See the "S..." bits in porcelain V2 output.)
837 #
838 # It is therefore important that the top level status not be tricked
839 # by the FSMonitor response to skip those recursive calls. That is,
840 # even if FSMonitor says that the mtime of the submodule directory
841 # hasn't changed and it could be implicitly marked valid, we must
842 # not take that shortcut. We need to force the recursion into the
843 # submodule so that we get a summary of the status *within* the
844 # submodule.
845
846 create_super () {
847 super="$1" &&
848
849 git init "$super" &&
850 echo x >"$super/file_1" &&
851 echo y >"$super/file_2" &&
852 echo z >"$super/file_3" &&
853 mkdir "$super/dir_1" &&
854 echo a >"$super/dir_1/file_11" &&
855 echo b >"$super/dir_1/file_12" &&
856 mkdir "$super/dir_1/dir_2" &&
857 echo a >"$super/dir_1/dir_2/file_21" &&
858 echo b >"$super/dir_1/dir_2/file_22" &&
859 git -C "$super" add . &&
860 git -C "$super" commit -m "initial $super commit"
861 }
862
863 create_sub () {
864 sub="$1" &&
865
866 git init "$sub" &&
867 echo x >"$sub/file_x" &&
868 echo y >"$sub/file_y" &&
869 echo z >"$sub/file_z" &&
870 mkdir "$sub/dir_x" &&
871 echo a >"$sub/dir_x/file_a" &&
872 echo b >"$sub/dir_x/file_b" &&
873 mkdir "$sub/dir_x/dir_y" &&
874 echo a >"$sub/dir_x/dir_y/file_a" &&
875 echo b >"$sub/dir_x/dir_y/file_b" &&
876 git -C "$sub" add . &&
877 git -C "$sub" commit -m "initial $sub commit"
878 }
879
880 my_match_and_clean () {
881 git -C super --no-optional-locks status --porcelain=v2 >actual.with &&
882 git -C super --no-optional-locks -c core.fsmonitor=false \
883 status --porcelain=v2 >actual.without &&
884 test_cmp actual.with actual.without &&
885
886 git -C super --no-optional-locks diff-index --name-status HEAD >actual.with &&
887 git -C super --no-optional-locks -c core.fsmonitor=false \
888 diff-index --name-status HEAD >actual.without &&
889 test_cmp actual.with actual.without &&
890
891 git -C super/dir_1/dir_2/sub reset --hard &&
892 git -C super/dir_1/dir_2/sub clean -d -f
893 }
894
895 test_expect_success 'submodule setup' '
896 git config --global protocol.file.allow always
897 '
898
899 test_expect_success 'submodule always visited' '
900 test_when_finished "git -C super fsmonitor--daemon stop; \
901 rm -rf super; \
902 rm -rf sub" &&
903
904 create_super super &&
905 create_sub sub &&
906
907 git -C super submodule add ../sub ./dir_1/dir_2/sub &&
908 git -C super commit -m "add sub" &&
909
910 start_daemon -C super &&
911 git -C super config core.fsmonitor true &&
912 git -C super update-index --fsmonitor &&
913 git -C super status &&
914
915 # Now run pairs of commands w/ and w/o FSMonitor while we make
916 # some dirt in the submodule and confirm matching output.
917
918 # Completely clean status.
919 my_match_and_clean &&
920
921 # .M S..U
922 echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_u &&
923 my_match_and_clean &&
924
925 # .M S.M.
926 echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_m &&
927 git -C super/dir_1/dir_2/sub add . &&
928 my_match_and_clean &&
929
930 # .M S.M.
931 echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
932 git -C super/dir_1/dir_2/sub add . &&
933 my_match_and_clean &&
934
935 # .M SC..
936 echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
937 git -C super/dir_1/dir_2/sub add . &&
938 git -C super/dir_1/dir_2/sub commit -m "SC.." &&
939 my_match_and_clean
940 '
941
942 # If a submodule has a `sub/.git/` directory (rather than a file
943 # pointing to the super's `.git/modules/sub`) and `core.fsmonitor`
944 # turned on in the submodule and the daemon is not yet started in
945 # the submodule, and someone does a `git submodule absorbgitdirs`
946 # in the super, Git will recursively invoke `git submodule--helper`
947 # to do the work and this may try to read the index. This will
948 # try to start the daemon in the submodule.
949
950 test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
951 test_when_finished "rm -rf super; \
952 rm -rf sub; \
953 rm super-sub.trace" &&
954
955 create_super super &&
956 create_sub sub &&
957
958 # Copy rather than submodule add so that we get a .git dir.
959 cp -R ./sub ./super/dir_1/dir_2/sub &&
960
961 git -C super/dir_1/dir_2/sub config core.fsmonitor true &&
962
963 git -C super submodule add ../sub ./dir_1/dir_2/sub &&
964 git -C super commit -m "add sub" &&
965
966 test_path_is_dir super/dir_1/dir_2/sub/.git &&
967
968 cwd="$(cd super && pwd)" &&
969 cat >expect <<-EOF &&
970 Migrating git directory of '\''dir_1/dir_2/sub'\'' from
971 '\''$cwd/dir_1/dir_2/sub/.git'\'' to
972 '\''$cwd/.git/modules/dir_1/dir_2/sub'\''
973 EOF
974 GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
975 git -C super submodule absorbgitdirs >out 2>actual &&
976 test_cmp expect actual &&
977 test_must_be_empty out &&
978
979 # Confirm that the trace2 log contains a record of the
980 # daemon starting.
981 test_subcommand git fsmonitor--daemon start <super-sub.trace
982 '
983
984 start_git_in_background () {
985 git "$@" &
986 git_pid=$!
987 git_pgid=$(ps -o pgid= -p $git_pid 2>/dev/null ||
988 awk '{print $5}' /proc/$git_pid/stat 2>/dev/null) &&
989 git_pgid="${git_pgid## }" &&
990 git_pgid="${git_pgid%% }"
991 nr_tries_left=10
992 while true
993 do
994 if test $nr_tries_left -eq 0
995 then
996 kill -- -$git_pgid
997 exit 1
998 fi
999 sleep 1
1000 nr_tries_left=$(($nr_tries_left - 1))
1001 done >/dev/null 2>&1 3>&- 4>&- 5>&- 6>&- 7>&- &
1002 watchdog_pid=$!
1003 wait $git_pid
1004 }
1005
1006 stop_git () {
1007 test -n "$git_pgid" || return 0
1008 while kill -0 -- -$git_pgid 2>/dev/null
1009 do
1010 kill -- -$git_pgid 2>/dev/null
1011 sleep 1
1012 done
1013 }
1014
1015 stop_watchdog () {
1016 while kill -0 $watchdog_pid
1017 do
1018 kill $watchdog_pid
1019 sleep 1
1020 done
1021 }
1022
1023 test_expect_success !MINGW "submodule implicitly starts daemon by pull" '
1024 test_atexit "stop_watchdog" &&
1025 test_when_finished "set +m; stop_git; rm -rf cloned super sub" &&
1026
1027 create_super super &&
1028 create_sub sub &&
1029
1030 git -C super submodule add ../sub ./dir_1/dir_2/sub &&
1031 git -C super commit -m "add sub" &&
1032 git clone --recurse-submodules super cloned &&
1033
1034 git -C cloned/dir_1/dir_2/sub config core.fsmonitor true &&
1035 set -m &&
1036 start_git_in_background -C cloned pull --recurse-submodules
1037 '
1038
1039 # On a case-insensitive file system, confirm that the daemon
1040 # notices when the .git directory is moved/renamed/deleted
1041 # regardless of how it is spelled in the FS event.
1042 # That is, does the FS event receive the spelling of the
1043 # operation or does it receive the spelling preserved with
1044 # the file/directory.
1045 #
1046 test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
1047 test_when_finished "stop_daemon_delete_repo test_insensitive" &&
1048
1049 git init test_insensitive &&
1050
1051 start_daemon -C test_insensitive --tf "$PWD/insensitive.trace" &&
1052
1053 mkdir -p test_insensitive/abc/def &&
1054 echo xyz >test_insensitive/ABC/DEF/xyz &&
1055
1056 test_path_is_dir test_insensitive/.git &&
1057 test_path_is_dir test_insensitive/.GIT &&
1058
1059 # Rename .git using an alternate spelling to verify that
1060 # the daemon detects it and automatically shuts down.
1061 mv test_insensitive/.GIT test_insensitive/.FOO &&
1062
1063 # See [1] above.
1064 mv test_insensitive/.FOO test_insensitive/.git &&
1065
1066 verify_implicit_shutdown test_insensitive &&
1067
1068 # Verify that events were reported using on-disk spellings of the
1069 # directories and files that we touched. We may or may not get a
1070 # trailing slash on modified directories.
1071 #
1072 grep -E "^event: abc/?$" ./insensitive.trace &&
1073 grep -E "^event: abc/def/?$" ./insensitive.trace &&
1074 grep -E "^event: abc/def/xyz$" ./insensitive.trace
1075 '
1076
1077 # The variable "unicode_debug" is defined in the following library
1078 # script to dump information about how the (OS, FS) handles Unicode
1079 # composition. Uncomment the following line if you want to enable it.
1080 #
1081 # unicode_debug=true
1082
1083 . "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh"
1084
1085 # See if the OS or filesystem does NFC/NFD aliasing/munging.
1086 #
1087 # The daemon should err on the side of caution and send BOTH the
1088 # NFC and NFD forms. It does not know the original spelling of
1089 # the pathname (how the user thinks it should be spelled), so
1090 # emit both and let the client decide (when necessary). This is
1091 # similar to "core.precomposeUnicode".
1092 #
1093 test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
1094 test_when_finished "stop_daemon_delete_repo test_unicode" &&
1095
1096 git init test_unicode &&
1097
1098 start_daemon -C test_unicode --tf "$PWD/unicode.trace" &&
1099
1100 # Create a directory using an NFC spelling.
1101 #
1102 mkdir test_unicode/nfc &&
1103 mkdir test_unicode/nfc/c_${utf8_nfc} &&
1104
1105 # Create a directory using an NFD spelling.
1106 #
1107 mkdir test_unicode/nfd &&
1108 mkdir test_unicode/nfd/d_${utf8_nfd} &&
1109
1110 test-tool -C test_unicode fsmonitor-client query --token 0 &&
1111
1112 if test_have_prereq UNICODE_NFC_PRESERVED
1113 then
1114 # We should have seen NFC event from OS.
1115 # We should not have synthesized an NFD event.
1116 grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
1117 grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
1118 else
1119 # We should have seen NFD event from OS.
1120 # We should have synthesized an NFC event.
1121 grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
1122 grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
1123 fi &&
1124
1125 # We assume UNICODE_NFD_PRESERVED.
1126 # We should have seen explicit NFD from OS.
1127 # We should have synthesized an NFC event.
1128 grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
1129 grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
1130 '
1131
1132 test_expect_success 'split-index and FSMonitor work well together' '
1133 git init split-index &&
1134 test_when_finished "git -C \"$PWD/split-index\" \
1135 fsmonitor--daemon stop" &&
1136 (
1137 cd split-index &&
1138 git config core.splitIndex true &&
1139 # force split-index in most cases
1140 git config splitIndex.maxPercentChange 99 &&
1141 git config core.fsmonitor true &&
1142
1143 # Create the following commit topology:
1144 #
1145 # * merge three
1146 # |\
1147 # | * three
1148 # * | merge two
1149 # |\|
1150 # | * two
1151 # * | one
1152 # |/
1153 # * 5a5efd7 initial
1154
1155 test_commit initial &&
1156 test_commit two &&
1157 test_commit three &&
1158 git reset --hard initial &&
1159 test_commit one &&
1160 test_tick &&
1161 git merge two &&
1162 test_tick &&
1163 git merge three &&
1164
1165 git rebase --force-rebase -r one
1166 )
1167 '
1168
1169 # The FSMonitor daemon reports the OBSERVED pathname of modified files
1170 # and thus contains the OBSERVED spelling on case-insensitive file
1171 # systems. The daemon does not (and should not) load the .git/index
1172 # file and therefore does not know the expected case-spelling. Since
1173 # it is possible for the user to create files/subdirectories with the
1174 # incorrect case, a modified file event for a tracked will not have
1175 # the EXPECTED case. This can cause `index_name_pos()` to incorrectly
1176 # report that the file is untracked. This causes the client to fail to
1177 # mark the file as possibly dirty (keeping the CE_FSMONITOR_VALID bit
1178 # set) so that `git status` will avoid inspecting it and thus not
1179 # present in the status output.
1180 #
1181 # The setup is a little contrived.
1182 #
1183 test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
1184 test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
1185
1186 git init subdir_case_wrong &&
1187 (
1188 cd subdir_case_wrong &&
1189 echo x >AAA &&
1190 echo x >BBB &&
1191
1192 mkdir dir1 &&
1193 echo x >dir1/file1 &&
1194 mkdir dir1/dir2 &&
1195 echo x >dir1/dir2/file2 &&
1196 mkdir dir1/dir2/dir3 &&
1197 echo x >dir1/dir2/dir3/file3 &&
1198
1199 echo x >yyy &&
1200 echo x >zzz &&
1201 git add . &&
1202 git commit -m "data" &&
1203
1204 # This will cause "dir1/" and everything under it
1205 # to be deleted.
1206 git sparse-checkout set --cone --sparse-index &&
1207
1208 # Create dir2 with the wrong case and then let Git
1209 # repopulate dir3 -- it will not correct the spelling
1210 # of dir2.
1211 mkdir dir1 &&
1212 mkdir dir1/DIR2 &&
1213 git sparse-checkout add dir1/dir2/dir3
1214 ) &&
1215
1216 start_daemon -C subdir_case_wrong --tf "$PWD/subdir_case_wrong.trace" &&
1217
1218 # Enable FSMonitor in the client. Run enough commands for
1219 # the .git/index to sync up with the daemon with everything
1220 # marked clean.
1221 git -C subdir_case_wrong config core.fsmonitor true &&
1222 git -C subdir_case_wrong update-index --fsmonitor &&
1223 git -C subdir_case_wrong status &&
1224
1225 # Make some files dirty so that FSMonitor gets FSEvents for
1226 # each of them.
1227 echo xx >>subdir_case_wrong/AAA &&
1228 echo xx >>subdir_case_wrong/dir1/DIR2/dir3/file3 &&
1229 echo xx >>subdir_case_wrong/zzz &&
1230
1231 GIT_TRACE_FSMONITOR="$PWD/subdir_case_wrong.log" \
1232 git -C subdir_case_wrong --no-optional-locks status --short \
1233 >"$PWD/subdir_case_wrong.out" &&
1234
1235 # "git status" should have gotten file events for each of
1236 # the 3 files.
1237 #
1238 # "dir2" should be in the observed case on disk.
1239 grep "fsmonitor_refresh_callback" \
1240 <"$PWD/subdir_case_wrong.log" \
1241 >"$PWD/subdir_case_wrong.log1" &&
1242
1243 grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" &&
1244 grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" &&
1245
1246 grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
1247
1248 # Verify that we get a mapping event to correct the case.
1249 grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
1250 "$PWD/subdir_case_wrong.log1" &&
1251
1252 # The refresh-callbacks should have caused "git status" to clear
1253 # the CE_FSMONITOR_VALID bit on each of those files and caused
1254 # the worktree scan to visit them and mark them as modified.
1255 grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
1256 grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
1257 grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
1258 '
1259
1260 test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
1261 test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
1262
1263 git init file_case_wrong &&
1264 (
1265 cd file_case_wrong &&
1266 echo x >AAA &&
1267 echo x >BBB &&
1268
1269 mkdir dir1 &&
1270 mkdir dir1/dir2 &&
1271 mkdir dir1/dir2/dir3 &&
1272 echo x >dir1/dir2/dir3/FILE-3-B &&
1273 echo x >dir1/dir2/dir3/XXXX-3-X &&
1274 echo x >dir1/dir2/dir3/file-3-a &&
1275 echo x >dir1/dir2/dir3/yyyy-3-y &&
1276 mkdir dir1/dir2/dir4 &&
1277 echo x >dir1/dir2/dir4/FILE-4-A &&
1278 echo x >dir1/dir2/dir4/XXXX-4-X &&
1279 echo x >dir1/dir2/dir4/file-4-b &&
1280 echo x >dir1/dir2/dir4/yyyy-4-y &&
1281
1282 echo x >yyy &&
1283 echo x >zzz &&
1284 git add . &&
1285 git commit -m "data"
1286 ) &&
1287
1288 start_daemon -C file_case_wrong --tf "$PWD/file_case_wrong.trace" &&
1289
1290 # Enable FSMonitor in the client. Run enough commands for
1291 # the .git/index to sync up with the daemon with everything
1292 # marked clean.
1293 git -C file_case_wrong config core.fsmonitor true &&
1294 git -C file_case_wrong update-index --fsmonitor &&
1295 git -C file_case_wrong status &&
1296
1297 # Make some files dirty so that FSMonitor gets FSEvents for
1298 # each of them.
1299 echo xx >>file_case_wrong/AAA &&
1300 echo xx >>file_case_wrong/zzz &&
1301
1302 # Rename some files so that FSMonitor sees a create and delete
1303 # FSEvent for each. (A simple "mv foo FOO" is not portable
1304 # between macOS and Windows. It works on both platforms, but makes
1305 # the test messy, since (1) one platform updates "ctime" on the
1306 # moved file and one does not and (2) it causes a directory event
1307 # on one platform and not on the other which causes additional
1308 # scanning during "git status" which causes a "H" vs "h" discrepancy
1309 # in "git ls-files -f".) So old-school it and move it out of the
1310 # way and copy it to the case-incorrect name so that we get fresh
1311 # "ctime" and "mtime" values.
1312
1313 mv file_case_wrong/dir1/dir2/dir3/file-3-a file_case_wrong/dir1/dir2/dir3/ORIG &&
1314 cp file_case_wrong/dir1/dir2/dir3/ORIG file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
1315 rm file_case_wrong/dir1/dir2/dir3/ORIG &&
1316 mv file_case_wrong/dir1/dir2/dir4/FILE-4-A file_case_wrong/dir1/dir2/dir4/ORIG &&
1317 cp file_case_wrong/dir1/dir2/dir4/ORIG file_case_wrong/dir1/dir2/dir4/file-4-a &&
1318 rm file_case_wrong/dir1/dir2/dir4/ORIG &&
1319
1320 # Run status enough times to fully sync.
1321 #
1322 # The first instance should get the create and delete FSEvents
1323 # for each pair. Status should update the index with a new FSM
1324 # token (so the next invocation will not see data for these
1325 # events).
1326
1327 GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \
1328 git -C file_case_wrong status --short \
1329 >"$PWD/file_case_wrong-try1.out" &&
1330 grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" &&
1331 grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4" "$PWD/file_case_wrong-try1.log" &&
1332 grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6" "$PWD/file_case_wrong-try1.log" &&
1333 grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" &&
1334
1335 # FSM refresh will have invalidated the FSM bit and cause a regular
1336 # (real) scan of these tracked files, so they should have "H" status.
1337 # (We will not see a "h" status until the next refresh (on the next
1338 # command).)
1339
1340 git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" &&
1341 grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" &&
1342 grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" &&
1343
1344
1345 # Try the status again. We assume that the above status command
1346 # advanced the token so that the next one will not see those events.
1347
1348 GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \
1349 git -C file_case_wrong status --short \
1350 >"$PWD/file_case_wrong-try2.out" &&
1351 ! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" &&
1352 ! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" &&
1353 ! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" &&
1354 ! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" &&
1355
1356 # FSM refresh saw nothing, so it will mark all files as valid,
1357 # so they should now have "h" status.
1358
1359 git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" &&
1360 grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" &&
1361 grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" &&
1362
1363
1364 # We now have files with clean content, but with case-incorrect
1365 # file names. Modify them to see if status properly reports
1366 # them.
1367
1368 echo xx >>file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
1369 echo xx >>file_case_wrong/dir1/dir2/dir4/file-4-a &&
1370
1371 GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
1372 git -C file_case_wrong --no-optional-locks status --short \
1373 >"$PWD/file_case_wrong-try3.out" &&
1374
1375 # Verify that we get a mapping event to correct the case.
1376 grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
1377 "$PWD/file_case_wrong-try3.log" &&
1378 grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
1379 "$PWD/file_case_wrong-try3.log" &&
1380
1381 # FSEvents are in observed case.
1382 grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
1383 grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
1384
1385 # The refresh-callbacks should have caused "git status" to clear
1386 # the CE_FSMONITOR_VALID bit on each of those files and caused
1387 # the worktree scan to visit them and mark them as modified.
1388 grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
1389 grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
1390 '
1391
1392 test_done