Git fork
at reftables-rust 2562 lines 81 kB view raw
1#!/bin/sh 2 3test_description='compare full workdir to sparse workdir' 4 5GIT_TEST_SPLIT_INDEX=0 6GIT_TEST_SPARSE_INDEX= 7 8. ./test-lib.sh 9 10test_expect_success 'setup' ' 11 git init initial-repo && 12 ( 13 GIT_TEST_SPARSE_INDEX=0 && 14 cd initial-repo && 15 echo a >a && 16 echo "after deep" >e && 17 echo "after folder1" >g && 18 echo "after x" >z && 19 mkdir folder1 folder2 deep before x && 20 echo "before deep" >before/a && 21 echo "before deep again" >before/b && 22 mkdir deep/deeper1 deep/deeper2 deep/before deep/later && 23 mkdir deep/deeper1/deepest && 24 mkdir deep/deeper1/deepest2 && 25 mkdir deep/deeper1/deepest3 && 26 echo "after deeper1" >deep/e && 27 echo "after deepest" >deep/deeper1/e && 28 cp a folder1 && 29 cp a folder2 && 30 cp a x && 31 cp a deep && 32 cp a deep/before && 33 cp a deep/deeper1 && 34 cp a deep/deeper2 && 35 cp a deep/later && 36 cp a deep/deeper1/deepest && 37 cp a deep/deeper1/deepest2 && 38 cp a deep/deeper1/deepest3 && 39 cp -r deep/deeper1/ deep/deeper2 && 40 mkdir deep/deeper1/0 && 41 mkdir deep/deeper1/0/0 && 42 touch deep/deeper1/0/1 && 43 touch deep/deeper1/0/0/0 && 44 >folder1- && 45 >folder1.x && 46 >folder10 && 47 cp -r deep/deeper1/0 folder1 && 48 cp -r deep/deeper1/0 folder2 && 49 echo >>folder1/0/0/0 && 50 echo >>folder2/0/1 && 51 git add . && 52 git commit -m "initial commit" && 53 git checkout -b base && 54 for dir in folder1 folder2 deep 55 do 56 git checkout -b update-$dir base && 57 echo "updated $dir" >$dir/a && 58 git commit -a -m "update $dir" || return 1 59 done && 60 61 git checkout -b rename-base base && 62 cat >folder1/larger-content <<-\EOF && 63 matching 64 lines 65 help 66 inexact 67 renames 68 EOF 69 cp folder1/larger-content folder2/ && 70 cp folder1/larger-content deep/deeper1/ && 71 git add . && 72 git commit -m "add interesting rename content" && 73 74 git checkout -b rename-out-to-out rename-base && 75 mv folder1/a folder2/b && 76 mv folder1/larger-content folder2/edited-content && 77 echo >>folder2/edited-content && 78 echo >>folder2/0/1 && 79 echo stuff >>deep/deeper1/a && 80 git add . && 81 git commit -m "rename folder1/... to folder2/..." && 82 83 git checkout -b rename-out-to-in rename-base && 84 mv folder1/a deep/deeper1/b && 85 echo more stuff >>deep/deeper1/a && 86 rm folder2/0/1 && 87 mkdir folder2/0/1 && 88 echo >>folder2/0/1/1 && 89 mv folder1/larger-content deep/deeper1/edited-content && 90 echo >>deep/deeper1/edited-content && 91 git add . && 92 git commit -m "rename folder1/... to deep/deeper1/..." && 93 94 git checkout -b rename-in-to-out rename-base && 95 mv deep/deeper1/a folder1/b && 96 echo >>folder2/0/1 && 97 rm -rf folder1/0/0 && 98 echo >>folder1/0/0 && 99 mv deep/deeper1/larger-content folder1/edited-content && 100 echo >>folder1/edited-content && 101 git add . && 102 git commit -m "rename deep/deeper1/... to folder1/..." && 103 104 git checkout -b df-conflict-1 base && 105 rm -rf folder1 && 106 echo content >folder1 && 107 git add . && 108 git commit -m "dir to file" && 109 110 git checkout -b df-conflict-2 base && 111 rm -rf folder2 && 112 echo content >folder2 && 113 git add . && 114 git commit -m "dir to file" && 115 116 git checkout -b fd-conflict base && 117 rm a && 118 mkdir a && 119 echo content >a/a && 120 git add . && 121 git commit -m "file to dir" && 122 123 for side in left right 124 do 125 git checkout -b merge-$side base && 126 echo $side >>deep/deeper2/a && 127 echo $side >>folder1/a && 128 echo $side >>folder2/a && 129 git add . && 130 git commit -m "$side" || return 1 131 done && 132 133 git checkout -b deepest base && 134 echo "updated deepest" >deep/deeper1/deepest/a && 135 echo "updated deepest2" >deep/deeper1/deepest2/a && 136 echo "updated deepest3" >deep/deeper1/deepest3/a && 137 git commit -a -m "update deepest" && 138 139 git checkout -f base && 140 git reset --hard 141 ) 142' 143 144init_repos () { 145 rm -rf full-checkout sparse-checkout sparse-index && 146 147 # create repos in initial state 148 cp -r initial-repo full-checkout && 149 git -C full-checkout reset --hard && 150 151 cp -r initial-repo sparse-checkout && 152 git -C sparse-checkout reset --hard && 153 154 cp -r initial-repo sparse-index && 155 git -C sparse-index reset --hard && 156 157 # initialize sparse-checkout definitions 158 git -C sparse-checkout sparse-checkout init --cone && 159 git -C sparse-checkout sparse-checkout set deep && 160 git -C sparse-index sparse-checkout init --cone --sparse-index && 161 test_cmp_config -C sparse-index true index.sparse && 162 git -C sparse-index sparse-checkout set deep && 163 164 # Disable this message to keep stderr the same. 165 git -C sparse-index config advice.sparseIndexExpanded false 166} 167 168init_repos_as_submodules () { 169 git reset --hard && 170 init_repos && 171 git submodule add ./full-checkout && 172 git submodule add ./sparse-checkout && 173 git submodule add ./sparse-index && 174 175 git submodule status >actual && 176 grep full-checkout actual && 177 grep sparse-checkout actual && 178 grep sparse-index actual 179} 180 181run_on_sparse () { 182 cat >run-on-sparse-input && 183 184 ( 185 cd sparse-checkout && 186 GIT_PROGRESS_DELAY=100000 "$@" >../sparse-checkout-out 2>../sparse-checkout-err 187 ) <run-on-sparse-input && 188 ( 189 cd sparse-index && 190 GIT_PROGRESS_DELAY=100000 "$@" >../sparse-index-out 2>../sparse-index-err 191 ) <run-on-sparse-input 192} 193 194run_on_all () { 195 cat >run-on-all-input && 196 197 ( 198 cd full-checkout && 199 GIT_PROGRESS_DELAY=100000 "$@" >../full-checkout-out 2>../full-checkout-err 200 ) <run-on-all-input && 201 run_on_sparse "$@" <run-on-all-input 202} 203 204test_all_match () { 205 run_on_all "$@" && 206 test_cmp full-checkout-out sparse-checkout-out && 207 test_cmp full-checkout-out sparse-index-out && 208 test_cmp full-checkout-err sparse-checkout-err && 209 test_cmp full-checkout-err sparse-index-err 210} 211 212test_sparse_match () { 213 run_on_sparse "$@" && 214 test_cmp sparse-checkout-out sparse-index-out && 215 test_cmp sparse-checkout-err sparse-index-err 216} 217 218test_sparse_unstaged () { 219 file=$1 && 220 for repo in sparse-checkout sparse-index 221 do 222 # Skip "unmerged" paths 223 git -C $repo diff --staged --diff-filter=u -- "$file" >diff && 224 test_must_be_empty diff || return 1 225 done 226} 227 228# Usage: test_sparse_checkout_set "<c1> ... <cN>" "<s1> ... <sM>" 229# Verifies that "git sparse-checkout set <c1> ... <cN>" succeeds and 230# leaves the sparse index in a state where <s1> ... <sM> are sparse 231# directories (and <c1> ... <cN> are not). 232test_sparse_checkout_set () { 233 CONE_DIRS=$1 && 234 SPARSE_DIRS=$2 && 235 git -C sparse-index sparse-checkout set --skip-checks $CONE_DIRS && 236 git -C sparse-index ls-files --sparse --stage >cache && 237 238 # Check that the directories outside of the sparse-checkout cone 239 # have sparse directory entries. 240 for dir in $SPARSE_DIRS 241 do 242 TREE=$(git -C sparse-index rev-parse HEAD:$dir) && 243 grep "040000 $TREE 0 $dir/" cache \ 244 || return 1 245 done && 246 247 # Check that the directories in the sparse-checkout cone 248 # are not sparse directory entries. 249 for dir in $CONE_DIRS 250 do 251 # Allow TREE to not exist because 252 # $dir does not exist at HEAD. 253 TREE=$(git -C sparse-index rev-parse HEAD:$dir) || 254 ! grep "040000 $TREE 0 $dir/" cache \ 255 || return 1 256 done 257} 258 259test_expect_success 'sparse-index contents' ' 260 init_repos && 261 262 # Remove deep, add three other directories. 263 test_sparse_checkout_set \ 264 "folder1 folder2 x" \ 265 "before deep" && 266 267 # Remove folder1, add deep 268 test_sparse_checkout_set \ 269 "deep folder2 x" \ 270 "before folder1" && 271 272 # Replace deep with deep/deeper2 (dropping deep/deeper1) 273 # Add folder1 274 test_sparse_checkout_set \ 275 "deep/deeper2 folder1 folder2 x" \ 276 "before deep/deeper1" && 277 278 # Replace deep/deeper2 with deep/deeper1 279 # Replace folder1 with folder1/0/0 280 # Replace folder2 with non-existent folder2/2/3 281 # Add non-existent "bogus" 282 test_sparse_checkout_set \ 283 "bogus deep/deeper1 folder1/0/0 folder2/2/3 x" \ 284 "before deep/deeper2 folder2/0" && 285 286 # Drop down to only files at root 287 test_sparse_checkout_set \ 288 "" \ 289 "before deep folder1 folder2 x" && 290 291 # Disabling the sparse-index replaces tree entries with full ones 292 git -C sparse-index sparse-checkout init --no-sparse-index && 293 test_sparse_match git ls-files --stage --sparse 294' 295 296test_expect_success 'expanded in-memory index matches full index' ' 297 init_repos && 298 test_sparse_match git ls-files --stage 299' 300 301test_expect_success 'root directory cannot be sparse' ' 302 init_repos && 303 304 # Remove all in-cone files and directories from the index, collapse index 305 # with `git sparse-checkout reapply` 306 git -C sparse-index rm -r . && 307 git -C sparse-index sparse-checkout reapply && 308 309 # Verify sparse directories still present, root directory is not sparse 310 cat >expect <<-EOF && 311 before/ 312 folder1/ 313 folder2/ 314 x/ 315 EOF 316 git -C sparse-index ls-files --sparse >actual && 317 test_cmp expect actual 318' 319 320test_expect_success 'status with options' ' 321 init_repos && 322 test_sparse_match ls && 323 test_all_match git status --porcelain=v2 && 324 test_all_match git status --porcelain=v2 -z -u && 325 test_all_match git status --porcelain=v2 -uno && 326 run_on_all touch README.md && 327 test_all_match git status --porcelain=v2 && 328 test_all_match git status --porcelain=v2 -z -u && 329 test_all_match git status --porcelain=v2 -uno && 330 test_all_match git add README.md && 331 test_all_match git status --porcelain=v2 && 332 test_all_match git status --porcelain=v2 -z -u && 333 test_all_match git status --porcelain=v2 -uno 334' 335 336test_expect_success 'status with diff in unexpanded sparse directory' ' 337 init_repos && 338 test_all_match git checkout rename-base && 339 test_all_match git reset --soft rename-out-to-out && 340 test_all_match git status --porcelain=v2 341' 342 343test_expect_success 'status reports sparse-checkout' ' 344 init_repos && 345 git -C sparse-checkout status >full && 346 git -C sparse-index status >sparse && 347 test_grep "You are in a sparse checkout with " full && 348 test_grep "You are in a sparse checkout." sparse 349' 350 351test_expect_success 'add, commit, checkout' ' 352 init_repos && 353 354 write_script edit-contents <<-\EOF && 355 echo text >>$1 356 EOF 357 run_on_all ../edit-contents README.md && 358 359 test_all_match git add README.md && 360 test_all_match git status --porcelain=v2 && 361 test_all_match git commit -m "Add README.md" && 362 363 test_all_match git checkout HEAD~1 && 364 test_all_match git checkout - && 365 366 run_on_all ../edit-contents README.md && 367 368 test_all_match git add -A && 369 test_all_match git status --porcelain=v2 && 370 test_all_match git commit -m "Extend README.md" && 371 372 test_all_match git checkout HEAD~1 && 373 test_all_match git checkout - && 374 375 run_on_all ../edit-contents deep/newfile && 376 377 test_all_match git status --porcelain=v2 -uno && 378 test_all_match git status --porcelain=v2 && 379 test_all_match git add . && 380 test_all_match git status --porcelain=v2 && 381 test_all_match git commit -m "add deep/newfile" && 382 383 test_all_match git checkout HEAD~1 && 384 test_all_match git checkout - 385' 386 387test_expect_success 'git add, checkout, and reset with -p' ' 388 init_repos && 389 390 write_script edit-contents <<-\EOF && 391 echo text >>$1 392 EOF 393 394 # Does not expand when edits are within sparse checkout. 395 run_on_all ../edit-contents deep/a && 396 run_on_all ../edit-contents deep/deeper1/a && 397 398 test_write_lines y n >in && 399 run_on_all git add -p <in && 400 test_all_match git status --porcelain=v2 && 401 test_all_match git reset -p <in && 402 403 test_write_lines u 1 "" q >in && 404 run_on_all git add -i <in && 405 test_all_match git status --porcelain=v2 && 406 test_all_match git reset --hard && 407 408 run_on_sparse mkdir -p folder1 && 409 run_on_all ../edit-contents folder1/a && 410 test_write_lines y n y >in && 411 run_on_all git add -p <in && 412 test_sparse_match git status --porcelain=v2 && 413 test_sparse_match git reset && 414 test_write_lines u 2 3 "" q >in && 415 run_on_all git add -i <in && 416 test_sparse_match git status --porcelain=v2 && 417 418 run_on_all git add --sparse folder1 && 419 run_on_all git commit -m "take changes" && 420 test_write_lines y n y >in && 421 test_sparse_match git checkout HEAD~1 --patch <in && 422 test_sparse_match git status --porcelain=v2 423' 424 425test_expect_success 'deep changes during checkout' ' 426 init_repos && 427 428 test_sparse_match git sparse-checkout set deep/deeper1/deepest && 429 test_all_match git checkout deepest && 430 test_all_match git checkout base 431' 432 433test_expect_success 'checkout with modified sparse directory' ' 434 init_repos && 435 436 test_all_match git checkout rename-in-to-out -- . && 437 test_sparse_match git sparse-checkout reapply && 438 test_all_match git checkout base 439' 440 441test_expect_success 'checkout orphan then non-orphan' ' 442 init_repos && 443 444 test_all_match git checkout --orphan test-orphan && 445 test_all_match git status --porcelain=v2 && 446 test_all_match git checkout base && 447 test_all_match git status --porcelain=v2 448' 449 450test_expect_success 'add outside sparse cone' ' 451 init_repos && 452 453 run_on_sparse mkdir folder1 && 454 run_on_sparse ../edit-contents folder1/a && 455 run_on_sparse ../edit-contents folder1/newfile && 456 test_sparse_match test_must_fail git add folder1/a && 457 grep "Disable or modify the sparsity rules" sparse-checkout-err && 458 test_sparse_unstaged folder1/a && 459 test_sparse_match test_must_fail git add folder1/newfile && 460 grep "Disable or modify the sparsity rules" sparse-checkout-err && 461 test_sparse_unstaged folder1/newfile 462' 463 464test_expect_success 'commit including unstaged changes' ' 465 init_repos && 466 467 write_script edit-file <<-\EOF && 468 echo $1 >$2 469 EOF 470 471 run_on_all ../edit-file 1 a && 472 run_on_all ../edit-file 1 deep/a && 473 474 test_all_match git commit -m "-a" -a && 475 test_all_match git status --porcelain=v2 && 476 477 run_on_all ../edit-file 2 a && 478 run_on_all ../edit-file 2 deep/a && 479 480 test_all_match git commit -m "--include" --include deep/a && 481 test_all_match git status --porcelain=v2 && 482 test_all_match git commit -m "--include" --include a && 483 test_all_match git status --porcelain=v2 && 484 485 run_on_all ../edit-file 3 a && 486 run_on_all ../edit-file 3 deep/a && 487 488 test_all_match git commit -m "--amend" -a --amend && 489 test_all_match git status --porcelain=v2 490' 491 492test_expect_success 'status/add: outside sparse cone' ' 493 init_repos && 494 495 # folder1 is at HEAD, but outside the sparse cone 496 run_on_sparse mkdir folder1 && 497 cp initial-repo/folder1/a sparse-checkout/folder1/a && 498 cp initial-repo/folder1/a sparse-index/folder1/a && 499 500 test_sparse_match git status && 501 502 write_script edit-contents <<-\EOF && 503 echo text >>$1 504 EOF 505 run_on_all ../edit-contents folder1/a && 506 run_on_all ../edit-contents folder1/new && 507 508 test_sparse_match git status --porcelain=v2 && 509 510 # Adding the path outside of the sparse-checkout cone should fail. 511 test_sparse_match test_must_fail git add folder1/a && 512 grep "Disable or modify the sparsity rules" sparse-checkout-err && 513 test_sparse_unstaged folder1/a && 514 test_all_match git add --refresh folder1/a && 515 test_must_be_empty sparse-checkout-err && 516 test_sparse_unstaged folder1/a && 517 test_sparse_match test_must_fail git add folder1/new && 518 grep "Disable or modify the sparsity rules" sparse-checkout-err && 519 test_sparse_unstaged folder1/new && 520 test_sparse_match git add --sparse folder1/a && 521 test_sparse_match git add --sparse folder1/new && 522 523 test_all_match git add --sparse . && 524 test_all_match git status --porcelain=v2 && 525 test_all_match git commit -m folder1/new && 526 test_all_match git rev-parse HEAD^{tree} && 527 528 run_on_all ../edit-contents folder1/newer && 529 test_all_match git add --sparse folder1/ && 530 test_all_match git status --porcelain=v2 && 531 test_all_match git commit -m folder1/newer && 532 test_all_match git rev-parse HEAD^{tree} 533' 534 535test_expect_success 'checkout and reset --hard' ' 536 init_repos && 537 538 test_all_match git checkout update-folder1 && 539 test_all_match git status --porcelain=v2 && 540 541 test_all_match git checkout update-deep && 542 test_all_match git status --porcelain=v2 && 543 544 test_all_match git checkout -b reset-test && 545 test_all_match git reset --hard deepest && 546 test_all_match git reset --hard update-folder1 && 547 test_all_match git reset --hard update-folder2 548' 549 550test_expect_success 'diff --cached' ' 551 init_repos && 552 553 write_script edit-contents <<-\EOF && 554 echo text >>README.md 555 EOF 556 run_on_all ../edit-contents && 557 558 test_all_match git diff && 559 test_all_match git diff --cached && 560 test_all_match git add README.md && 561 test_all_match git diff && 562 test_all_match git diff --cached 563' 564 565# NEEDSWORK: sparse-checkout behaves differently from full-checkout when 566# running this test with 'df-conflict-2' after 'df-conflict-1'. 567test_expect_success 'diff with renames and conflicts' ' 568 init_repos && 569 570 for branch in rename-out-to-out \ 571 rename-out-to-in \ 572 rename-in-to-out \ 573 df-conflict-1 \ 574 fd-conflict 575 do 576 test_all_match git checkout rename-base && 577 test_all_match git checkout $branch -- . && 578 test_all_match git status --porcelain=v2 && 579 test_all_match git diff --cached --no-renames && 580 test_all_match git diff --cached --find-renames || return 1 581 done 582' 583 584test_expect_success 'diff with directory/file conflicts' ' 585 init_repos && 586 587 for branch in rename-out-to-out \ 588 rename-out-to-in \ 589 rename-in-to-out \ 590 df-conflict-1 \ 591 df-conflict-2 \ 592 fd-conflict 593 do 594 git -C full-checkout reset --hard && 595 test_sparse_match git reset --hard && 596 test_all_match git checkout $branch && 597 test_all_match git checkout rename-base -- . && 598 test_all_match git status --porcelain=v2 && 599 test_all_match git diff --cached --no-renames && 600 test_all_match git diff --cached --find-renames || return 1 601 done 602' 603 604test_expect_success 'log with pathspec outside sparse definition' ' 605 init_repos && 606 607 test_all_match git log -- a && 608 test_all_match git log -- folder1/a && 609 test_all_match git log -- folder2/a && 610 test_all_match git log -- deep/a && 611 test_all_match git log -- deep/deeper1/a && 612 test_all_match git log -- deep/deeper1/deepest/a && 613 614 test_all_match git checkout update-folder1 && 615 test_all_match git log -- folder1/a 616' 617 618test_expect_success 'blame with pathspec inside sparse definition' ' 619 init_repos && 620 621 for file in a \ 622 deep/a \ 623 deep/deeper1/a \ 624 deep/deeper1/deepest/a 625 do 626 test_all_match git blame $file || return 1 627 done 628' 629 630# Without a revision specified, blame will error if passed any file that 631# is not present in the working directory (even if the file is tracked). 632# Here we just verify that this is also true with sparse checkouts. 633test_expect_success 'blame with pathspec outside sparse definition' ' 634 init_repos && 635 test_sparse_match git sparse-checkout set && 636 637 for file in \ 638 deep/a \ 639 deep/deeper1/a \ 640 deep/deeper1/deepest/a 641 do 642 test_sparse_match test_must_fail git blame $file && 643 cat >expect <<-EOF && 644 fatal: Cannot lstat '"'"'$file'"'"': No such file or directory 645 EOF 646 # We compare sparse-checkout-err and sparse-index-err in 647 # `test_sparse_match`. Given we know they are the same, we 648 # only check the content of sparse-index-err here. 649 test_cmp expect sparse-index-err || return 1 650 done 651' 652 653test_expect_success 'checkout and reset (mixed)' ' 654 init_repos && 655 656 test_all_match git checkout -b reset-test update-deep && 657 test_all_match git reset deepest && 658 659 # Because skip-worktree is preserved, resetting to update-folder1 660 # will show worktree changes for folder1/a in full-checkout, but not 661 # in sparse-checkout or sparse-index. 662 git -C full-checkout reset update-folder1 >full-checkout-out && 663 test_sparse_match git reset update-folder1 && 664 grep "M folder1/a" full-checkout-out && 665 ! grep "M folder1/a" sparse-checkout-out && 666 run_on_sparse test_path_is_missing folder1 667' 668 669test_expect_success 'checkout and reset (merge)' ' 670 init_repos && 671 672 write_script edit-contents <<-\EOF && 673 echo text >>$1 674 EOF 675 676 test_all_match git checkout -b reset-test update-deep && 677 run_on_all ../edit-contents a && 678 test_all_match git reset --merge deepest && 679 test_all_match git status --porcelain=v2 && 680 681 test_all_match git reset --hard update-deep && 682 run_on_all ../edit-contents deep/a && 683 test_all_match test_must_fail git reset --merge deepest 684' 685 686test_expect_success 'checkout and reset (keep)' ' 687 init_repos && 688 689 write_script edit-contents <<-\EOF && 690 echo text >>$1 691 EOF 692 693 test_all_match git checkout -b reset-test update-deep && 694 run_on_all ../edit-contents a && 695 test_all_match git reset --keep deepest && 696 test_all_match git status --porcelain=v2 && 697 698 test_all_match git reset --hard update-deep && 699 run_on_all ../edit-contents deep/a && 700 test_all_match test_must_fail git reset --keep deepest 701' 702 703test_expect_success 'reset with pathspecs inside sparse definition' ' 704 init_repos && 705 706 write_script edit-contents <<-\EOF && 707 echo text >>$1 708 EOF 709 710 test_all_match git checkout -b reset-test update-deep && 711 run_on_all ../edit-contents deep/a && 712 713 test_all_match git reset base -- deep/a && 714 test_all_match git status --porcelain=v2 && 715 716 test_all_match git reset base -- nonexistent-file && 717 test_all_match git status --porcelain=v2 && 718 719 test_all_match git reset deepest -- deep && 720 test_all_match git status --porcelain=v2 721' 722 723# Although the working tree differs between full and sparse checkouts after 724# reset, the state of the index is the same. 725test_expect_success 'reset with pathspecs outside sparse definition' ' 726 init_repos && 727 test_all_match git checkout -b reset-test base && 728 729 test_sparse_match git reset update-folder1 -- folder1 && 730 git -C full-checkout reset update-folder1 -- folder1 && 731 test_all_match git ls-files -s -- folder1 && 732 733 test_sparse_match git reset update-folder2 -- folder2/a && 734 git -C full-checkout reset update-folder2 -- folder2/a && 735 test_all_match git ls-files -s -- folder2/a 736' 737 738test_expect_success 'reset with wildcard pathspec' ' 739 init_repos && 740 741 test_all_match git reset update-deep -- deep\* && 742 test_all_match git ls-files -s -- deep && 743 744 test_all_match git reset deepest -- deep\*\*\* && 745 test_all_match git ls-files -s -- deep && 746 747 # The following `git reset`s result in updating the index on files with 748 # `skip-worktree` enabled. To avoid failing due to discrepancies in reported 749 # "modified" files, `test_sparse_match` reset is performed separately from 750 # "full-checkout" reset, then the index contents of all repos are verified. 751 752 test_sparse_match git reset update-folder1 -- \*/a && 753 git -C full-checkout reset update-folder1 -- \*/a && 754 test_all_match git ls-files -s -- deep/a folder1/a && 755 756 test_sparse_match git reset update-folder2 -- folder\* && 757 git -C full-checkout reset update-folder2 -- folder\* && 758 test_all_match git ls-files -s -- folder10 folder1 folder2 && 759 760 test_sparse_match git reset base -- folder1/\* && 761 git -C full-checkout reset base -- folder1/\* && 762 test_all_match git ls-files -s -- folder1 763' 764 765test_expect_success 'reset hard with removed sparse dir' ' 766 init_repos && 767 768 run_on_all git rm -r --sparse folder1 && 769 test_all_match git status --porcelain=v2 && 770 771 test_all_match git reset --hard && 772 test_all_match git status --porcelain=v2 && 773 774 cat >expect <<-\EOF && 775 folder1/ 776 EOF 777 778 git -C sparse-index ls-files --sparse folder1 >out && 779 test_cmp expect out 780' 781 782test_expect_success 'update-index modify outside sparse definition' ' 783 init_repos && 784 785 write_script edit-contents <<-\EOF && 786 echo text >>$1 787 EOF 788 789 # Create & modify folder1/a 790 # Note that this setup is a manual way of reaching the erroneous 791 # condition in which a `skip-worktree` enabled, outside-of-cone file 792 # exists on disk. It is used here to ensure `update-index` is stable 793 # and behaves predictably if such a condition occurs. 794 run_on_sparse mkdir -p folder1 && 795 run_on_sparse cp ../initial-repo/folder1/a folder1/a && 796 run_on_all ../edit-contents folder1/a && 797 798 # If file has skip-worktree enabled, but the file is present, it is 799 # treated the same as if skip-worktree is disabled 800 test_all_match git status --porcelain=v2 && 801 test_all_match git update-index folder1/a && 802 test_all_match git status --porcelain=v2 && 803 804 # When skip-worktree is disabled (even on files outside sparse cone), file 805 # is updated in the index 806 test_sparse_match git update-index --no-skip-worktree folder1/a && 807 test_all_match git status --porcelain=v2 && 808 test_all_match git update-index folder1/a && 809 test_all_match git status --porcelain=v2 810' 811 812test_expect_success 'update-index --add outside sparse definition' ' 813 init_repos && 814 815 write_script edit-contents <<-\EOF && 816 echo text >>$1 817 EOF 818 819 # Create folder1, add new file 820 run_on_sparse mkdir -p folder1 && 821 run_on_all ../edit-contents folder1/b && 822 823 # The *untracked* out-of-cone file is added to the index because it does 824 # not have a `skip-worktree` bit to signal that it should be ignored 825 # (unlike in `git add`, which will fail due to the file being outside 826 # the sparse checkout definition). 827 test_all_match git update-index --add folder1/b && 828 test_all_match git status --porcelain=v2 829' 830 831# NEEDSWORK: `--remove`, unlike the rest of `update-index`, does not ignore 832# `skip-worktree` entries by default and will remove them from the index. 833# The `--ignore-skip-worktree-entries` flag must be used in conjunction with 834# `--remove` to ignore the `skip-worktree` entries and prevent their removal 835# from the index. 836test_expect_success 'update-index --remove outside sparse definition' ' 837 init_repos && 838 839 # When --ignore-skip-worktree-entries is _not_ specified: 840 # out-of-cone, not-on-disk files are removed from the index 841 test_sparse_match git update-index --remove folder1/a && 842 cat >expect <<-EOF && 843 D folder1/a 844 EOF 845 test_sparse_match git diff --cached --name-status && 846 test_cmp expect sparse-checkout-out && 847 848 test_sparse_match git diff-index --cached HEAD && 849 850 # Reset the state 851 test_all_match git reset --hard && 852 853 # When --ignore-skip-worktree-entries is specified, out-of-cone 854 # (skip-worktree) files are ignored 855 test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a && 856 test_sparse_match git diff --cached --name-status && 857 test_must_be_empty sparse-checkout-out && 858 859 test_sparse_match git diff-index --cached HEAD && 860 861 # Reset the state 862 test_all_match git reset --hard && 863 864 # --force-remove supersedes --ignore-skip-worktree-entries, removing 865 # a skip-worktree file from the index (and disk) when both are specified 866 # with --remove 867 test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a && 868 cat >expect <<-EOF && 869 D folder1/a 870 EOF 871 test_sparse_match git diff --cached --name-status && 872 test_cmp expect sparse-checkout-out && 873 874 test_sparse_match git diff-index --cached HEAD 875' 876 877test_expect_success 'update-index with directories' ' 878 init_repos && 879 880 # update-index will exit silently when provided with a directory name 881 # containing a trailing slash 882 test_all_match git update-index deep/ folder1/ && 883 grep "Ignoring path deep/" sparse-checkout-err && 884 grep "Ignoring path folder1/" sparse-checkout-err && 885 886 # When update-index is given a directory name WITHOUT a trailing slash, it will 887 # behave in different ways depending on the status of the directory on disk: 888 # * if it exists, the command exits with an error ("add individual files instead") 889 # * if it does NOT exist (e.g., in a sparse-checkout), it is assumed to be a 890 # file and either triggers an error ("does not exist and --remove not passed") 891 # or is ignored completely (when using --remove) 892 test_all_match test_must_fail git update-index deep && 893 run_on_all test_must_fail git update-index folder1 && 894 test_must_fail git -C full-checkout update-index --remove folder1 && 895 test_sparse_match git update-index --remove folder1 && 896 test_all_match git status --porcelain=v2 897' 898 899test_expect_success 'update-index --again file outside sparse definition' ' 900 init_repos && 901 902 test_all_match git checkout -b test-reupdate && 903 904 # Update HEAD without modifying the index to introduce a difference in 905 # folder1/a 906 test_sparse_match git reset --soft update-folder1 && 907 908 # Because folder1/a differs in the index vs HEAD, 909 # `git update-index --no-skip-worktree --again` will effectively perform 910 # `git update-index --no-skip-worktree folder1/a` and remove the skip-worktree 911 # flag from folder1/a 912 test_sparse_match git update-index --no-skip-worktree --again && 913 test_sparse_match git status --porcelain=v2 && 914 915 cat >expect <<-EOF && 916 D folder1/a 917 EOF 918 test_sparse_match git diff --name-status && 919 test_cmp expect sparse-checkout-out 920' 921 922test_expect_success 'update-index --cacheinfo' ' 923 init_repos && 924 925 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) && 926 folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) && 927 folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) && 928 929 test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a && 930 test_all_match git status --porcelain=v2 && 931 932 # Cannot add sparse directory, even in sparse index case 933 test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ && 934 935 # Sparse match only: the new outside-of-cone entry is added *without* skip-worktree, 936 # so `git status` reports it as "deleted" in the worktree 937 test_sparse_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a && 938 test_sparse_match git status --porcelain=v2 && 939 cat >expect <<-EOF && 940 MD folder1/a 941 EOF 942 test_sparse_match git status --short -- folder1/a && 943 test_cmp expect sparse-checkout-out && 944 945 # To return folder1/a to "normal" for a sparse checkout (ignored & 946 # outside-of-cone), add the skip-worktree flag. 947 test_sparse_match git update-index --skip-worktree folder1/a && 948 cat >expect <<-EOF && 949 S folder1/a 950 EOF 951 test_sparse_match git ls-files -t -- folder1/a && 952 test_cmp expect sparse-checkout-out 953' 954 955for MERGE_TREES in "base HEAD update-folder2" \ 956 "update-folder1 update-folder2" \ 957 "update-folder2" 958do 959 test_expect_success "'read-tree -mu $MERGE_TREES' with files outside sparse definition" ' 960 init_repos && 961 962 # Although the index matches, without --no-sparse-checkout, outside-of- 963 # definition files will not exist on disk for sparse checkouts 964 test_all_match git read-tree -mu $MERGE_TREES && 965 test_all_match git status --porcelain=v2 && 966 test_path_is_missing sparse-checkout/folder2 && 967 test_path_is_missing sparse-index/folder2 && 968 969 test_all_match git read-tree --reset -u HEAD && 970 test_all_match git status --porcelain=v2 && 971 972 test_all_match git read-tree -mu --no-sparse-checkout $MERGE_TREES && 973 test_all_match git status --porcelain=v2 && 974 test_cmp sparse-checkout/folder2/a sparse-index/folder2/a && 975 test_cmp sparse-checkout/folder2/a full-checkout/folder2/a 976 977 ' 978done 979 980test_expect_success 'read-tree --merge with edit/edit conflicts in sparse directories' ' 981 init_repos && 982 983 # Merge of multiple changes to same directory (but not same files) should 984 # succeed 985 test_all_match git read-tree -mu base rename-base update-folder1 && 986 test_all_match git status --porcelain=v2 && 987 988 test_all_match git reset --hard && 989 990 test_all_match git read-tree -mu rename-base update-folder2 && 991 test_all_match git status --porcelain=v2 && 992 993 test_all_match git reset --hard && 994 995 test_all_match test_must_fail git read-tree -mu base update-folder1 rename-out-to-in && 996 test_all_match test_must_fail git read-tree -mu rename-out-to-in update-folder1 997' 998 999test_expect_success 'read-tree --prefix' ' 1000 init_repos && 1001 1002 # If files differing between the index and target <commit-ish> exist 1003 # inside the prefix, `read-tree --prefix` should fail 1004 test_all_match test_must_fail git read-tree --prefix=deep/ deepest && 1005 test_all_match test_must_fail git read-tree --prefix=folder1/ update-folder1 && 1006 1007 # If no differing index entries exist matching the prefix, 1008 # `read-tree --prefix` updates the index successfully 1009 test_all_match git rm -rf deep/deeper1/deepest/ && 1010 test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest && 1011 test_all_match git status --porcelain=v2 && 1012 1013 run_on_all git rm -rf --sparse folder1/ && 1014 test_all_match git read-tree --prefix=folder1/ -u update-folder1 && 1015 test_all_match git status --porcelain=v2 && 1016 1017 test_all_match git rm -rf --sparse folder2/0 && 1018 test_all_match git read-tree --prefix=folder2/0/ -u rename-out-to-out && 1019 test_all_match git status --porcelain=v2 1020' 1021 1022test_expect_success 'read-tree --merge with directory-file conflicts' ' 1023 init_repos && 1024 1025 test_all_match git checkout -b test-branch rename-base && 1026 1027 # Although the index matches, without --no-sparse-checkout, outside-of- 1028 # definition files will not exist on disk for sparse checkouts 1029 test_sparse_match git read-tree -mu rename-out-to-out && 1030 test_sparse_match git status --porcelain=v2 && 1031 test_path_is_missing sparse-checkout/folder2 && 1032 test_path_is_missing sparse-index/folder2 && 1033 1034 test_sparse_match git read-tree --reset -u HEAD && 1035 test_sparse_match git status --porcelain=v2 && 1036 1037 test_sparse_match git read-tree -mu --no-sparse-checkout rename-out-to-out && 1038 test_sparse_match git status --porcelain=v2 && 1039 test_cmp sparse-checkout/folder2/0/1 sparse-index/folder2/0/1 1040' 1041 1042test_expect_success 'merge, cherry-pick, and rebase' ' 1043 init_repos && 1044 1045 for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge" 1046 do 1047 test_all_match git checkout -B temp update-deep && 1048 test_all_match git $OPERATION update-folder1 && 1049 test_all_match git rev-parse HEAD^{tree} && 1050 test_all_match git $OPERATION update-folder2 && 1051 test_all_match git rev-parse HEAD^{tree} || return 1 1052 done 1053' 1054 1055test_expect_success 'merge with conflict outside cone' ' 1056 init_repos && 1057 1058 test_all_match git checkout -b merge-tip merge-left && 1059 test_all_match git status --porcelain=v2 && 1060 test_all_match test_must_fail git merge -m merge merge-right && 1061 test_all_match git status --porcelain=v2 && 1062 1063 # Resolve the conflict in different ways: 1064 # 1. Revert to the base 1065 test_all_match git checkout base -- deep/deeper2/a && 1066 test_all_match git status --porcelain=v2 && 1067 1068 # 2. Add the file with conflict markers 1069 test_sparse_match test_must_fail git add folder1/a && 1070 grep "Disable or modify the sparsity rules" sparse-checkout-err && 1071 test_sparse_unstaged folder1/a && 1072 test_all_match git add --sparse folder1/a && 1073 test_all_match git status --porcelain=v2 && 1074 1075 # 3. Rename the file to another sparse filename and 1076 # accept conflict markers as resolved content. 1077 run_on_all mv folder2/a folder2/z && 1078 test_sparse_match test_must_fail git add folder2 && 1079 grep "Disable or modify the sparsity rules" sparse-checkout-err && 1080 test_sparse_unstaged folder2/z && 1081 test_all_match git add --sparse folder2 && 1082 test_all_match git status --porcelain=v2 && 1083 1084 test_all_match git merge --continue && 1085 test_all_match git status --porcelain=v2 && 1086 test_all_match git rev-parse HEAD^{tree} 1087' 1088 1089test_expect_success 'cherry-pick/rebase with conflict outside cone' ' 1090 init_repos && 1091 1092 for OPERATION in cherry-pick rebase 1093 do 1094 test_all_match git checkout -B tip && 1095 test_all_match git reset --hard merge-left && 1096 test_all_match git status --porcelain=v2 && 1097 test_all_match test_must_fail git $OPERATION merge-right && 1098 test_all_match git status --porcelain=v2 && 1099 1100 # Resolve the conflict in different ways: 1101 # 1. Revert to the base 1102 test_all_match git checkout base -- deep/deeper2/a && 1103 test_all_match git status --porcelain=v2 && 1104 1105 # 2. Add the file with conflict markers 1106 # NEEDSWORK: Even though the merge conflict removed the 1107 # SKIP_WORKTREE bit from the index entry for folder1/a, we should 1108 # warn that this is a problematic add. 1109 test_sparse_match test_must_fail git add folder1/a && 1110 grep "Disable or modify the sparsity rules" sparse-checkout-err && 1111 test_sparse_unstaged folder1/a && 1112 test_all_match git add --sparse folder1/a && 1113 test_all_match git status --porcelain=v2 && 1114 1115 # 3. Rename the file to another sparse filename and 1116 # accept conflict markers as resolved content. 1117 # NEEDSWORK: This mode now fails, because folder2/z is 1118 # outside of the sparse-checkout cone and does not match an 1119 # existing index entry with the SKIP_WORKTREE bit cleared. 1120 run_on_all mv folder2/a folder2/z && 1121 test_sparse_match test_must_fail git add folder2 && 1122 grep "Disable or modify the sparsity rules" sparse-checkout-err && 1123 test_sparse_unstaged folder2/z && 1124 test_all_match git add --sparse folder2 && 1125 test_all_match git status --porcelain=v2 && 1126 1127 test_all_match git $OPERATION --continue && 1128 test_all_match git status --porcelain=v2 && 1129 test_all_match git rev-parse HEAD^{tree} || return 1 1130 done 1131' 1132 1133test_expect_success 'merge with outside renames' ' 1134 init_repos && 1135 1136 for type in out-to-out out-to-in in-to-out 1137 do 1138 test_all_match git reset --hard && 1139 test_all_match git checkout -f -b merge-$type update-deep && 1140 test_all_match git merge -m "$type" rename-$type && 1141 test_all_match git rev-parse HEAD^{tree} || return 1 1142 done 1143' 1144 1145# Sparse-index fails to convert the index in the 1146# final 'git cherry-pick' command. 1147test_expect_success 'cherry-pick with conflicts' ' 1148 init_repos && 1149 1150 write_script edit-conflict <<-\EOF && 1151 echo $1 >conflict 1152 EOF 1153 1154 test_all_match git checkout -b to-cherry-pick && 1155 run_on_all ../edit-conflict ABC && 1156 test_all_match git add conflict && 1157 test_all_match git commit -m "conflict to pick" && 1158 1159 test_all_match git checkout -B base HEAD~1 && 1160 run_on_all ../edit-conflict DEF && 1161 test_all_match git add conflict && 1162 test_all_match git commit -m "conflict in base" && 1163 1164 test_all_match test_must_fail git cherry-pick to-cherry-pick 1165' 1166 1167test_expect_success 'stash' ' 1168 init_repos && 1169 1170 write_script edit-contents <<-\EOF && 1171 echo text >>$1 1172 EOF 1173 1174 # Stash a sparse directory (folder1) 1175 test_all_match git checkout -b test-branch rename-base && 1176 test_all_match git reset --soft rename-out-to-out && 1177 test_all_match git stash && 1178 test_all_match git status --porcelain=v2 && 1179 1180 # Apply the sparse directory stash without reinstating the index 1181 test_all_match git stash apply -q && 1182 test_all_match git status --porcelain=v2 && 1183 1184 # Reset to state where stash can be applied 1185 test_sparse_match git sparse-checkout reapply && 1186 test_all_match git reset --hard rename-out-to-out && 1187 1188 # Apply the sparse directory stash *with* reinstating the index 1189 test_all_match git stash apply --index -q && 1190 test_all_match git status --porcelain=v2 && 1191 1192 # Reset to state where we will get a conflict applying the stash 1193 test_sparse_match git sparse-checkout reapply && 1194 test_all_match git reset --hard update-folder1 && 1195 1196 # Apply the sparse directory stash with conflicts 1197 test_all_match test_must_fail git stash apply --index -q && 1198 test_all_match test_must_fail git stash apply -q && 1199 test_all_match git status --porcelain=v2 && 1200 1201 # Reset to base branch 1202 test_sparse_match git sparse-checkout reapply && 1203 test_all_match git reset --hard base && 1204 1205 # Stash & unstash an untracked file outside of the sparse checkout 1206 # definition. 1207 run_on_sparse mkdir -p folder1 && 1208 run_on_all ../edit-contents folder1/new && 1209 test_all_match git stash -u && 1210 test_all_match git status --porcelain=v2 && 1211 1212 test_all_match git stash pop -q && 1213 test_all_match git status --porcelain=v2 1214' 1215 1216test_expect_success 'checkout-index inside sparse definition' ' 1217 init_repos && 1218 1219 run_on_all rm -f deep/a && 1220 test_all_match git checkout-index -- deep/a && 1221 test_all_match git status --porcelain=v2 && 1222 1223 echo test >>new-a && 1224 run_on_all cp ../new-a a && 1225 test_all_match test_must_fail git checkout-index -- a && 1226 test_all_match git checkout-index -f -- a && 1227 test_all_match git status --porcelain=v2 1228' 1229 1230test_expect_success 'checkout-index outside sparse definition' ' 1231 init_repos && 1232 1233 # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger 1234 # an error 1235 test_sparse_match test_must_fail git checkout-index -- folder1/a && 1236 test_grep "folder1/a has skip-worktree enabled" sparse-checkout-err && 1237 test_path_is_missing folder1/a && 1238 1239 # With --ignore-skip-worktree-bits, outside-of-cone files are checked out 1240 test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a && 1241 test_cmp sparse-checkout/folder1/a sparse-index/folder1/a && 1242 test_cmp sparse-checkout/folder1/a full-checkout/folder1/a && 1243 1244 run_on_sparse rm -rf folder1 && 1245 echo test >new-a && 1246 run_on_sparse mkdir -p folder1 && 1247 run_on_all cp ../new-a folder1/a && 1248 1249 test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a && 1250 test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a && 1251 test_cmp sparse-checkout/folder1/a sparse-index/folder1/a && 1252 test_cmp sparse-checkout/folder1/a full-checkout/folder1/a 1253' 1254 1255test_expect_success 'checkout-index with folders' ' 1256 init_repos && 1257 1258 # Inside checkout definition 1259 test_all_match test_must_fail git checkout-index -f -- deep/ && 1260 1261 # Outside checkout definition 1262 # Note: although all tests fail (as expected), the messaging differs. For 1263 # non-sparse index checkouts, the error is that the "file" does not appear 1264 # in the index; for sparse checkouts, the error is explicitly that the 1265 # entry is a sparse directory. 1266 run_on_all test_must_fail git checkout-index -f -- folder1/ && 1267 test_cmp full-checkout-err sparse-checkout-err && 1268 ! test_cmp full-checkout-err sparse-index-err && 1269 grep "is a sparse directory" sparse-index-err 1270' 1271 1272test_expect_success 'checkout-index --all' ' 1273 init_repos && 1274 1275 test_all_match git checkout-index --all && 1276 test_sparse_match test_path_is_missing folder1 && 1277 1278 # --ignore-skip-worktree-bits will cause `skip-worktree` files to be 1279 # checked out, causing the outside-of-cone `folder1` to exist on-disk 1280 test_all_match git checkout-index --ignore-skip-worktree-bits --all && 1281 test_all_match test_path_exists folder1 1282' 1283 1284test_expect_success 'clean' ' 1285 init_repos && 1286 1287 echo bogus >>.gitignore && 1288 run_on_all cp ../.gitignore . && 1289 test_all_match git add .gitignore && 1290 test_all_match git commit -m "ignore bogus files" && 1291 1292 run_on_sparse mkdir folder1 && 1293 run_on_all mkdir -p deep/untracked-deep && 1294 run_on_all touch folder1/bogus && 1295 run_on_all touch folder1/untracked && 1296 run_on_all touch deep/untracked-deep/bogus && 1297 run_on_all touch deep/untracked-deep/untracked && 1298 1299 test_all_match git status --porcelain=v2 && 1300 test_all_match git clean -f && 1301 test_all_match git status --porcelain=v2 && 1302 test_sparse_match ls && 1303 test_sparse_match ls folder1 && 1304 run_on_all test_path_exists folder1/bogus && 1305 run_on_all test_path_is_missing folder1/untracked && 1306 run_on_all test_path_exists deep/untracked-deep/bogus && 1307 run_on_all test_path_exists deep/untracked-deep/untracked && 1308 1309 test_all_match git clean -fd && 1310 test_all_match git status --porcelain=v2 && 1311 test_sparse_match ls && 1312 test_sparse_match ls folder1 && 1313 run_on_all test_path_exists folder1/bogus && 1314 run_on_all test_path_exists deep/untracked-deep/bogus && 1315 run_on_all test_path_is_missing deep/untracked-deep/untracked && 1316 1317 test_all_match git clean -xf && 1318 test_all_match git status --porcelain=v2 && 1319 test_sparse_match ls && 1320 test_sparse_match ls folder1 && 1321 run_on_all test_path_is_missing folder1/bogus && 1322 run_on_all test_path_exists deep/untracked-deep/bogus && 1323 1324 test_all_match git clean -xdf && 1325 test_all_match git status --porcelain=v2 && 1326 test_sparse_match ls && 1327 test_sparse_match ls folder1 && 1328 run_on_all test_path_is_missing deep/untracked-deep/bogus && 1329 1330 test_sparse_match test_path_is_dir folder1 1331' 1332 1333for builtin in show rev-parse 1334do 1335 test_expect_success "$builtin (cached blobs/trees)" " 1336 init_repos && 1337 1338 test_all_match git $builtin :a && 1339 test_all_match git $builtin :deep/a && 1340 test_sparse_match git $builtin :folder1/a && 1341 1342 # The error message differs depending on whether 1343 # the directory exists in the worktree. 1344 test_all_match test_must_fail git $builtin :deep/ && 1345 test_must_fail git -C full-checkout $builtin :folder1/ && 1346 test_sparse_match test_must_fail git $builtin :folder1/ && 1347 1348 # Change the sparse cone for an extra case: 1349 run_on_sparse git sparse-checkout set deep/deeper1 && 1350 1351 # deep/deeper2 is a sparse directory in the sparse index. 1352 test_sparse_match test_must_fail git $builtin :deep/deeper2/ && 1353 1354 # deep/deeper2/deepest is not in the sparse index, but 1355 # will trigger an index expansion. 1356 test_sparse_match test_must_fail git $builtin :deep/deeper2/deepest/ 1357 " 1358done 1359 1360test_expect_success 'submodule handling' ' 1361 init_repos && 1362 1363 test_sparse_match git sparse-checkout add modules && 1364 test_all_match mkdir modules && 1365 test_all_match touch modules/a && 1366 test_all_match git add modules && 1367 test_all_match git commit -m "add modules directory" && 1368 1369 test_config_global protocol.file.allow always && 1370 1371 run_on_all git submodule add "$(pwd)/initial-repo" modules/sub && 1372 test_all_match git commit -m "add submodule" && 1373 1374 # having a submodule prevents "modules" from collapse 1375 test_sparse_match git sparse-checkout set deep/deeper1 && 1376 git -C sparse-index ls-files --sparse --stage >cache && 1377 grep "100644 .* modules/a" cache && 1378 grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache 1379' 1380 1381test_expect_success 'git apply functionality' ' 1382 init_repos && 1383 1384 test_all_match git checkout base && 1385 1386 git -C full-checkout diff base..merge-right -- deep >patch-in-sparse && 1387 git -C full-checkout diff base..merge-right -- folder2 >patch-outside && 1388 1389 # Apply a patch to a file inside the sparse definition 1390 test_all_match git apply --index --stat ../patch-in-sparse && 1391 test_all_match git status --porcelain=v2 && 1392 1393 # Apply a patch to a file outside the sparse definition 1394 test_sparse_match test_must_fail git apply ../patch-outside && 1395 grep "No such file or directory" sparse-checkout-err && 1396 1397 # But it works with --index and --cached 1398 test_all_match git apply --index --stat ../patch-outside && 1399 test_all_match git status --porcelain=v2 && 1400 test_all_match git reset --hard && 1401 test_all_match git apply --cached --stat ../patch-outside && 1402 test_all_match git status --porcelain=v2 1403' 1404 1405# When working with a sparse index, some commands will need to expand the 1406# index to operate properly. If those commands also write the index back 1407# to disk, they need to convert the index to sparse before writing. 1408# This test verifies that both of these events are logged in trace2 logs. 1409test_expect_success 'sparse-index is expanded and converted back' ' 1410 init_repos && 1411 1412 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ 1413 git -C sparse-index reset -- folder1/a && 1414 test_region index convert_to_sparse trace2.txt && 1415 test_region index ensure_full_index trace2.txt && 1416 1417 # ls-files expands on read, but does not write. 1418 rm trace2.txt && 1419 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ 1420 git -C sparse-index ls-files && 1421 test_region index ensure_full_index trace2.txt 1422' 1423 1424test_expect_success 'index.sparse disabled inline uses full index' ' 1425 init_repos && 1426 1427 # When index.sparse is disabled inline with `git status`, the 1428 # index is expanded at the beginning of the execution then never 1429 # converted back to sparse. It is then written to disk as a full index. 1430 rm -f trace2.txt && 1431 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ 1432 git -C sparse-index -c index.sparse=false status && 1433 ! test_region index convert_to_sparse trace2.txt && 1434 test_region index ensure_full_index trace2.txt && 1435 1436 # Since index.sparse is set to true at a repo level, the index 1437 # is converted from full to sparse when read, then never expanded 1438 # over the course of `git status`. It is written to disk as a sparse 1439 # index. 1440 rm -f trace2.txt && 1441 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ 1442 git -C sparse-index status && 1443 test_region index convert_to_sparse trace2.txt && 1444 ! test_region index ensure_full_index trace2.txt && 1445 1446 # Now that the index has been written to disk as sparse, it is not 1447 # converted to sparse (or expanded to full) when read by `git status`. 1448 rm -f trace2.txt && 1449 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ 1450 git -C sparse-index status && 1451 ! test_region index convert_to_sparse trace2.txt && 1452 ! test_region index ensure_full_index trace2.txt 1453' 1454 1455run_sparse_index_trace2 () { 1456 rm -f trace2.txt && 1457 if test -z "$WITHOUT_UNTRACKED_TXT" 1458 then 1459 echo >>sparse-index/untracked.txt 1460 fi && 1461 1462 if test "$1" = "!" 1463 then 1464 shift && 1465 test_must_fail env \ 1466 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ 1467 git -C sparse-index "$@" \ 1468 >sparse-index-out \ 1469 2>sparse-index-error || return 1 1470 else 1471 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ 1472 git -C sparse-index "$@" \ 1473 >sparse-index-out \ 1474 2>sparse-index-error || return 1 1475 fi 1476} 1477 1478ensure_expanded () { 1479 run_sparse_index_trace2 "$@" && 1480 test_region index ensure_full_index trace2.txt 1481} 1482 1483ensure_not_expanded () { 1484 run_sparse_index_trace2 "$@" && 1485 test_region ! index ensure_full_index trace2.txt 1486} 1487 1488test_expect_success 'sparse-index is not expanded' ' 1489 init_repos && 1490 1491 ensure_not_expanded status && 1492 ensure_not_expanded ls-files --sparse && 1493 ensure_not_expanded commit --allow-empty -m empty && 1494 echo >>sparse-index/a && 1495 ensure_not_expanded commit -a -m a && 1496 echo >>sparse-index/a && 1497 ensure_not_expanded commit --include a -m a && 1498 echo >>sparse-index/deep/deeper1/a && 1499 ensure_not_expanded commit --include deep/deeper1/a -m deeper && 1500 ensure_not_expanded checkout rename-out-to-out && 1501 ensure_not_expanded checkout - && 1502 ensure_not_expanded switch rename-out-to-out && 1503 ensure_not_expanded switch - && 1504 ensure_not_expanded reset --hard && 1505 ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 && 1506 ensure_not_expanded reset --hard && 1507 ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 && 1508 1509 ensure_not_expanded ls-files deep/deeper1 && 1510 1511 echo >>sparse-index/README.md && 1512 ensure_not_expanded add -A && 1513 echo >>sparse-index/extra.txt && 1514 ensure_not_expanded add extra.txt && 1515 echo >>sparse-index/untracked.txt && 1516 ensure_not_expanded add . && 1517 1518 ensure_not_expanded checkout-index -f a && 1519 ensure_not_expanded checkout-index -f --all && 1520 for ref in update-deep update-folder1 update-folder2 update-deep 1521 do 1522 echo >>sparse-index/README.md && 1523 ensure_not_expanded reset --hard $ref || return 1 1524 done && 1525 1526 ensure_not_expanded reset --mixed base && 1527 ensure_not_expanded reset --hard update-deep && 1528 ensure_not_expanded reset --keep base && 1529 ensure_not_expanded reset --merge update-deep && 1530 ensure_not_expanded reset --hard && 1531 1532 ensure_not_expanded reset base -- deep/a && 1533 ensure_not_expanded reset base -- nonexistent-file && 1534 ensure_not_expanded reset deepest -- deep && 1535 1536 # Although folder1 is outside the sparse definition, it exists as a 1537 # directory entry in the index, so the pathspec will not force the 1538 # index to be expanded. 1539 ensure_not_expanded reset deepest -- folder1 && 1540 ensure_not_expanded reset deepest -- folder1/ && 1541 1542 # Wildcard identifies only in-cone files, no index expansion 1543 ensure_not_expanded reset deepest -- deep/\* && 1544 1545 # Wildcard identifies only full sparse directories, no index expansion 1546 ensure_not_expanded reset deepest -- folder\* && 1547 1548 ensure_not_expanded clean -fd && 1549 1550 ensure_not_expanded checkout -f update-deep && 1551 test_config -C sparse-index pull.twohead ort && 1552 ( 1553 for OPERATION in "merge -m merge" cherry-pick rebase 1554 do 1555 ensure_not_expanded merge -m merge update-folder1 && 1556 ensure_not_expanded merge -m merge update-folder2 || return 1 1557 done 1558 ) 1559' 1560 1561test_expect_success 'sparse-index is not expanded: merge conflict in cone' ' 1562 init_repos && 1563 1564 for side in right left 1565 do 1566 git -C sparse-index checkout -b expand-$side base && 1567 echo $side >sparse-index/deep/a && 1568 git -C sparse-index commit -a -m "$side" || return 1 1569 done && 1570 1571 ( 1572 git -C sparse-index config pull.twohead ort && 1573 ensure_not_expanded ! merge -m merged expand-right 1574 ) 1575' 1576 1577test_expect_success 'sparse-index is not expanded: stash' ' 1578 init_repos && 1579 1580 echo >>sparse-index/a && 1581 ensure_not_expanded stash && 1582 ensure_not_expanded stash list && 1583 ensure_not_expanded stash show stash@{0} && 1584 ensure_not_expanded stash apply stash@{0} && 1585 ensure_not_expanded stash drop stash@{0} && 1586 1587 echo >>sparse-index/deep/new && 1588 ensure_not_expanded stash -u && 1589 ( 1590 WITHOUT_UNTRACKED_TXT=1 && 1591 ensure_not_expanded stash pop 1592 ) && 1593 1594 ensure_not_expanded stash create && 1595 oid=$(git -C sparse-index stash create) && 1596 ensure_not_expanded stash store -m "test" $oid && 1597 ensure_not_expanded reset --hard && 1598 ensure_not_expanded stash pop 1599' 1600 1601test_expect_success 'describe tested on all' ' 1602 init_repos && 1603 1604 # Add tag to be read by describe 1605 1606 run_on_all git tag -a v1.0 -m "Version 1" && 1607 test_all_match git describe --dirty && 1608 run_on_all rm g && 1609 test_all_match git describe --dirty 1610' 1611 1612test_expect_success 'ls-files filtering and expansion' ' 1613 init_repos && 1614 1615 # This filtering will hit a sparse directory midway 1616 # through the iteration. 1617 test_all_match git ls-files deep && 1618 1619 # This pathspec will filter the index to only a sparse 1620 # directory. 1621 test_all_match git ls-files folder1 1622' 1623 1624test_expect_success 'sparse-index is not expanded: describe' ' 1625 init_repos && 1626 1627 # Add tag to be read by describe 1628 1629 git -C sparse-index tag -a v1.0 -m "Version 1" && 1630 1631 ensure_not_expanded describe --dirty && 1632 echo "test" >>sparse-index/g && 1633 ensure_not_expanded describe --dirty && 1634 ensure_not_expanded describe 1635' 1636 1637test_expect_success 'sparse index is not expanded: diff and diff-index' ' 1638 init_repos && 1639 1640 write_script edit-contents <<-\EOF && 1641 echo text >>$1 1642 EOF 1643 1644 # Add file within cone 1645 test_sparse_match git sparse-checkout set deep && 1646 run_on_all ../edit-contents deep/testfile && 1647 test_all_match git add deep/testfile && 1648 run_on_all ../edit-contents deep/testfile && 1649 1650 test_all_match git diff && 1651 test_all_match git diff --cached && 1652 ensure_not_expanded diff && 1653 ensure_not_expanded diff --cached && 1654 ensure_not_expanded diff-index --cached HEAD && 1655 1656 # Add file outside cone 1657 test_all_match git reset --hard && 1658 run_on_all mkdir newdirectory && 1659 run_on_all ../edit-contents newdirectory/testfile && 1660 test_sparse_match git sparse-checkout set newdirectory && 1661 test_all_match git add newdirectory/testfile && 1662 run_on_all ../edit-contents newdirectory/testfile && 1663 test_sparse_match git sparse-checkout set && 1664 1665 test_all_match git diff && 1666 test_all_match git diff --cached && 1667 ensure_not_expanded diff && 1668 ensure_not_expanded diff --cached && 1669 ensure_not_expanded diff-index --cached HEAD && 1670 1671 # Merge conflict outside cone 1672 # The sparse checkout will report a warning that is not in the 1673 # full checkout, so we use `run_on_all` instead of 1674 # `test_all_match` 1675 run_on_all git reset --hard && 1676 test_all_match git checkout merge-left && 1677 test_all_match test_must_fail git merge merge-right && 1678 1679 test_all_match git diff && 1680 test_all_match git diff --cached && 1681 ensure_not_expanded diff && 1682 ensure_not_expanded diff --cached && 1683 ensure_not_expanded diff-index --cached HEAD 1684' 1685 1686test_expect_success 'sparse index is not expanded: show and rev-parse' ' 1687 init_repos && 1688 1689 ensure_not_expanded show :a && 1690 ensure_not_expanded show :deep/a && 1691 ensure_not_expanded rev-parse :a && 1692 ensure_not_expanded rev-parse :deep/a 1693' 1694 1695test_expect_success 'sparse index is not expanded: update-index' ' 1696 init_repos && 1697 1698 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) && 1699 ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a && 1700 1701 echo "test" >sparse-index/README.md && 1702 echo "test2" >sparse-index/a && 1703 rm -f sparse-index/deep/a && 1704 1705 ensure_not_expanded update-index --add README.md && 1706 ensure_not_expanded update-index a && 1707 ensure_not_expanded update-index --remove deep/a && 1708 1709 ensure_not_expanded reset --soft update-deep && 1710 ensure_not_expanded update-index --add --remove --again 1711' 1712 1713test_expect_success 'sparse index is not expanded: blame' ' 1714 init_repos && 1715 1716 for file in a \ 1717 deep/a \ 1718 deep/deeper1/a \ 1719 deep/deeper1/deepest/a 1720 do 1721 ensure_not_expanded blame $file || return 1 1722 done 1723' 1724 1725test_expect_success 'sparse index is not expanded: fetch/pull' ' 1726 init_repos && 1727 1728 git -C sparse-index remote add full "file://$(pwd)/full-checkout" && 1729 ensure_not_expanded fetch full && 1730 git -C full-checkout commit --allow-empty -m "for pull merge" && 1731 git -C sparse-index commit --allow-empty -m "for pull merge" && 1732 ensure_not_expanded pull full base 1733' 1734 1735test_expect_success 'sparse index is not expanded: read-tree' ' 1736 init_repos && 1737 1738 ensure_not_expanded checkout -b test-branch update-folder1 && 1739 for MERGE_TREES in "base HEAD update-folder2" \ 1740 "base HEAD rename-base" \ 1741 "base update-folder2" \ 1742 "base rename-base" \ 1743 "update-folder2" 1744 do 1745 ensure_not_expanded read-tree -mu $MERGE_TREES && 1746 ensure_not_expanded reset --hard || return 1 1747 done && 1748 1749 rm -rf sparse-index/deep/deeper2 && 1750 ensure_not_expanded add . && 1751 ensure_not_expanded commit -m "test" && 1752 1753 ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest 1754' 1755 1756test_expect_success 'ls-files' ' 1757 init_repos && 1758 1759 # Use a smaller sparse-checkout for reduced output 1760 test_sparse_match git sparse-checkout set && 1761 1762 # Behavior agrees by default. Sparse index is expanded. 1763 test_all_match git ls-files && 1764 1765 # With --sparse, the sparse index data changes behavior. 1766 git -C sparse-index ls-files --sparse >actual && 1767 1768 cat >expect <<-\EOF && 1769 a 1770 before/ 1771 deep/ 1772 e 1773 folder1- 1774 folder1.x 1775 folder1/ 1776 folder10 1777 folder2/ 1778 g 1779 x/ 1780 z 1781 EOF 1782 1783 test_cmp expect actual && 1784 1785 # With --sparse and no sparse index, nothing changes. 1786 git -C sparse-checkout ls-files >dense && 1787 git -C sparse-checkout ls-files --sparse >sparse && 1788 test_cmp dense sparse && 1789 1790 # Set up a strange condition of having a file edit 1791 # outside of the sparse-checkout cone. We want to verify 1792 # that all modes handle this the same, and detect the 1793 # modification. 1794 write_script edit-content <<-\EOF && 1795 mkdir -p folder1 && 1796 echo content >>folder1/a 1797 EOF 1798 run_on_all ../edit-content && 1799 1800 test_all_match git ls-files --modified && 1801 1802 git -C sparse-index ls-files --sparse --modified >sparse-index-out && 1803 cat >expect <<-\EOF && 1804 folder1/a 1805 EOF 1806 test_cmp expect sparse-index-out && 1807 1808 # Add folder1 to the sparse-checkout cone and 1809 # check that ls-files shows the expanded files. 1810 test_sparse_match git sparse-checkout add folder1 && 1811 test_all_match git ls-files --modified && 1812 1813 test_all_match git ls-files && 1814 git -C sparse-index ls-files --sparse >actual && 1815 1816 cat >expect <<-\EOF && 1817 a 1818 before/ 1819 deep/ 1820 e 1821 folder1- 1822 folder1.x 1823 folder1/0/0/0 1824 folder1/0/1 1825 folder1/a 1826 folder10 1827 folder2/ 1828 g 1829 x/ 1830 z 1831 EOF 1832 1833 test_cmp expect actual && 1834 1835 # Double-check index expansion is avoided 1836 ensure_not_expanded ls-files --sparse 1837' 1838 1839test_expect_success 'sparse index is not expanded: sparse-checkout' ' 1840 init_repos && 1841 1842 ensure_not_expanded sparse-checkout set deep/deeper2 && 1843 ensure_not_expanded sparse-checkout set deep/deeper1 && 1844 ensure_not_expanded sparse-checkout set deep && 1845 ensure_not_expanded sparse-checkout add folder1 && 1846 ensure_not_expanded sparse-checkout set deep/deeper1 && 1847 ensure_not_expanded sparse-checkout set folder2 && 1848 1849 # Demonstrate that the checks that "folder1/a" is a file 1850 # do not cause a sparse-index expansion (since it is in the 1851 # sparse-checkout cone). 1852 echo >>sparse-index/folder2/a && 1853 git -C sparse-index add folder2/a && 1854 1855 ensure_not_expanded sparse-checkout add folder1 && 1856 1857 # Skip checks here, since deep/deeper1 is inside a sparse directory 1858 # that must be expanded to check whether `deep/deeper1` is a file 1859 # or not. 1860 ensure_not_expanded sparse-checkout set --skip-checks deep/deeper1 && 1861 ensure_not_expanded sparse-checkout set 1862' 1863 1864# NEEDSWORK: a sparse-checkout behaves differently from a full checkout 1865# in this scenario, but it shouldn't. 1866test_expect_success 'reset mixed and checkout orphan' ' 1867 init_repos && 1868 1869 test_all_match git checkout rename-out-to-in && 1870 1871 # Sparse checkouts do not agree with full checkouts about 1872 # how to report a directory/file conflict during a reset. 1873 # This command would fail with test_all_match because the 1874 # full checkout reports "T folder1/0/1" while a sparse 1875 # checkout reports "D folder1/0/1". This matches because 1876 # the sparse checkouts skip "adding" the other side of 1877 # the conflict. 1878 test_sparse_match git reset --mixed HEAD~1 && 1879 test_sparse_match git ls-files --stage && 1880 test_sparse_match git status --porcelain=v2 && 1881 1882 # At this point, sparse-checkouts behave differently 1883 # from the full-checkout. 1884 test_sparse_match git checkout --orphan new-branch && 1885 test_sparse_match git ls-files --stage && 1886 test_sparse_match git status --porcelain=v2 1887' 1888 1889test_expect_success 'add everything with deep new file' ' 1890 init_repos && 1891 1892 run_on_sparse git sparse-checkout set deep/deeper1/deepest && 1893 1894 run_on_all touch deep/deeper1/x && 1895 test_all_match git add . && 1896 test_all_match git status --porcelain=v2 1897' 1898 1899# NEEDSWORK: 'git checkout' behaves incorrectly in the case of 1900# directory/file conflicts, even without sparse-checkout. Use this 1901# test only as a documentation of the incorrect behavior, not a 1902# measure of how it _should_ behave. 1903test_expect_success 'checkout behaves oddly with df-conflict-1' ' 1904 init_repos && 1905 1906 test_sparse_match git sparse-checkout disable && 1907 1908 write_script edit-content <<-\EOF && 1909 echo content >>folder1/larger-content 1910 git add folder1 1911 EOF 1912 1913 run_on_all ../edit-content && 1914 test_all_match git status --porcelain=v2 && 1915 1916 git -C sparse-checkout sparse-checkout init --cone && 1917 git -C sparse-index sparse-checkout init --cone --sparse-index && 1918 1919 test_all_match git status --porcelain=v2 && 1920 1921 # This checkout command should fail, because we have a staged 1922 # change to folder1/larger-content, but the destination changes 1923 # folder1 to a file. 1924 git -C full-checkout checkout df-conflict-1 \ 1925 1>full-checkout-out \ 1926 2>full-checkout-err && 1927 git -C sparse-checkout checkout df-conflict-1 \ 1928 1>sparse-checkout-out \ 1929 2>sparse-checkout-err && 1930 git -C sparse-index checkout df-conflict-1 \ 1931 1>sparse-index-out \ 1932 2>sparse-index-err && 1933 1934 # Instead, the checkout deletes the folder1 file and adds the 1935 # folder1/larger-content file, leaving all other paths that were 1936 # in folder1/ as deleted (without any warning). 1937 cat >expect <<-EOF && 1938 D folder1 1939 A folder1/larger-content 1940 EOF 1941 test_cmp expect full-checkout-out && 1942 test_cmp expect sparse-checkout-out && 1943 1944 # The sparse-index reports no output 1945 test_must_be_empty sparse-index-out && 1946 1947 # stderr: Switched to branch df-conflict-1 1948 test_cmp full-checkout-err sparse-checkout-err && 1949 test_cmp full-checkout-err sparse-checkout-err 1950' 1951 1952# NEEDSWORK: 'git checkout' behaves incorrectly in the case of 1953# directory/file conflicts, even without sparse-checkout. Use this 1954# test only as a documentation of the incorrect behavior, not a 1955# measure of how it _should_ behave. 1956test_expect_success 'checkout behaves oddly with df-conflict-2' ' 1957 init_repos && 1958 1959 test_sparse_match git sparse-checkout disable && 1960 1961 write_script edit-content <<-\EOF && 1962 echo content >>folder2/larger-content 1963 git add folder2 1964 EOF 1965 1966 run_on_all ../edit-content && 1967 test_all_match git status --porcelain=v2 && 1968 1969 git -C sparse-checkout sparse-checkout init --cone && 1970 git -C sparse-index sparse-checkout init --cone --sparse-index && 1971 1972 test_all_match git status --porcelain=v2 && 1973 1974 # This checkout command should fail, because we have a staged 1975 # change to folder1/larger-content, but the destination changes 1976 # folder1 to a file. 1977 git -C full-checkout checkout df-conflict-2 \ 1978 1>full-checkout-out \ 1979 2>full-checkout-err && 1980 git -C sparse-checkout checkout df-conflict-2 \ 1981 1>sparse-checkout-out \ 1982 2>sparse-checkout-err && 1983 git -C sparse-index checkout df-conflict-2 \ 1984 1>sparse-index-out \ 1985 2>sparse-index-err && 1986 1987 # The full checkout deviates from the df-conflict-1 case here! 1988 # It drops the change to folder1/larger-content and leaves the 1989 # folder1 path as-is on disk. The sparse-index behaves the same. 1990 test_must_be_empty full-checkout-out && 1991 test_must_be_empty sparse-index-out && 1992 1993 # In the sparse-checkout case, the checkout deletes the folder1 1994 # file and adds the folder1/larger-content file, leaving all other 1995 # paths that were in folder1/ as deleted (without any warning). 1996 cat >expect <<-EOF && 1997 D folder2 1998 A folder2/larger-content 1999 EOF 2000 test_cmp expect sparse-checkout-out && 2001 2002 # Switched to branch df-conflict-1 2003 test_cmp full-checkout-err sparse-checkout-err && 2004 test_cmp full-checkout-err sparse-index-err 2005' 2006 2007test_expect_success 'mv directory from out-of-cone to in-cone' ' 2008 init_repos && 2009 2010 # <source> as a sparse directory (or SKIP_WORKTREE_DIR without enabling 2011 # sparse index). 2012 test_all_match git mv --sparse folder1 deep && 2013 test_all_match git status --porcelain=v2 && 2014 test_sparse_match git ls-files -t && 2015 git -C sparse-checkout ls-files -t >actual && 2016 grep -e "H deep/folder1/0/0/0" actual && 2017 grep -e "H deep/folder1/0/1" actual && 2018 grep -e "H deep/folder1/a" actual && 2019 2020 test_all_match git reset --hard && 2021 2022 # <source> as a directory deeper than sparse index boundary (where 2023 # sparse index will expand). 2024 test_sparse_match git mv --sparse folder1/0 deep && 2025 test_sparse_match git status --porcelain=v2 && 2026 test_sparse_match git ls-files -t && 2027 git -C sparse-checkout ls-files -t >actual && 2028 grep -e "H deep/0/0/0" actual && 2029 grep -e "H deep/0/1" actual 2030' 2031 2032test_expect_success 'rm pathspec inside sparse definition' ' 2033 init_repos && 2034 2035 test_all_match git rm deep/a && 2036 test_all_match git status --porcelain=v2 && 2037 2038 # test wildcard 2039 run_on_all git reset --hard && 2040 test_all_match git rm deep/* && 2041 test_all_match git status --porcelain=v2 && 2042 2043 # test recursive rm 2044 run_on_all git reset --hard && 2045 test_all_match git rm -r deep && 2046 test_all_match git status --porcelain=v2 2047' 2048 2049test_expect_success 'rm pathspec outside sparse definition' ' 2050 init_repos && 2051 2052 for file in folder1/a folder1/0/1 2053 do 2054 test_sparse_match test_must_fail git rm $file && 2055 test_sparse_match test_must_fail git rm --cached $file && 2056 test_sparse_match git rm --sparse $file && 2057 test_sparse_match git status --porcelain=v2 || return 1 2058 done && 2059 2060 cat >folder1-full <<-EOF && 2061 rm ${SQ}folder1/0/0/0${SQ} 2062 rm ${SQ}folder1/0/1${SQ} 2063 rm ${SQ}folder1/a${SQ} 2064 EOF 2065 2066 cat >folder1-sparse <<-EOF && 2067 rm ${SQ}folder1/${SQ} 2068 EOF 2069 2070 # test wildcard 2071 run_on_sparse git reset --hard && 2072 run_on_sparse git sparse-checkout reapply && 2073 test_sparse_match test_must_fail git rm folder1/* && 2074 run_on_sparse git rm --sparse folder1/* && 2075 test_cmp folder1-full sparse-checkout-out && 2076 test_cmp folder1-sparse sparse-index-out && 2077 test_sparse_match git status --porcelain=v2 && 2078 2079 # test recursive rm 2080 run_on_sparse git reset --hard && 2081 run_on_sparse git sparse-checkout reapply && 2082 test_sparse_match test_must_fail git rm --sparse folder1 && 2083 run_on_sparse git rm --sparse -r folder1 && 2084 test_cmp folder1-full sparse-checkout-out && 2085 test_cmp folder1-sparse sparse-index-out && 2086 test_sparse_match git status --porcelain=v2 2087' 2088 2089test_expect_success 'rm pathspec expands index when necessary' ' 2090 init_repos && 2091 2092 # in-cone pathspec (do not expand) 2093 ensure_not_expanded rm "deep/deep*" && 2094 test_must_be_empty sparse-index-err && 2095 2096 # out-of-cone pathspec (expand) 2097 ! ensure_not_expanded rm --sparse "folder1/a*" && 2098 test_must_be_empty sparse-index-err && 2099 2100 # pathspec that should expand index 2101 ! ensure_not_expanded rm "*/a" && 2102 test_must_be_empty sparse-index-err && 2103 2104 ! ensure_not_expanded rm "**a" && 2105 test_must_be_empty sparse-index-err 2106' 2107 2108test_expect_success 'sparse index is not expanded: rm' ' 2109 init_repos && 2110 2111 ensure_not_expanded rm deep/a && 2112 2113 # test in-cone wildcard 2114 git -C sparse-index reset --hard && 2115 ensure_not_expanded rm deep/* && 2116 2117 # test recursive rm 2118 git -C sparse-index reset --hard && 2119 ensure_not_expanded rm -r deep 2120' 2121 2122test_expect_success 'grep with and --cached' ' 2123 init_repos && 2124 2125 test_all_match git grep --cached a && 2126 test_all_match git grep --cached a -- "folder1/*" 2127' 2128 2129test_expect_success 'grep is not expanded' ' 2130 init_repos && 2131 2132 ensure_not_expanded grep a && 2133 ensure_not_expanded grep a -- deep/* && 2134 2135 # All files within the folder1/* pathspec are sparse, 2136 # so this command does not find any matches 2137 ensure_not_expanded ! grep a -- folder1/* && 2138 2139 # test out-of-cone pathspec with or without wildcard 2140 ensure_not_expanded grep --cached a -- "folder1/a" && 2141 ensure_not_expanded grep --cached a -- "folder1/*" && 2142 2143 # test in-cone pathspec with or without wildcard 2144 ensure_not_expanded grep --cached a -- "deep/a" && 2145 ensure_not_expanded grep --cached a -- "deep/*" 2146' 2147 2148# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules, 2149# Git expands the index of the submodules unexpectedly. Even though `grep` 2150# builtin is marked as "command_requires_full_index = 0", this config is only 2151# useful for the superproject. Namely, the submodules have their own configs, 2152# which are _not_ populated by the one-time sparse-index feature switch. 2153test_expect_failure 'grep within submodules is not expanded' ' 2154 init_repos_as_submodules && 2155 2156 # do not use ensure_not_expanded() here, because `grep` should be 2157 # run in the superproject, not in "./sparse-index" 2158 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ 2159 git grep --cached --recurse-submodules a -- "*/folder1/*" && 2160 test_region ! index ensure_full_index trace2.txt 2161' 2162 2163# NEEDSWORK: this test is not actually testing the code. The design purpose 2164# of this test is to verify the grep result when the submodules are using a 2165# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but 2166# because of the index expansion, we are now grepping the "folder1/a" blob. 2167# Because of the problem stated above 'grep within submodules is not expanded', 2168# we don't have the ideal test environment yet. 2169test_expect_success 'grep sparse directory within submodules' ' 2170 init_repos_as_submodules && 2171 2172 cat >expect <<-\EOF && 2173 full-checkout/folder1/a:a 2174 sparse-checkout/folder1/a:a 2175 sparse-index/folder1/a:a 2176 EOF 2177 git grep --cached --recurse-submodules a -- "*/folder1/*" >actual && 2178 test_cmp actual expect 2179' 2180 2181test_expect_success 'write-tree' ' 2182 init_repos && 2183 2184 test_all_match git write-tree && 2185 2186 write_script edit-contents <<-\EOF && 2187 echo text >>"$1" 2188 EOF 2189 2190 # make a change inside the sparse cone 2191 run_on_all ../edit-contents deep/a && 2192 test_all_match git update-index deep/a && 2193 test_all_match git write-tree && 2194 test_all_match git status --porcelain=v2 && 2195 2196 # make a change outside the sparse cone 2197 run_on_all mkdir -p folder1 && 2198 run_on_all cp a folder1/a && 2199 run_on_all ../edit-contents folder1/a && 2200 test_all_match git update-index folder1/a && 2201 test_all_match git write-tree && 2202 test_all_match git status --porcelain=v2 && 2203 2204 # check that SKIP_WORKTREE files are not materialized 2205 test_path_is_missing sparse-checkout/folder2/a && 2206 test_path_is_missing sparse-index/folder2/a 2207' 2208 2209test_expect_success 'sparse-index is not expanded: write-tree' ' 2210 init_repos && 2211 2212 ensure_not_expanded write-tree && 2213 2214 echo "test1" >>sparse-index/a && 2215 git -C sparse-index update-index a && 2216 ensure_not_expanded write-tree 2217' 2218 2219test_expect_success 'diff-files with pathspec inside sparse definition' ' 2220 init_repos && 2221 2222 write_script edit-contents <<-\EOF && 2223 echo text >>"$1" 2224 EOF 2225 2226 run_on_all ../edit-contents deep/a && 2227 2228 test_all_match git diff-files && 2229 2230 test_all_match git diff-files -- deep/a && 2231 2232 # test wildcard 2233 test_all_match git diff-files -- "deep/*" 2234' 2235 2236test_expect_success 'diff-files with pathspec outside sparse definition' ' 2237 init_repos && 2238 2239 test_sparse_match git diff-files -- folder2/a && 2240 2241 write_script edit-contents <<-\EOF && 2242 echo text >>"$1" 2243 EOF 2244 2245 # The directory "folder1" is outside the cone of interest 2246 # and will not exist in the sparse checkout repositories. 2247 # Create it as needed, add file "folder1/a" there with 2248 # contents that is different from the staged version. 2249 run_on_all mkdir -p folder1 && 2250 run_on_all cp a folder1/a && 2251 2252 run_on_all ../edit-contents folder1/a && 2253 test_all_match git diff-files && 2254 test_all_match git diff-files -- folder1/a && 2255 test_all_match git diff-files -- "folder*/a" 2256' 2257 2258test_expect_success 'sparse index is not expanded: diff-files' ' 2259 init_repos && 2260 2261 write_script edit-contents <<-\EOF && 2262 echo text >>"$1" 2263 EOF 2264 2265 run_on_all ../edit-contents deep/a && 2266 2267 ensure_not_expanded diff-files && 2268 ensure_not_expanded diff-files -- deep/a && 2269 ensure_not_expanded diff-files -- "deep/*" 2270' 2271 2272test_expect_success 'diff-tree' ' 2273 init_repos && 2274 2275 # Test change inside sparse cone 2276 tree1=$(git -C sparse-index rev-parse HEAD^{tree}) && 2277 tree2=$(git -C sparse-index rev-parse update-deep^{tree}) && 2278 test_all_match git diff-tree $tree1 $tree2 && 2279 test_all_match git diff-tree $tree1 $tree2 -- deep/a && 2280 test_all_match git diff-tree HEAD update-deep && 2281 test_all_match git diff-tree HEAD update-deep -- deep/a && 2282 2283 # Test change outside sparse cone 2284 tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) && 2285 test_all_match git diff-tree $tree1 $tree3 && 2286 test_all_match git diff-tree $tree1 $tree3 -- folder1/a && 2287 test_all_match git diff-tree HEAD update-folder1 && 2288 test_all_match git diff-tree HEAD update-folder1 -- folder1/a && 2289 2290 # Check that SKIP_WORKTREE files are not materialized 2291 test_path_is_missing sparse-checkout/folder1/a && 2292 test_path_is_missing sparse-index/folder1/a && 2293 test_path_is_missing sparse-checkout/folder2/a && 2294 test_path_is_missing sparse-index/folder2/a 2295' 2296 2297test_expect_success 'sparse-index is not expanded: diff-tree' ' 2298 init_repos && 2299 2300 tree1=$(git -C sparse-index rev-parse HEAD^{tree}) && 2301 tree2=$(git -C sparse-index rev-parse update-deep^{tree}) && 2302 tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) && 2303 2304 ensure_not_expanded diff-tree $tree1 $tree2 && 2305 ensure_not_expanded diff-tree $tree1 $tree2 -- deep/a && 2306 ensure_not_expanded diff-tree HEAD update-deep && 2307 ensure_not_expanded diff-tree HEAD update-deep -- deep/a && 2308 ensure_not_expanded diff-tree $tree1 $tree3 && 2309 ensure_not_expanded diff-tree $tree1 $tree3 -- folder1/a && 2310 ensure_not_expanded diff-tree HEAD update-folder1 && 2311 ensure_not_expanded diff-tree HEAD update-folder1 -- folder1/a 2312' 2313 2314test_expect_success 'worktree' ' 2315 init_repos && 2316 2317 write_script edit-contents <<-\EOF && 2318 echo text >>"$1" 2319 EOF 2320 2321 for repo in full-checkout sparse-checkout sparse-index 2322 do 2323 worktree=${repo}-wt && 2324 git -C $repo worktree add ../$worktree && 2325 2326 # Compare worktree content with "ls" 2327 (cd $repo && ls) >worktree_contents && 2328 (cd $worktree && ls) >new_worktree_contents && 2329 test_cmp worktree_contents new_worktree_contents && 2330 2331 # Compare index content with "ls-files --sparse" 2332 git -C $repo ls-files --sparse >index_contents && 2333 git -C $worktree ls-files --sparse >new_index_contents && 2334 test_cmp index_contents new_index_contents && 2335 2336 git -C $repo worktree remove ../$worktree || return 1 2337 done && 2338 2339 test_all_match git worktree add .worktrees/hotfix && 2340 run_on_all ../edit-contents .worktrees/hotfix/deep/a && 2341 test_all_match test_must_fail git worktree remove .worktrees/hotfix 2342' 2343 2344test_expect_success 'worktree is not expanded' ' 2345 init_repos && 2346 2347 ensure_not_expanded worktree add .worktrees/hotfix && 2348 ensure_not_expanded worktree remove .worktrees/hotfix 2349' 2350 2351test_expect_success 'check-attr with pathspec inside sparse definition' ' 2352 init_repos && 2353 2354 echo "a -crlf myAttr" >>.gitattributes && 2355 run_on_all cp ../.gitattributes ./deep && 2356 2357 test_all_match git check-attr -a -- deep/a && 2358 2359 test_all_match git add deep/.gitattributes && 2360 test_all_match git check-attr -a --cached -- deep/a 2361' 2362 2363test_expect_success 'check-attr with pathspec outside sparse definition' ' 2364 init_repos && 2365 2366 echo "a -crlf myAttr" >>.gitattributes && 2367 run_on_sparse mkdir folder1 && 2368 run_on_all cp ../.gitattributes ./folder1 && 2369 run_on_all cp a folder1/a && 2370 2371 test_all_match git check-attr -a -- folder1/a && 2372 2373 git -C full-checkout add folder1/.gitattributes && 2374 test_sparse_match git add --sparse folder1/.gitattributes && 2375 test_all_match git commit -m "add .gitattributes" && 2376 test_sparse_match git sparse-checkout reapply && 2377 test_all_match git check-attr -a --cached -- folder1/a 2378' 2379 2380# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due 2381# to an underlying issue in oneway_diff() within diff-lib.c. 2382# 'do_oneway_diff()' is not called as expected for paths that could match 2383# inside of a sparse directory. Specifically, the 'ce_path_match()' function 2384# fails to recognize files inside a sparse directory (e.g., when 'folder1/' 2385# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to 2386# proceed with 'do_oneway_diff()' if the pathspec could match inside of a 2387# sparse directory. 2388test_expect_failure 'diff --check with pathspec outside sparse definition' ' 2389 init_repos && 2390 2391 write_script edit-contents <<-\EOF && 2392 echo "a " >"$1" 2393 EOF 2394 2395 test_all_match git config core.whitespace -trailing-space,-space-before-tab && 2396 2397 echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes && 2398 run_on_all mkdir -p folder1 && 2399 run_on_all cp ../.gitattributes ./folder1 && 2400 test_all_match git add --sparse folder1/.gitattributes && 2401 run_on_all ../edit-contents folder1/a && 2402 test_all_match git add --sparse folder1/a && 2403 2404 test_sparse_match git sparse-checkout reapply && 2405 test_all_match test_must_fail git diff --check --cached -- folder1/a 2406' 2407 2408test_expect_success 'sparse-index is not expanded: check-attr' ' 2409 init_repos && 2410 2411 echo "a -crlf myAttr" >>.gitattributes && 2412 mkdir ./sparse-index/folder1 && 2413 cp ./sparse-index/a ./sparse-index/folder1/a && 2414 cp .gitattributes ./sparse-index/deep && 2415 cp .gitattributes ./sparse-index/folder1 && 2416 2417 git -C sparse-index add deep/.gitattributes && 2418 git -C sparse-index add --sparse folder1/.gitattributes && 2419 ensure_not_expanded check-attr -a --cached -- deep/a && 2420 ensure_not_expanded check-attr -a --cached -- folder1/a 2421' 2422 2423test_expect_success 'sparse-index is not expanded: git apply' ' 2424 init_repos && 2425 2426 git -C sparse-index checkout base && 2427 git -C full-checkout diff base..merge-right -- deep >patch-in-sparse && 2428 git -C full-checkout diff base..merge-right -- folder2 >patch-outside && 2429 2430 # Apply a patch to a file inside the sparse definition 2431 ensure_not_expanded apply --index --stat ../patch-in-sparse && 2432 2433 # Apply a patch to a file outside the sparse definition 2434 # Fails when caring about the worktree. 2435 ensure_not_expanded ! apply ../patch-outside && 2436 2437 # Expands when using --index. 2438 ensure_expanded apply --index ../patch-outside && 2439 2440 # Does not when index is partially expanded. 2441 git -C sparse-index reset --hard && 2442 ensure_not_expanded apply --cached ../patch-outside && 2443 2444 # Try again with a reset and collapsed index. 2445 git -C sparse-index reset --hard && 2446 git -C sparse-index sparse-checkout reapply && 2447 2448 # Expands when index is collapsed. 2449 ensure_expanded apply --cached ../patch-outside 2450' 2451 2452test_expect_success 'sparse-index is not expanded: git add -p' ' 2453 init_repos && 2454 2455 # Does not expand when edits are within sparse checkout. 2456 echo "new content" >sparse-index/deep/a && 2457 echo "new content" >sparse-index/deep/deeper1/a && 2458 test_write_lines y n >in && 2459 ensure_not_expanded add -p <in && 2460 git -C sparse-index reset && 2461 ensure_not_expanded add -i <in && 2462 2463 # -p does expand when edits are outside sparse checkout. 2464 mkdir -p sparse-index/folder1 && 2465 echo "new content" >sparse-index/folder1/a && 2466 test_write_lines y n y >in && 2467 ensure_expanded add -p <in && 2468 2469 # Fully reset the index. 2470 git -C sparse-index reset --hard && 2471 git -C sparse-index sparse-checkout reapply && 2472 2473 # -i does expand when edits are outside sparse checkout. 2474 mkdir -p sparse-index/folder1 && 2475 echo "new content" >sparse-index/folder1/a && 2476 test_write_lines u 2 3 "" q >in && 2477 ensure_expanded add -i <in 2478' 2479 2480test_expect_success 'sparse-index is not expanded: checkout -p, reset -p' ' 2481 init_repos && 2482 2483 # Does not expand when edits are within sparse checkout. 2484 echo "new content" >sparse-index/deep/a && 2485 echo "new content" >sparse-index/deep/deeper1/a && 2486 git -C sparse-index commit -a -m "inside-changes" && 2487 2488 test_write_lines y y >in && 2489 ensure_not_expanded checkout HEAD~1 --patch <in && 2490 2491 echo "new content" >sparse-index/deep/a && 2492 echo "new content" >sparse-index/deep/deeper1/a && 2493 git -C sparse-index add . && 2494 ensure_not_expanded reset --patch <in && 2495 2496 # -p does expand when edits are outside sparse checkout. 2497 mkdir -p sparse-index/folder1 && 2498 echo "new content" >sparse-index/folder1/a && 2499 git -C sparse-index add --sparse folder1 && 2500 git -C sparse-index sparse-checkout reapply && 2501 ensure_expanded reset --patch <in && 2502 2503 # Fully reset the index. 2504 mkdir -p sparse-index/folder1 && 2505 echo "new content" >sparse-index/folder1/a && 2506 git -C sparse-index add --sparse folder1 && 2507 git -C sparse-index commit -m "folder1 change" && 2508 git -C sparse-index sparse-checkout reapply && 2509 ensure_expanded checkout HEAD~1 --patch <in 2510' 2511 2512test_expect_success 'advice.sparseIndexExpanded' ' 2513 init_repos && 2514 2515 git -C sparse-index config --unset advice.sparseIndexExpanded && 2516 git -C sparse-index sparse-checkout set deep/deeper1 && 2517 mkdir -p sparse-index/deep/deeper2/deepest && 2518 touch sparse-index/deep/deeper2/deepest/bogus && 2519 git -C sparse-index status 2>err && 2520 grep "The sparse index is expanding to a full index" err && 2521 2522 git -C sparse-index sparse-checkout disable 2>err && 2523 test_line_count = 0 err 2524' 2525 2526test_expect_success 'cat-file -p' ' 2527 init_repos && 2528 echo "new content" >>full-checkout/deep/a && 2529 echo "new content" >>sparse-checkout/deep/a && 2530 echo "new content" >>sparse-index/deep/a && 2531 run_on_all git add deep/a && 2532 2533 test_all_match git cat-file -p :deep/a && 2534 ensure_not_expanded cat-file -p :deep/a && 2535 test_all_match git cat-file -p :folder1/a && 2536 ensure_expanded cat-file -p :folder1/a 2537' 2538 2539test_expect_success 'cat-file --batch' ' 2540 init_repos && 2541 echo "new content" >>full-checkout/deep/a && 2542 echo "new content" >>sparse-checkout/deep/a && 2543 echo "new content" >>sparse-index/deep/a && 2544 run_on_all git add deep/a && 2545 2546 echo ":deep/a" >in && 2547 test_all_match git cat-file --batch <in && 2548 ensure_not_expanded cat-file --batch <in && 2549 2550 echo ":folder1/a" >in && 2551 test_all_match git cat-file --batch <in && 2552 ensure_expanded cat-file --batch <in && 2553 2554 cat >in <<-\EOF && 2555 :deep/a 2556 :folder1/a 2557 EOF 2558 test_all_match git cat-file --batch <in && 2559 ensure_expanded cat-file --batch <in 2560' 2561 2562test_done