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