@@ -621,6 +621,156 @@ test_expect_success 'reset with wildcard pathspec' '
621621 test_all_match git status --porcelain=v2
622622'
623623
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+
624774test_expect_success ' merge, cherry-pick, and rebase' '
625775 init_repos &&
626776
@@ -907,6 +1057,21 @@ test_expect_success 'sparse index is not expanded: sparse-checkout' '
9071057 ensure_not_expanded sparse-checkout set
9081058'
9091059
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+
9101075# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
9111076# in this scenario, but it shouldn't.
9121077test_expect_success ' reset mixed and checkout orphan' '
0 commit comments