@@ -655,6 +655,156 @@ test_expect_success 'reset with wildcard pathspec' '
655
655
test_all_match git status --porcelain=v2
656
656
'
657
657
658
+ # NEEDSWORK: although update-index executes without error on files outside
659
+ # the sparse checkout definition, it does not actually add the file to the
660
+ # index. This is also true when "--no-ignore-skip-worktree-entries" is
661
+ # specified.
662
+ test_expect_success ' update-index add outside sparse definition' '
663
+ init_repos &&
664
+
665
+ write_script edit-contents <<-\EOF &&
666
+ echo text >>$1
667
+ EOF
668
+
669
+ run_on_sparse mkdir -p folder1 &&
670
+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
671
+
672
+ # Edit the file only in sparse checkouts so that, when checking the status
673
+ # of the index, the unmodified full-checkout is compared to the "modified"
674
+ # sparse checkouts.
675
+ run_on_sparse ../edit-contents folder1/a &&
676
+
677
+ test_sparse_match git update-index --add folder1/a &&
678
+ test_all_match git status --porcelain=v2 &&
679
+ test_sparse_match git update-index --add --no-ignore-skip-worktree-entries folder1/a &&
680
+ test_all_match git status --porcelain=v2
681
+ '
682
+
683
+ test_expect_success ' update-index remove outside sparse definition' '
684
+ init_repos &&
685
+
686
+ # When --remove is specified, files outside the sparse checkout definition
687
+ # are considered "removed".
688
+ rm -f full-checkout/folder1/a &&
689
+ test_all_match git update-index --remove folder1/a &&
690
+ test_all_match git status --porcelain=v2 &&
691
+
692
+ git reset --hard &&
693
+
694
+ # When --ignore-skip-worktree-entries is explicitly specified, a file
695
+ # outside the sparse definition is not added to the index as "removed"
696
+ # (thus matching the unmodified full-checkout).
697
+ test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
698
+ test_all_match git status --porcelain=v2 &&
699
+
700
+ git reset --hard &&
701
+
702
+ # --force-remove supercedes --ignore-skip-worktree-entries and always
703
+ # removes the file from the index.
704
+ test_all_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
705
+ test_all_match git status --porcelain=v2
706
+ '
707
+
708
+ test_expect_success ' update-index folder add/remove' '
709
+ init_repos &&
710
+
711
+ test_all_match test_must_fail git update-index --add --remove deep &&
712
+ test_all_match test_must_fail git update-index --add --remove deep/ &&
713
+
714
+ # NEEDSWORK: attempting to update-index on an existing folder outside the
715
+ # sparse checkout definition does not throw an error (as it does for folders
716
+ # inside the definition, or in the full checkout). However, it is a no-op.
717
+ test_sparse_match git update-index --add --remove folder1 &&
718
+ test_sparse_match git update-index --add --remove folder1/ &&
719
+ test_sparse_match git update-index --force-remove folder1/ &&
720
+ test_all_match git status --porcelain=v2 &&
721
+
722
+ # New folders, even in sparse checkouts, throw an error on update-index
723
+ run_on_all mkdir folder3 &&
724
+ run_on_all cp a folder3/a &&
725
+ run_on_all test_must_fail git update-index --add --remove folder3
726
+ '
727
+
728
+ test_expect_success ' update-index with updated flags' '
729
+ init_repos &&
730
+
731
+ # NEEDSWORK: updating flags runs inconsistently on directories, performing no
732
+ # operation with warning text specifying the path being ignored if a trailing
733
+ # slash is in the path, but throwing an error if there is no trailing slash.
734
+ test_all_match test_must_fail git update-index --no-skip-worktree folder1 &&
735
+ test_all_match git update-index --no-skip-worktree folder1/ &&
736
+ test_all_match git status --porcelain=v2 &&
737
+
738
+ # Removing the skip-worktree bit from a file outside the sparse checkout
739
+ # will cause the file to appear as unstaged and deleted.
740
+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
741
+ rm -f full-checkout/folder1/a &&
742
+ test_all_match git status --porcelain=v2
743
+ '
744
+
745
+ test_expect_success ' update-index --again file outside sparse definition' '
746
+ init_repos &&
747
+
748
+ write_script edit-contents <<-\EOF &&
749
+ echo text >>$1
750
+ EOF
751
+
752
+ # When a file is manually added and modified outside the checkout
753
+ # definition, it will not be changed with `--again` because its changes are
754
+ # not tracked in the index.
755
+ run_on_sparse mkdir -p folder1 &&
756
+ run_on_sparse ../edit-contents folder1/a &&
757
+ test_sparse_match git update-index --again &&
758
+ test_sparse_match git status --porcelain=v2 &&
759
+
760
+ # Update the sparse checkouts so that folder1/a is no longer skipped and
761
+ # exists on-disk
762
+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
763
+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
764
+ test_all_match git status --porcelain=v2 &&
765
+
766
+ # Stage change for commit
767
+ run_on_all ../edit-contents folder1/a &&
768
+ test_all_match git update-index folder1/a &&
769
+ test_all_match git status --porcelain=v2 &&
770
+
771
+ # Modify the file
772
+ run_on_all ../edit-contents folder1/a &&
773
+ test_all_match git status --porcelain=v2 &&
774
+
775
+ # Run update-index --again, which re-stages the local changes
776
+ test_all_match git update-index --again &&
777
+ test_all_match git ls-files -s folder1/a &&
778
+ test_all_match git status --porcelain=v2 &&
779
+
780
+ # Running update-index --again with staged changes after manually deleting
781
+ # the file on disk will cause it to fail if --remove is not also specified
782
+ run_on_all rm -f folder1/a &&
783
+ test_all_match test_must_fail git update-index --again folder1 &&
784
+ test_all_match git update-index --remove --again &&
785
+ test_all_match git status --porcelain=v2
786
+ '
787
+
788
+ test_expect_success ' update-index --cacheinfo' '
789
+ init_repos &&
790
+
791
+ deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
792
+ folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
793
+ folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
794
+
795
+ test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
796
+ test_all_match git status --porcelain=v2 &&
797
+
798
+ # Cannot add sparse directory, even in sparse index case
799
+ test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
800
+
801
+ # Sparse match only - because folder1/a is outside the sparse checkout
802
+ # definition (and thus not on-disk), it will appear as "deleted" in
803
+ # unstaged changes.
804
+ test_all_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
805
+ test_sparse_match git status --porcelain=v2
806
+ '
807
+
658
808
test_expect_success ' merge, cherry-pick, and rebase' '
659
809
init_repos &&
660
810
@@ -993,6 +1143,21 @@ test_expect_success 'sparse index is not expanded: sparse-checkout' '
993
1143
ensure_not_expanded sparse-checkout set
994
1144
'
995
1145
1146
+ test_expect_success ' sparse index is not expanded: update-index' '
1147
+ init_repos &&
1148
+
1149
+ echo "test" >sparse-index/README.md &&
1150
+ echo "test2" >sparse-index/a &&
1151
+ rm -f sparse-index/deep/a &&
1152
+
1153
+ ensure_not_expanded update-index --add README.md &&
1154
+ ensure_not_expanded update-index a &&
1155
+ ensure_not_expanded update-index --remove deep/a &&
1156
+
1157
+ rm -f sparse-index/README.md sparse-index/a &&
1158
+ ensure_not_expanded update-index --add --remove --again
1159
+ '
1160
+
996
1161
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
997
1162
# in this scenario, but it shouldn't.
998
1163
test_expect_success ' reset mixed and checkout orphan' '
0 commit comments