@@ -655,6 +655,156 @@ test_expect_success 'reset with wildcard pathspec' '
655655 test_all_match git status --porcelain=v2
656656'
657657
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+
658808test_expect_success ' merge, cherry-pick, and rebase' '
659809 init_repos &&
660810
@@ -993,6 +1143,21 @@ test_expect_success 'sparse index is not expanded: sparse-checkout' '
9931143 ensure_not_expanded sparse-checkout set
9941144'
9951145
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+
9961161# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
9971162# in this scenario, but it shouldn't.
9981163test_expect_success ' reset mixed and checkout orphan' '
0 commit comments