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