@@ -774,6 +774,156 @@ test_expect_success 'reset with wildcard pathspec' '
774774 test_all_match git status --porcelain=v2
775775'
776776
777+ # NEEDSWORK: although update-index executes without error on files outside
778+ # the sparse checkout definition, it does not actually add the file to the
779+ # index. This is also true when "--no-ignore-skip-worktree-entries" is
780+ # specified.
781+ test_expect_success ' update-index add outside sparse definition' '
782+ init_repos &&
783+
784+ write_script edit-contents <<-\EOF &&
785+ echo text >>$1
786+ EOF
787+
788+ run_on_sparse mkdir -p folder1 &&
789+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
790+
791+ # Edit the file only in sparse checkouts so that, when checking the status
792+ # of the index, the unmodified full-checkout is compared to the "modified"
793+ # sparse checkouts.
794+ run_on_sparse ../edit-contents folder1/a &&
795+
796+ test_sparse_match git update-index --add folder1/a &&
797+ test_all_match git status --porcelain=v2 &&
798+ test_sparse_match git update-index --add --no-ignore-skip-worktree-entries folder1/a &&
799+ test_all_match git status --porcelain=v2
800+ '
801+
802+ test_expect_success ' update-index remove outside sparse definition' '
803+ init_repos &&
804+
805+ # When --remove is specified, files outside the sparse checkout definition
806+ # are considered "removed".
807+ rm -f full-checkout/folder1/a &&
808+ test_all_match git update-index --remove folder1/a &&
809+ test_all_match git status --porcelain=v2 &&
810+
811+ git reset --hard &&
812+
813+ # When --ignore-skip-worktree-entries is explicitly specified, a file
814+ # outside the sparse definition is not added to the index as "removed"
815+ # (thus matching the unmodified full-checkout).
816+ test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
817+ test_all_match git status --porcelain=v2 &&
818+
819+ git reset --hard &&
820+
821+ # --force-remove supercedes --ignore-skip-worktree-entries and always
822+ # removes the file from the index.
823+ test_all_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
824+ test_all_match git status --porcelain=v2
825+ '
826+
827+ test_expect_success ' update-index folder add/remove' '
828+ init_repos &&
829+
830+ test_all_match test_must_fail git update-index --add --remove deep &&
831+ test_all_match test_must_fail git update-index --add --remove deep/ &&
832+
833+ # NEEDSWORK: attempting to update-index on an existing folder outside the
834+ # sparse checkout definition does not throw an error (as it does for folders
835+ # inside the definition, or in the full checkout). However, it is a no-op.
836+ test_sparse_match git update-index --add --remove folder1 &&
837+ test_sparse_match git update-index --add --remove folder1/ &&
838+ test_sparse_match git update-index --force-remove folder1/ &&
839+ test_all_match git status --porcelain=v2 &&
840+
841+ # New folders, even in sparse checkouts, throw an error on update-index
842+ run_on_all mkdir folder3 &&
843+ run_on_all cp a folder3/a &&
844+ run_on_all test_must_fail git update-index --add --remove folder3
845+ '
846+
847+ test_expect_success ' update-index with updated flags' '
848+ init_repos &&
849+
850+ # NEEDSWORK: updating flags runs inconsistently on directories, performing no
851+ # operation with warning text specifying the path being ignored if a trailing
852+ # slash is in the path, but throwing an error if there is no trailing slash.
853+ test_all_match test_must_fail git update-index --no-skip-worktree folder1 &&
854+ test_all_match git update-index --no-skip-worktree folder1/ &&
855+ test_all_match git status --porcelain=v2 &&
856+
857+ # Removing the skip-worktree bit from a file outside the sparse checkout
858+ # will cause the file to appear as unstaged and deleted.
859+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
860+ rm -f full-checkout/folder1/a &&
861+ test_all_match git status --porcelain=v2
862+ '
863+
864+ test_expect_success ' update-index --again file outside sparse definition' '
865+ init_repos &&
866+
867+ write_script edit-contents <<-\EOF &&
868+ echo text >>$1
869+ EOF
870+
871+ # When a file is manually added and modified outside the checkout
872+ # definition, it will not be changed with `--again` because its changes are
873+ # not tracked in the index.
874+ run_on_sparse mkdir -p folder1 &&
875+ run_on_sparse ../edit-contents folder1/a &&
876+ test_sparse_match git update-index --again &&
877+ test_sparse_match git status --porcelain=v2 &&
878+
879+ # Update the sparse checkouts so that folder1/a is no longer skipped and
880+ # exists on-disk
881+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
882+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
883+ test_all_match git status --porcelain=v2 &&
884+
885+ # Stage change for commit
886+ run_on_all ../edit-contents folder1/a &&
887+ test_all_match git update-index folder1/a &&
888+ test_all_match git status --porcelain=v2 &&
889+
890+ # Modify the file
891+ run_on_all ../edit-contents folder1/a &&
892+ test_all_match git status --porcelain=v2 &&
893+
894+ # Run update-index --again, which re-stages the local changes
895+ test_all_match git update-index --again &&
896+ test_all_match git ls-files -s folder1/a &&
897+ test_all_match git status --porcelain=v2 &&
898+
899+ # Running update-index --again with staged changes after manually deleting
900+ # the file on disk will cause it to fail if --remove is not also specified
901+ run_on_all rm -f folder1/a &&
902+ test_all_match test_must_fail git update-index --again folder1 &&
903+ test_all_match git update-index --remove --again &&
904+ test_all_match git status --porcelain=v2
905+ '
906+
907+ test_expect_success ' update-index --cacheinfo' '
908+ init_repos &&
909+
910+ deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
911+ folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
912+ folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
913+
914+ test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
915+ test_all_match git status --porcelain=v2 &&
916+
917+ # Cannot add sparse directory, even in sparse index case
918+ test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
919+
920+ # Sparse match only - because folder1/a is outside the sparse checkout
921+ # definition (and thus not on-disk), it will appear as "deleted" in
922+ # unstaged changes.
923+ test_all_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
924+ test_sparse_match git status --porcelain=v2
925+ '
926+
777927test_expect_success ' merge, cherry-pick, and rebase' '
778928 init_repos &&
779929
@@ -1274,6 +1424,21 @@ test_expect_success 'sparse index is not expanded: sparse-checkout' '
12741424 ensure_not_expanded sparse-checkout set
12751425'
12761426
1427+ test_expect_success ' sparse index is not expanded: update-index' '
1428+ init_repos &&
1429+
1430+ echo "test" >sparse-index/README.md &&
1431+ echo "test2" >sparse-index/a &&
1432+ rm -f sparse-index/deep/a &&
1433+
1434+ ensure_not_expanded update-index --add README.md &&
1435+ ensure_not_expanded update-index a &&
1436+ ensure_not_expanded update-index --remove deep/a &&
1437+
1438+ rm -f sparse-index/README.md sparse-index/a &&
1439+ ensure_not_expanded update-index --add --remove --again
1440+ '
1441+
12771442# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
12781443# in this scenario, but it shouldn't.
12791444test_expect_success ' reset mixed and checkout orphan' '
0 commit comments