@@ -621,6 +621,156 @@ test_expect_success 'reset with wildcard pathspec' '
621
621
test_all_match git status --porcelain=v2
622
622
'
623
623
624
+ # NEEDSWORK: although update-index executes without error on files outside
625
+ # the sparse checkout definition, it does not actually add the file to the
626
+ # index. This is also true when "--no-ignore-skip-worktree-entries" is
627
+ # specified.
628
+ test_expect_success ' update-index add outside sparse definition' '
629
+ init_repos &&
630
+
631
+ write_script edit-contents <<-\EOF &&
632
+ echo text >>$1
633
+ EOF
634
+
635
+ run_on_sparse mkdir -p folder1 &&
636
+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
637
+
638
+ # Edit the file only in sparse checkouts so that, when checking the status
639
+ # of the index, the unmodified full-checkout is compared to the "modified"
640
+ # sparse checkouts.
641
+ run_on_sparse ../edit-contents folder1/a &&
642
+
643
+ test_sparse_match git update-index --add folder1/a &&
644
+ test_all_match git status --porcelain=v2 &&
645
+ test_sparse_match git update-index --add --no-ignore-skip-worktree-entries folder1/a &&
646
+ test_all_match git status --porcelain=v2
647
+ '
648
+
649
+ test_expect_success ' update-index remove outside sparse definition' '
650
+ init_repos &&
651
+
652
+ # When --remove is specified, files outside the sparse checkout definition
653
+ # are considered "removed".
654
+ rm -f full-checkout/folder1/a &&
655
+ test_all_match git update-index --remove folder1/a &&
656
+ test_all_match git status --porcelain=v2 &&
657
+
658
+ git reset --hard &&
659
+
660
+ # When --ignore-skip-worktree-entries is explicitly specified, a file
661
+ # outside the sparse definition is not added to the index as "removed"
662
+ # (thus matching the unmodified full-checkout).
663
+ test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
664
+ test_all_match git status --porcelain=v2 &&
665
+
666
+ git reset --hard &&
667
+
668
+ # --force-remove supercedes --ignore-skip-worktree-entries and always
669
+ # removes the file from the index.
670
+ test_all_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
671
+ test_all_match git status --porcelain=v2
672
+ '
673
+
674
+ test_expect_success ' update-index folder add/remove' '
675
+ init_repos &&
676
+
677
+ test_all_match test_must_fail git update-index --add --remove deep &&
678
+ test_all_match test_must_fail git update-index --add --remove deep/ &&
679
+
680
+ # NEEDSWORK: attempting to update-index on an existing folder outside the
681
+ # sparse checkout definition does not throw an error (as it does for folders
682
+ # inside the definition, or in the full checkout). However, it is a no-op.
683
+ test_sparse_match git update-index --add --remove folder1 &&
684
+ test_sparse_match git update-index --add --remove folder1/ &&
685
+ test_sparse_match git update-index --force-remove folder1/ &&
686
+ test_all_match git status --porcelain=v2 &&
687
+
688
+ # New folders, even in sparse checkouts, throw an error on update-index
689
+ run_on_all mkdir folder3 &&
690
+ run_on_all cp a folder3/a &&
691
+ run_on_all test_must_fail git update-index --add --remove folder3
692
+ '
693
+
694
+ test_expect_success ' update-index with updated flags' '
695
+ init_repos &&
696
+
697
+ # NEEDSWORK: updating flags runs inconsistently on directories, performing no
698
+ # operation with warning text specifying the path being ignored if a trailing
699
+ # slash is in the path, but throwing an error if there is no trailing slash.
700
+ test_all_match test_must_fail git update-index --no-skip-worktree folder1 &&
701
+ test_all_match git update-index --no-skip-worktree folder1/ &&
702
+ test_all_match git status --porcelain=v2 &&
703
+
704
+ # Removing the skip-worktree bit from a file outside the sparse checkout
705
+ # will cause the file to appear as unstaged and deleted.
706
+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
707
+ rm -f full-checkout/folder1/a &&
708
+ test_all_match git status --porcelain=v2
709
+ '
710
+
711
+ test_expect_success ' update-index --again file outside sparse definition' '
712
+ init_repos &&
713
+
714
+ write_script edit-contents <<-\EOF &&
715
+ echo text >>$1
716
+ EOF
717
+
718
+ # When a file is manually added and modified outside the checkout
719
+ # definition, it will not be changed with `--again` because its changes are
720
+ # not tracked in the index.
721
+ run_on_sparse mkdir -p folder1 &&
722
+ run_on_sparse ../edit-contents folder1/a &&
723
+ test_sparse_match git update-index --again &&
724
+ test_sparse_match git status --porcelain=v2 &&
725
+
726
+ # Update the sparse checkouts so that folder1/a is no longer skipped and
727
+ # exists on-disk
728
+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
729
+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
730
+ test_all_match git status --porcelain=v2 &&
731
+
732
+ # Stage change for commit
733
+ run_on_all ../edit-contents folder1/a &&
734
+ test_all_match git update-index folder1/a &&
735
+ test_all_match git status --porcelain=v2 &&
736
+
737
+ # Modify the file
738
+ run_on_all ../edit-contents folder1/a &&
739
+ test_all_match git status --porcelain=v2 &&
740
+
741
+ # Run update-index --again, which re-stages the local changes
742
+ test_all_match git update-index --again &&
743
+ test_all_match git ls-files -s folder1/a &&
744
+ test_all_match git status --porcelain=v2 &&
745
+
746
+ # Running update-index --again with staged changes after manually deleting
747
+ # the file on disk will cause it to fail if --remove is not also specified
748
+ run_on_all rm -f folder1/a &&
749
+ test_all_match test_must_fail git update-index --again folder1 &&
750
+ test_all_match git update-index --remove --again &&
751
+ test_all_match git status --porcelain=v2
752
+ '
753
+
754
+ test_expect_success ' update-index --cacheinfo' '
755
+ init_repos &&
756
+
757
+ deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
758
+ folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
759
+ folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
760
+
761
+ test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
762
+ test_all_match git status --porcelain=v2 &&
763
+
764
+ # Cannot add sparse directory, even in sparse index case
765
+ test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
766
+
767
+ # Sparse match only - because folder1/a is outside the sparse checkout
768
+ # definition (and thus not on-disk), it will appear as "deleted" in
769
+ # unstaged changes.
770
+ test_all_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
771
+ test_sparse_match git status --porcelain=v2
772
+ '
773
+
624
774
test_expect_success ' merge, cherry-pick, and rebase' '
625
775
init_repos &&
626
776
@@ -907,6 +1057,21 @@ test_expect_success 'sparse index is not expanded: sparse-checkout' '
907
1057
ensure_not_expanded sparse-checkout set
908
1058
'
909
1059
1060
+ test_expect_success ' sparse index is not expanded: update-index' '
1061
+ init_repos &&
1062
+
1063
+ echo "test" >sparse-index/README.md &&
1064
+ echo "test2" >sparse-index/a &&
1065
+ rm -f sparse-index/deep/a &&
1066
+
1067
+ ensure_not_expanded update-index --add README.md &&
1068
+ ensure_not_expanded update-index a &&
1069
+ ensure_not_expanded update-index --remove deep/a &&
1070
+
1071
+ rm -f sparse-index/README.md sparse-index/a &&
1072
+ ensure_not_expanded update-index --add --remove --again
1073
+ '
1074
+
910
1075
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
911
1076
# in this scenario, but it shouldn't.
912
1077
test_expect_success ' reset mixed and checkout orphan' '
0 commit comments