3 test_description
='compare full workdir to sparse workdir'
10 test_expect_success
'setup' '
11 git init initial-repo &&
13 GIT_TEST_SPARSE_INDEX=0 &&
16 echo "after deep" >e &&
17 echo "after folder1" >g &&
19 mkdir folder1 folder2 deep x &&
20 mkdir deep/deeper1 deep/deeper2 deep/before deep/later &&
21 mkdir deep/deeper1/deepest &&
22 mkdir deep/deeper1/deepest2 &&
23 mkdir deep/deeper1/deepest3 &&
24 echo "after deeper1" >deep/e &&
25 echo "after deepest" >deep/deeper1/e &&
34 cp a deep/deeper1/deepest &&
35 cp a deep/deeper1/deepest2 &&
36 cp a deep/deeper1/deepest3 &&
37 cp -r deep/deeper1/ deep/deeper2 &&
38 mkdir deep/deeper1/0 &&
39 mkdir deep/deeper1/0/0 &&
40 touch deep/deeper1/0/1 &&
41 touch deep/deeper1/0/0/0 &&
45 cp -r deep/deeper1/0 folder1 &&
46 cp -r deep/deeper1/0 folder2 &&
47 echo >>folder1/0/0/0 &&
50 git commit -m "initial commit" &&
51 git checkout -b base &&
52 for dir in folder1 folder2 deep
54 git checkout -b update-$dir base &&
55 echo "updated $dir" >$dir/a &&
56 git commit -a -m "update $dir" || return 1
59 git checkout -b rename-base base &&
60 cat >folder1/larger-content <<-\EOF &&
67 cp folder1/larger-content folder2/ &&
68 cp folder1/larger-content deep/deeper1/ &&
70 git commit -m "add interesting rename content" &&
72 git checkout -b rename-out-to-out rename-base &&
73 mv folder1/a folder2/b &&
74 mv folder1/larger-content folder2/edited-content &&
75 echo >>folder2/edited-content &&
77 echo stuff >>deep/deeper1/a &&
79 git commit -m "rename folder1/... to folder2/..." &&
81 git checkout -b rename-out-to-in rename-base &&
82 mv folder1/a deep/deeper1/b &&
83 echo more stuff >>deep/deeper1/a &&
86 echo >>folder2/0/1/1 &&
87 mv folder1/larger-content deep/deeper1/edited-content &&
88 echo >>deep/deeper1/edited-content &&
90 git commit -m "rename folder1/... to deep/deeper1/..." &&
92 git checkout -b rename-in-to-out rename-base &&
93 mv deep/deeper1/a folder1/b &&
97 mv deep/deeper1/larger-content folder1/edited-content &&
98 echo >>folder1/edited-content &&
100 git commit -m "rename deep/deeper1/... to folder1/..." &&
102 git checkout -b df-conflict-1 base &&
104 echo content >folder1 &&
106 git commit -m "dir to file" &&
108 git checkout -b df-conflict-2 base &&
110 echo content >folder2 &&
112 git commit -m "dir to file" &&
114 git checkout -b fd-conflict base &&
119 git commit -m "file to dir" &&
121 for side in left right
123 git checkout -b merge-$side base &&
124 echo $side >>deep/deeper2/a &&
125 echo $side >>folder1/a &&
126 echo $side >>folder2/a &&
128 git commit -m "$side" || return 1
131 git checkout -b deepest base &&
132 echo "updated deepest" >deep/deeper1/deepest/a &&
133 echo "updated deepest2" >deep/deeper1/deepest2/a &&
134 echo "updated deepest3" >deep/deeper1/deepest3/a &&
135 git commit -a -m "update deepest" &&
137 git checkout -f base &&
143 rm -rf full-checkout sparse-checkout sparse-index
&&
145 # create repos in initial state
146 cp -r initial-repo full-checkout
&&
147 git
-C full-checkout
reset --hard &&
149 cp -r initial-repo sparse-checkout
&&
150 git
-C sparse-checkout
reset --hard &&
152 cp -r initial-repo sparse-index
&&
153 git
-C sparse-index
reset --hard &&
155 # initialize sparse-checkout definitions
156 git
-C sparse-checkout sparse-checkout init
--cone &&
157 git
-C sparse-checkout sparse-checkout
set deep
&&
158 git
-C sparse-index sparse-checkout init
--cone --sparse-index &&
159 test_cmp_config
-C sparse-index true index.sparse
&&
160 git
-C sparse-index sparse-checkout
set deep
165 cd sparse-checkout
&&
166 GIT_PROGRESS_DELAY
=100000 "$@" >..
/sparse-checkout-out
2>..
/sparse-checkout-err
170 GIT_PROGRESS_DELAY
=100000 "$@" >..
/sparse-index-out
2>..
/sparse-index-err
177 GIT_PROGRESS_DELAY
=100000 "$@" >..
/full-checkout-out
2>..
/full-checkout-err
184 test_cmp full-checkout-out sparse-checkout-out
&&
185 test_cmp full-checkout-out sparse-index-out
&&
186 test_cmp full-checkout-err sparse-checkout-err
&&
187 test_cmp full-checkout-err sparse-index-err
190 test_sparse_match
() {
191 run_on_sparse
"$@" &&
192 test_cmp sparse-checkout-out sparse-index-out
&&
193 test_cmp sparse-checkout-err sparse-index-err
196 test_sparse_unstaged
() {
198 for repo
in sparse-checkout sparse-index
200 # Skip "unmerged" paths
201 git
-C $repo diff --staged --diff-filter=u
-- "$file" >diff &&
202 test_must_be_empty
diff ||
return 1
206 test_expect_success
'sparse-index contents' '
209 git -C sparse-index ls-files --sparse --stage >cache &&
210 for dir in folder1 folder2 x
212 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
213 grep "040000 $TREE 0 $dir/" cache \
217 git -C sparse-index sparse-checkout set folder1 &&
219 git -C sparse-index ls-files --sparse --stage >cache &&
220 for dir in deep folder2 x
222 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
223 grep "040000 $TREE 0 $dir/" cache \
227 git -C sparse-index sparse-checkout set deep/deeper1 &&
229 git -C sparse-index ls-files --sparse --stage >cache &&
230 for dir in deep/deeper2 folder1 folder2 x
232 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
233 grep "040000 $TREE 0 $dir/" cache \
237 # Disabling the sparse-index replaces tree entries with full ones
238 git -C sparse-index sparse-checkout init --no-sparse-index &&
239 test_sparse_match git ls-files --stage --sparse
242 test_expect_success
'expanded in-memory index matches full index' '
244 test_sparse_match git ls-files --stage
247 test_expect_success
'status with options' '
249 test_sparse_match ls &&
250 test_all_match git status --porcelain=v2 &&
251 test_all_match git status --porcelain=v2 -z -u &&
252 test_all_match git status --porcelain=v2 -uno &&
253 run_on_all touch README.md &&
254 test_all_match git status --porcelain=v2 &&
255 test_all_match git status --porcelain=v2 -z -u &&
256 test_all_match git status --porcelain=v2 -uno &&
257 test_all_match git add README.md &&
258 test_all_match git status --porcelain=v2 &&
259 test_all_match git status --porcelain=v2 -z -u &&
260 test_all_match git status --porcelain=v2 -uno
263 test_expect_success
'status reports sparse-checkout' '
265 git -C sparse-checkout status >full &&
266 git -C sparse-index status >sparse &&
267 test_i18ngrep "You are in a sparse checkout with " full &&
268 test_i18ngrep "You are in a sparse checkout." sparse
271 test_expect_success
'add, commit, checkout' '
274 write_script edit-contents <<-\EOF &&
277 run_on_all ../edit-contents README.md &&
279 test_all_match git add README.md &&
280 test_all_match git status --porcelain=v2 &&
281 test_all_match git commit -m "Add README.md" &&
283 test_all_match git checkout HEAD~1 &&
284 test_all_match git checkout - &&
286 run_on_all ../edit-contents README.md &&
288 test_all_match git add -A &&
289 test_all_match git status --porcelain=v2 &&
290 test_all_match git commit -m "Extend README.md" &&
292 test_all_match git checkout HEAD~1 &&
293 test_all_match git checkout - &&
295 run_on_all ../edit-contents deep/newfile &&
297 test_all_match git status --porcelain=v2 -uno &&
298 test_all_match git status --porcelain=v2 &&
299 test_all_match git add . &&
300 test_all_match git status --porcelain=v2 &&
301 test_all_match git commit -m "add deep/newfile" &&
303 test_all_match git checkout HEAD~1 &&
304 test_all_match git checkout -
307 test_expect_success
'deep changes during checkout' '
310 test_sparse_match git sparse-checkout set deep/deeper1/deepest &&
311 test_all_match git checkout deepest &&
312 test_all_match git checkout base
315 test_expect_success
'add outside sparse cone' '
318 run_on_sparse mkdir folder1 &&
319 run_on_sparse ../edit-contents folder1/a &&
320 run_on_sparse ../edit-contents folder1/newfile &&
321 test_sparse_match test_must_fail git add folder1/a &&
322 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
323 test_sparse_unstaged folder1/a &&
324 test_sparse_match test_must_fail git add folder1/newfile &&
325 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
326 test_sparse_unstaged folder1/newfile
329 test_expect_success
'commit including unstaged changes' '
332 write_script edit-file <<-\EOF &&
336 run_on_all ../edit-file 1 a &&
337 run_on_all ../edit-file 1 deep/a &&
339 test_all_match git commit -m "-a" -a &&
340 test_all_match git status --porcelain=v2 &&
342 run_on_all ../edit-file 2 a &&
343 run_on_all ../edit-file 2 deep/a &&
345 test_all_match git commit -m "--include" --include deep/a &&
346 test_all_match git status --porcelain=v2 &&
347 test_all_match git commit -m "--include" --include a &&
348 test_all_match git status --porcelain=v2 &&
350 run_on_all ../edit-file 3 a &&
351 run_on_all ../edit-file 3 deep/a &&
353 test_all_match git commit -m "--amend" -a --amend &&
354 test_all_match git status --porcelain=v2
357 test_expect_success
'status/add: outside sparse cone' '
360 # folder1 is at HEAD, but outside the sparse cone
361 run_on_sparse mkdir folder1 &&
362 cp initial-repo/folder1/a sparse-checkout/folder1/a &&
363 cp initial-repo/folder1/a sparse-index/folder1/a &&
365 test_sparse_match git status &&
367 write_script edit-contents <<-\EOF &&
370 run_on_sparse ../edit-contents folder1/a &&
371 run_on_all ../edit-contents folder1/new &&
373 test_sparse_match git status --porcelain=v2 &&
375 # Adding the path outside of the sparse-checkout cone should fail.
376 test_sparse_match test_must_fail git add folder1/a &&
377 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
378 test_sparse_unstaged folder1/a &&
379 test_sparse_match test_must_fail git add --refresh folder1/a &&
380 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
381 test_sparse_unstaged folder1/a &&
382 test_sparse_match test_must_fail git add folder1/new &&
383 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
384 test_sparse_unstaged folder1/new &&
385 test_sparse_match git add --sparse folder1/a &&
386 test_sparse_match git add --sparse folder1/new &&
388 test_all_match git add --sparse . &&
389 test_all_match git status --porcelain=v2 &&
390 test_all_match git commit -m folder1/new &&
391 test_all_match git rev-parse HEAD^{tree} &&
393 run_on_all ../edit-contents folder1/newer &&
394 test_all_match git add --sparse folder1/ &&
395 test_all_match git status --porcelain=v2 &&
396 test_all_match git commit -m folder1/newer &&
397 test_all_match git rev-parse HEAD^{tree}
400 test_expect_success
'checkout and reset --hard' '
403 test_all_match git checkout update-folder1 &&
404 test_all_match git status --porcelain=v2 &&
406 test_all_match git checkout update-deep &&
407 test_all_match git status --porcelain=v2 &&
409 test_all_match git checkout -b reset-test &&
410 test_all_match git reset --hard deepest &&
411 test_all_match git reset --hard update-folder1 &&
412 test_all_match git reset --hard update-folder2
415 test_expect_success
'diff --cached' '
418 write_script edit-contents <<-\EOF &&
419 echo text >>README.md
421 run_on_all ../edit-contents &&
423 test_all_match git diff &&
424 test_all_match git diff --cached &&
425 test_all_match git add README.md &&
426 test_all_match git diff &&
427 test_all_match git diff --cached
430 # NEEDSWORK: sparse-checkout behaves differently from full-checkout when
431 # running this test with 'df-conflict-2' after 'df-conflict-1'.
432 test_expect_success
'diff with renames and conflicts' '
435 for branch in rename-out-to-out \
441 test_all_match git checkout rename-base &&
442 test_all_match git checkout $branch -- . &&
443 test_all_match git status --porcelain=v2 &&
444 test_all_match git diff --cached --no-renames &&
445 test_all_match git diff --cached --find-renames || return 1
449 test_expect_success
'diff with directory/file conflicts' '
452 for branch in rename-out-to-out \
459 git -C full-checkout reset --hard &&
460 test_sparse_match git reset --hard &&
461 test_all_match git checkout $branch &&
462 test_all_match git checkout rename-base -- . &&
463 test_all_match git status --porcelain=v2 &&
464 test_all_match git diff --cached --no-renames &&
465 test_all_match git diff --cached --find-renames || return 1
469 test_expect_success
'log with pathspec outside sparse definition' '
472 test_all_match git log -- a &&
473 test_all_match git log -- folder1/a &&
474 test_all_match git log -- folder2/a &&
475 test_all_match git log -- deep/a &&
476 test_all_match git log -- deep/deeper1/a &&
477 test_all_match git log -- deep/deeper1/deepest/a &&
479 test_all_match git checkout update-folder1 &&
480 test_all_match git log -- folder1/a
483 test_expect_success
'blame with pathspec inside sparse definition' '
489 deep/deeper1/deepest/a
491 test_all_match git blame $file
495 # Without a revision specified, blame will error if passed any file that
496 # is not present in the working directory (even if the file is tracked).
497 # Here we just verify that this is also true with sparse checkouts.
498 test_expect_success
'blame with pathspec outside sparse definition' '
500 test_sparse_match git sparse-checkout set &&
505 deep/deeper1/deepest/a
507 test_sparse_match test_must_fail git blame $file &&
508 cat >expect <<-EOF &&
509 fatal: Cannot lstat '"'"'$file'"'"': No such file or directory
511 # We compare sparse-checkout-err and sparse-index-err in
512 # `test_sparse_match`. Given we know they are the same, we
513 # only check the content of sparse-index-err here.
514 test_cmp expect sparse-index-err
518 test_expect_success
'checkout and reset (mixed)' '
521 test_all_match git checkout -b reset-test update-deep &&
522 test_all_match git reset deepest &&
524 # Because skip-worktree is preserved, resetting to update-folder1
525 # will show worktree changes for folder1/a in full-checkout, but not
526 # in sparse-checkout or sparse-index.
527 git -C full-checkout reset update-folder1 >full-checkout-out &&
528 test_sparse_match git reset update-folder1 &&
529 grep "M folder1/a" full-checkout-out &&
530 ! grep "M folder1/a" sparse-checkout-out &&
531 run_on_sparse test_path_is_missing folder1
534 test_expect_success
'checkout and reset (merge)' '
537 write_script edit-contents <<-\EOF &&
541 test_all_match git checkout -b reset-test update-deep &&
542 run_on_all ../edit-contents a &&
543 test_all_match git reset --merge deepest &&
544 test_all_match git status --porcelain=v2 &&
546 test_all_match git reset --hard update-deep &&
547 run_on_all ../edit-contents deep/a &&
548 test_all_match test_must_fail git reset --merge deepest
551 test_expect_success
'checkout and reset (keep)' '
554 write_script edit-contents <<-\EOF &&
558 test_all_match git checkout -b reset-test update-deep &&
559 run_on_all ../edit-contents a &&
560 test_all_match git reset --keep deepest &&
561 test_all_match git status --porcelain=v2 &&
563 test_all_match git reset --hard update-deep &&
564 run_on_all ../edit-contents deep/a &&
565 test_all_match test_must_fail git reset --keep deepest
568 test_expect_success
'reset with pathspecs inside sparse definition' '
571 write_script edit-contents <<-\EOF &&
575 test_all_match git checkout -b reset-test update-deep &&
576 run_on_all ../edit-contents deep/a &&
578 test_all_match git reset base -- deep/a &&
579 test_all_match git status --porcelain=v2 &&
581 test_all_match git reset base -- nonexistent-file &&
582 test_all_match git status --porcelain=v2 &&
584 test_all_match git reset deepest -- deep &&
585 test_all_match git status --porcelain=v2
588 # Although the working tree differs between full and sparse checkouts after
589 # reset, the state of the index is the same.
590 test_expect_success
'reset with pathspecs outside sparse definition' '
592 test_all_match git checkout -b reset-test base &&
594 test_sparse_match git reset update-folder1 -- folder1 &&
595 git -C full-checkout reset update-folder1 -- folder1 &&
596 test_all_match git ls-files -s -- folder1 &&
598 test_sparse_match git reset update-folder2 -- folder2/a &&
599 git -C full-checkout reset update-folder2 -- folder2/a &&
600 test_all_match git ls-files -s -- folder2/a
603 test_expect_success
'reset with wildcard pathspec' '
606 test_all_match git reset update-deep -- deep\* &&
607 test_all_match git ls-files -s -- deep &&
609 test_all_match git reset deepest -- deep\*\*\* &&
610 test_all_match git ls-files -s -- deep &&
612 # The following `git reset`s result in updating the index on files with
613 # `skip-worktree` enabled. To avoid failing due to discrepencies in reported
614 # "modified" files, `test_sparse_match` reset is performed separately from
615 # "full-checkout" reset, then the index contents of all repos are verified.
617 test_sparse_match git reset update-folder1 -- \*/a &&
618 git -C full-checkout reset update-folder1 -- \*/a &&
619 test_all_match git ls-files -s -- deep/a folder1/a &&
621 test_sparse_match git reset update-folder2 -- folder\* &&
622 git -C full-checkout reset update-folder2 -- folder\* &&
623 test_all_match git ls-files -s -- folder10 folder1 folder2 &&
625 test_sparse_match git reset base -- folder1/\* &&
626 git -C full-checkout reset base -- folder1/\* &&
627 test_all_match git ls-files -s -- folder1
630 test_expect_success
'update-index modify outside sparse definition' '
633 write_script edit-contents <<-\EOF &&
637 # Create & modify folder1/a
638 # Note that this setup is a manual way of reaching the erroneous
639 # condition in which a `skip-worktree` enabled, outside-of-cone file
640 # exists on disk. It is used here to ensure `update-index` is stable
641 # and behaves predictably if such a condition occurs.
642 run_on_sparse mkdir -p folder1 &&
643 run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
644 run_on_all ../edit-contents folder1/a &&
646 # If file has skip-worktree enabled, update-index does not modify the
648 test_sparse_match git update-index folder1/a &&
649 test_sparse_match git status --porcelain=v2 &&
650 test_must_be_empty sparse-checkout-out &&
652 # When skip-worktree is disabled (even on files outside sparse cone), file
653 # is updated in the index
654 test_sparse_match git update-index --no-skip-worktree folder1/a &&
655 test_all_match git status --porcelain=v2 &&
656 test_all_match git update-index folder1/a &&
657 test_all_match git status --porcelain=v2
660 test_expect_success
'update-index --add outside sparse definition' '
663 write_script edit-contents <<-\EOF &&
667 # Create folder1, add new file
668 run_on_sparse mkdir -p folder1 &&
669 run_on_all ../edit-contents folder1/b &&
671 # The *untracked* out-of-cone file is added to the index because it does
672 # not have a `skip-worktree` bit to signal that it should be ignored
673 # (unlike in `git add`, which will fail due to the file being outside
674 # the sparse checkout definition).
675 test_all_match git update-index --add folder1/b &&
676 test_all_match git status --porcelain=v2
679 # NEEDSWORK: `--remove`, unlike the rest of `update-index`, does not ignore
680 # `skip-worktree` entries by default and will remove them from the index.
681 # The `--ignore-skip-worktree-entries` flag must be used in conjunction with
682 # `--remove` to ignore the `skip-worktree` entries and prevent their removal
684 test_expect_success
'update-index --remove outside sparse definition' '
687 # When --ignore-skip-worktree-entries is _not_ specified:
688 # out-of-cone, not-on-disk files are removed from the index
689 test_sparse_match git update-index --remove folder1/a &&
690 cat >expect <<-EOF &&
693 test_sparse_match git diff --cached --name-status &&
694 test_cmp expect sparse-checkout-out &&
697 test_all_match git reset --hard &&
699 # When --ignore-skip-worktree-entries is specified, out-of-cone
700 # (skip-worktree) files are ignored
701 test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
702 test_sparse_match git diff --cached --name-status &&
703 test_must_be_empty sparse-checkout-out &&
706 test_all_match git reset --hard &&
708 # --force-remove supercedes --ignore-skip-worktree-entries, removing
709 # a skip-worktree file from the index (and disk) when both are specified
711 test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
712 cat >expect <<-EOF &&
715 test_sparse_match git diff --cached --name-status &&
716 test_cmp expect sparse-checkout-out
719 test_expect_success
'update-index with directories' '
722 # update-index will exit silently when provided with a directory name
723 # containing a trailing slash
724 test_all_match git update-index deep/ folder1/ &&
725 grep "Ignoring path deep/" sparse-checkout-err &&
726 grep "Ignoring path folder1/" sparse-checkout-err &&
728 # When update-index is given a directory name WITHOUT a trailing slash, it will
729 # behave in different ways depending on the status of the directory on disk:
730 # * if it exists, the command exits with an error ("add individual files instead")
731 # * if it does NOT exist (e.g., in a sparse-checkout), it is assumed to be a
732 # file and either triggers an error ("does not exist and --remove not passed")
733 # or is ignored completely (when using --remove)
734 test_all_match test_must_fail git update-index deep &&
735 run_on_all test_must_fail git update-index folder1 &&
736 test_must_fail git -C full-checkout update-index --remove folder1 &&
737 test_sparse_match git update-index --remove folder1 &&
738 test_all_match git status --porcelain=v2
741 test_expect_success
'update-index --again file outside sparse definition' '
744 test_all_match git checkout -b test-reupdate &&
746 # Update HEAD without modifying the index to introduce a difference in
748 test_sparse_match git reset --soft update-folder1 &&
750 # Because folder1/a differs in the index vs HEAD,
751 # `git update-index --no-skip-worktree --again` will effectively perform
752 # `git update-index --no-skip-worktree folder1/a` and remove the skip-worktree
753 # flag from folder1/a
754 test_sparse_match git update-index --no-skip-worktree --again &&
755 test_sparse_match git status --porcelain=v2 &&
757 cat >expect <<-EOF &&
760 test_sparse_match git diff --name-status &&
761 test_cmp expect sparse-checkout-out
764 test_expect_success
'update-index --cacheinfo' '
767 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
768 folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
769 folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
771 test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
772 test_all_match git status --porcelain=v2 &&
774 # Cannot add sparse directory, even in sparse index case
775 test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
777 # Sparse match only: the new outside-of-cone entry is added *without* skip-worktree,
778 # so `git status` reports it as "deleted" in the worktree
779 test_sparse_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
780 test_sparse_match git status --porcelain=v2 &&
781 cat >expect <<-EOF &&
784 test_sparse_match git status --short -- folder1/a &&
785 test_cmp expect sparse-checkout-out &&
787 # To return folder1/a to "normal" for a sparse checkout (ignored &
788 # outside-of-cone), add the skip-worktree flag.
789 test_sparse_match git update-index --skip-worktree folder1/a &&
790 cat >expect <<-EOF &&
793 test_sparse_match git ls-files -t -- folder1/a &&
794 test_cmp expect sparse-checkout-out
797 test_expect_success
'merge, cherry-pick, and rebase' '
800 for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge"
802 test_all_match git checkout -B temp update-deep &&
803 test_all_match git $OPERATION update-folder1 &&
804 test_all_match git rev-parse HEAD^{tree} &&
805 test_all_match git $OPERATION update-folder2 &&
806 test_all_match git rev-parse HEAD^{tree} || return 1
810 test_expect_success
'merge with conflict outside cone' '
813 test_all_match git checkout -b merge-tip merge-left &&
814 test_all_match git status --porcelain=v2 &&
815 test_all_match test_must_fail git merge -m merge merge-right &&
816 test_all_match git status --porcelain=v2 &&
818 # Resolve the conflict in different ways:
819 # 1. Revert to the base
820 test_all_match git checkout base -- deep/deeper2/a &&
821 test_all_match git status --porcelain=v2 &&
823 # 2. Add the file with conflict markers
824 test_sparse_match test_must_fail git add folder1/a &&
825 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
826 test_sparse_unstaged folder1/a &&
827 test_all_match git add --sparse folder1/a &&
828 test_all_match git status --porcelain=v2 &&
830 # 3. Rename the file to another sparse filename and
831 # accept conflict markers as resolved content.
832 run_on_all mv folder2/a folder2/z &&
833 test_sparse_match test_must_fail git add folder2 &&
834 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
835 test_sparse_unstaged folder2/z &&
836 test_all_match git add --sparse folder2 &&
837 test_all_match git status --porcelain=v2 &&
839 test_all_match git merge --continue &&
840 test_all_match git status --porcelain=v2 &&
841 test_all_match git rev-parse HEAD^{tree}
844 test_expect_success
'cherry-pick/rebase with conflict outside cone' '
847 for OPERATION in cherry-pick rebase
849 test_all_match git checkout -B tip &&
850 test_all_match git reset --hard merge-left &&
851 test_all_match git status --porcelain=v2 &&
852 test_all_match test_must_fail git $OPERATION merge-right &&
853 test_all_match git status --porcelain=v2 &&
855 # Resolve the conflict in different ways:
856 # 1. Revert to the base
857 test_all_match git checkout base -- deep/deeper2/a &&
858 test_all_match git status --porcelain=v2 &&
860 # 2. Add the file with conflict markers
861 # NEEDSWORK: Even though the merge conflict removed the
862 # SKIP_WORKTREE bit from the index entry for folder1/a, we should
863 # warn that this is a problematic add.
864 test_sparse_match test_must_fail git add folder1/a &&
865 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
866 test_sparse_unstaged folder1/a &&
867 test_all_match git add --sparse folder1/a &&
868 test_all_match git status --porcelain=v2 &&
870 # 3. Rename the file to another sparse filename and
871 # accept conflict markers as resolved content.
872 # NEEDSWORK: This mode now fails, because folder2/z is
873 # outside of the sparse-checkout cone and does not match an
874 # existing index entry with the SKIP_WORKTREE bit cleared.
875 run_on_all mv folder2/a folder2/z &&
876 test_sparse_match test_must_fail git add folder2 &&
877 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
878 test_sparse_unstaged folder2/z &&
879 test_all_match git add --sparse folder2 &&
880 test_all_match git status --porcelain=v2 &&
882 test_all_match git $OPERATION --continue &&
883 test_all_match git status --porcelain=v2 &&
884 test_all_match git rev-parse HEAD^{tree} || return 1
888 test_expect_success
'merge with outside renames' '
891 for type in out-to-out out-to-in in-to-out
893 test_all_match git reset --hard &&
894 test_all_match git checkout -f -b merge-$type update-deep &&
895 test_all_match git merge -m "$type" rename-$type &&
896 test_all_match git rev-parse HEAD^{tree} || return 1
900 # Sparse-index fails to convert the index in the
901 # final 'git cherry-pick' command.
902 test_expect_success
'cherry-pick with conflicts' '
905 write_script edit-conflict <<-\EOF &&
909 test_all_match git checkout -b to-cherry-pick &&
910 run_on_all ../edit-conflict ABC &&
911 test_all_match git add conflict &&
912 test_all_match git commit -m "conflict to pick" &&
914 test_all_match git checkout -B base HEAD~1 &&
915 run_on_all ../edit-conflict DEF &&
916 test_all_match git add conflict &&
917 test_all_match git commit -m "conflict in base" &&
919 test_all_match test_must_fail git cherry-pick to-cherry-pick
922 test_expect_success
'checkout-index inside sparse definition' '
925 run_on_all rm -f deep/a &&
926 test_all_match git checkout-index -- deep/a &&
927 test_all_match git status --porcelain=v2 &&
930 run_on_all cp ../new-a a &&
931 test_all_match test_must_fail git checkout-index -- a &&
932 test_all_match git checkout-index -f -- a &&
933 test_all_match git status --porcelain=v2
936 test_expect_success
'checkout-index outside sparse definition' '
939 # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
941 test_sparse_match test_must_fail git checkout-index -- folder1/a &&
942 test_i18ngrep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
943 test_path_is_missing folder1/a &&
945 # With --ignore-skip-worktree-bits, outside-of-cone files are checked out
946 test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
947 test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
948 test_cmp sparse-checkout/folder1/a full-checkout/folder1/a &&
950 run_on_sparse rm -rf folder1 &&
952 run_on_sparse mkdir -p folder1 &&
953 run_on_all cp ../new-a folder1/a &&
955 test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
956 test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a &&
957 test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
958 test_cmp sparse-checkout/folder1/a full-checkout/folder1/a
961 test_expect_success
'checkout-index with folders' '
964 # Inside checkout definition
965 test_all_match test_must_fail git checkout-index -f -- deep/ &&
967 # Outside checkout definition
968 # Note: although all tests fail (as expected), the messaging differs. For
969 # non-sparse index checkouts, the error is that the "file" does not appear
970 # in the index; for sparse checkouts, the error is explicitly that the
971 # entry is a sparse directory.
972 run_on_all test_must_fail git checkout-index -f -- folder1/ &&
973 test_cmp full-checkout-err sparse-checkout-err &&
974 ! test_cmp full-checkout-err sparse-index-err &&
975 grep "is a sparse directory" sparse-index-err
978 test_expect_success
'checkout-index --all' '
981 test_all_match git checkout-index --all &&
982 test_sparse_match test_path_is_missing folder1 &&
984 # --ignore-skip-worktree-bits will cause `skip-worktree` files to be
985 # checked out, causing the outside-of-cone `folder1` to exist on-disk
986 test_all_match git checkout-index --ignore-skip-worktree-bits --all &&
987 test_all_match test_path_exists folder1
990 test_expect_success
'clean' '
993 echo bogus >>.gitignore &&
994 run_on_all cp ../.gitignore . &&
995 test_all_match git add .gitignore &&
996 test_all_match git commit -m "ignore bogus files" &&
998 run_on_sparse mkdir folder1 &&
999 run_on_all mkdir -p deep/untracked-deep &&
1000 run_on_all touch folder1/bogus &&
1001 run_on_all touch folder1/untracked &&
1002 run_on_all touch deep/untracked-deep/bogus &&
1003 run_on_all touch deep/untracked-deep/untracked &&
1005 test_all_match git status --porcelain=v2 &&
1006 test_all_match git clean -f &&
1007 test_all_match git status --porcelain=v2 &&
1008 test_sparse_match ls &&
1009 test_sparse_match ls folder1 &&
1010 run_on_all test_path_exists folder1/bogus &&
1011 run_on_all test_path_is_missing folder1/untracked &&
1012 run_on_all test_path_exists deep/untracked-deep/bogus &&
1013 run_on_all test_path_exists deep/untracked-deep/untracked &&
1015 test_all_match git clean -fd &&
1016 test_all_match git status --porcelain=v2 &&
1017 test_sparse_match ls &&
1018 test_sparse_match ls folder1 &&
1019 run_on_all test_path_exists folder1/bogus &&
1020 run_on_all test_path_exists deep/untracked-deep/bogus &&
1021 run_on_all test_path_is_missing deep/untracked-deep/untracked &&
1023 test_all_match git clean -xf &&
1024 test_all_match git status --porcelain=v2 &&
1025 test_sparse_match ls &&
1026 test_sparse_match ls folder1 &&
1027 run_on_all test_path_is_missing folder1/bogus &&
1028 run_on_all test_path_exists deep/untracked-deep/bogus &&
1030 test_all_match git clean -xdf &&
1031 test_all_match git status --porcelain=v2 &&
1032 test_sparse_match ls &&
1033 test_sparse_match ls folder1 &&
1034 run_on_all test_path_is_missing deep/untracked-deep/bogus &&
1036 test_sparse_match test_path_is_dir folder1
1039 test_expect_success
'submodule handling' '
1042 test_sparse_match git sparse-checkout add modules &&
1043 test_all_match mkdir modules &&
1044 test_all_match touch modules/a &&
1045 test_all_match git add modules &&
1046 test_all_match git commit -m "add modules directory" &&
1048 run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
1049 test_all_match git commit -m "add submodule" &&
1051 # having a submodule prevents "modules" from collapse
1052 test_sparse_match git sparse-checkout set deep/deeper1 &&
1053 git -C sparse-index ls-files --sparse --stage >cache &&
1054 grep "100644 .* modules/a" cache &&
1055 grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache
1058 # When working with a sparse index, some commands will need to expand the
1059 # index to operate properly. If those commands also write the index back
1060 # to disk, they need to convert the index to sparse before writing.
1061 # This test verifies that both of these events are logged in trace2 logs.
1062 test_expect_success
'sparse-index is expanded and converted back' '
1065 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
1066 git -C sparse-index reset -- folder1/a &&
1067 test_region index convert_to_sparse trace2.txt &&
1068 test_region index ensure_full_index trace2.txt &&
1070 # ls-files expands on read, but does not write.
1072 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1073 git -C sparse-index ls-files &&
1074 test_region index ensure_full_index trace2.txt
1077 test_expect_success
'index.sparse disabled inline uses full index' '
1080 # When index.sparse is disabled inline with `git status`, the
1081 # index is expanded at the beginning of the execution then never
1082 # converted back to sparse. It is then written to disk as a full index.
1084 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1085 git -C sparse-index -c index.sparse=false status &&
1086 ! test_region index convert_to_sparse trace2.txt &&
1087 test_region index ensure_full_index trace2.txt &&
1089 # Since index.sparse is set to true at a repo level, the index
1090 # is converted from full to sparse when read, then never expanded
1091 # over the course of `git status`. It is written to disk as a sparse
1094 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1095 git -C sparse-index status &&
1096 test_region index convert_to_sparse trace2.txt &&
1097 ! test_region index ensure_full_index trace2.txt &&
1099 # Now that the index has been written to disk as sparse, it is not
1100 # converted to sparse (or expanded to full) when read by `git status`.
1102 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1103 git -C sparse-index status &&
1104 ! test_region index convert_to_sparse trace2.txt &&
1105 ! test_region index ensure_full_index trace2.txt
1108 ensure_not_expanded
() {
1110 echo >>sparse-index
/untracked.txt
&&
1115 test_must_fail env \
1116 GIT_TRACE2_EVENT
="$(pwd)/trace2.txt" \
1117 git
-C sparse-index
"$@" ||
return 1
1119 GIT_TRACE2_EVENT
="$(pwd)/trace2.txt" \
1120 git
-C sparse-index
"$@" ||
return 1
1122 test_region
! index ensure_full_index trace2.txt
1125 test_expect_success
'sparse-index is not expanded' '
1128 ensure_not_expanded status &&
1129 ensure_not_expanded ls-files --sparse &&
1130 ensure_not_expanded commit --allow-empty -m empty &&
1131 echo >>sparse-index/a &&
1132 ensure_not_expanded commit -a -m a &&
1133 echo >>sparse-index/a &&
1134 ensure_not_expanded commit --include a -m a &&
1135 echo >>sparse-index/deep/deeper1/a &&
1136 ensure_not_expanded commit --include deep/deeper1/a -m deeper &&
1137 ensure_not_expanded checkout rename-out-to-out &&
1138 ensure_not_expanded checkout - &&
1139 ensure_not_expanded switch rename-out-to-out &&
1140 ensure_not_expanded switch - &&
1141 ensure_not_expanded reset --hard &&
1142 ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 &&
1143 ensure_not_expanded reset --hard &&
1144 ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 &&
1146 echo >>sparse-index/README.md &&
1147 ensure_not_expanded add -A &&
1148 echo >>sparse-index/extra.txt &&
1149 ensure_not_expanded add extra.txt &&
1150 echo >>sparse-index/untracked.txt &&
1151 ensure_not_expanded add . &&
1153 ensure_not_expanded checkout-index -f a &&
1154 ensure_not_expanded checkout-index -f --all &&
1155 for ref in update-deep update-folder1 update-folder2 update-deep
1157 echo >>sparse-index/README.md &&
1158 ensure_not_expanded reset --hard $ref || return 1
1161 ensure_not_expanded reset --mixed base &&
1162 ensure_not_expanded reset --hard update-deep &&
1163 ensure_not_expanded reset --keep base &&
1164 ensure_not_expanded reset --merge update-deep &&
1165 ensure_not_expanded reset --hard &&
1167 ensure_not_expanded reset base -- deep/a &&
1168 ensure_not_expanded reset base -- nonexistent-file &&
1169 ensure_not_expanded reset deepest -- deep &&
1171 # Although folder1 is outside the sparse definition, it exists as a
1172 # directory entry in the index, so the pathspec will not force the
1173 # index to be expanded.
1174 ensure_not_expanded reset deepest -- folder1 &&
1175 ensure_not_expanded reset deepest -- folder1/ &&
1177 # Wildcard identifies only in-cone files, no index expansion
1178 ensure_not_expanded reset deepest -- deep/\* &&
1180 # Wildcard identifies only full sparse directories, no index expansion
1181 ensure_not_expanded reset deepest -- folder\* &&
1183 ensure_not_expanded clean -fd &&
1185 ensure_not_expanded checkout -f update-deep &&
1186 test_config -C sparse-index pull.twohead ort &&
1188 sane_unset GIT_TEST_MERGE_ALGORITHM &&
1189 for OPERATION in "merge -m merge" cherry-pick rebase
1191 ensure_not_expanded merge -m merge update-folder1 &&
1192 ensure_not_expanded merge -m merge update-folder2 || return 1
1197 test_expect_success
'sparse-index is not expanded: merge conflict in cone' '
1200 for side in right left
1202 git -C sparse-index checkout -b expand-$side base &&
1203 echo $side >sparse-index/deep/a &&
1204 git -C sparse-index commit -a -m "$side" || return 1
1208 sane_unset GIT_TEST_MERGE_ALGORITHM &&
1209 git -C sparse-index config pull.twohead ort &&
1210 ensure_not_expanded ! merge -m merged expand-right
1214 test_expect_success
'sparse index is not expanded: diff' '
1217 write_script edit-contents <<-\EOF &&
1221 # Add file within cone
1222 test_sparse_match git sparse-checkout set deep &&
1223 run_on_all ../edit-contents deep/testfile &&
1224 test_all_match git add deep/testfile &&
1225 run_on_all ../edit-contents deep/testfile &&
1227 test_all_match git diff &&
1228 test_all_match git diff --cached &&
1229 ensure_not_expanded diff &&
1230 ensure_not_expanded diff --cached &&
1232 # Add file outside cone
1233 test_all_match git reset --hard &&
1234 run_on_all mkdir newdirectory &&
1235 run_on_all ../edit-contents newdirectory/testfile &&
1236 test_sparse_match git sparse-checkout set newdirectory &&
1237 test_all_match git add newdirectory/testfile &&
1238 run_on_all ../edit-contents newdirectory/testfile &&
1239 test_sparse_match git sparse-checkout set &&
1241 test_all_match git diff &&
1242 test_all_match git diff --cached &&
1243 ensure_not_expanded diff &&
1244 ensure_not_expanded diff --cached &&
1246 # Merge conflict outside cone
1247 # The sparse checkout will report a warning that is not in the
1248 # full checkout, so we use `run_on_all` instead of
1250 run_on_all git reset --hard &&
1251 test_all_match git checkout merge-left &&
1252 test_all_match test_must_fail git merge merge-right &&
1254 test_all_match git diff &&
1255 test_all_match git diff --cached &&
1256 ensure_not_expanded diff &&
1257 ensure_not_expanded diff --cached
1260 test_expect_success
'sparse index is not expanded: update-index' '
1263 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
1264 ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a &&
1266 echo "test" >sparse-index/README.md &&
1267 echo "test2" >sparse-index/a &&
1268 rm -f sparse-index/deep/a &&
1270 ensure_not_expanded update-index --add README.md &&
1271 ensure_not_expanded update-index a &&
1272 ensure_not_expanded update-index --remove deep/a &&
1274 ensure_not_expanded reset --soft update-deep &&
1275 ensure_not_expanded update-index --add --remove --again
1278 test_expect_success
'sparse index is not expanded: blame' '
1284 deep/deeper1/deepest/a
1286 ensure_not_expanded blame $file
1290 test_expect_success
'sparse index is not expanded: fetch/pull' '
1293 git -C sparse-index remote add full "file://$(pwd)/full-checkout" &&
1294 ensure_not_expanded fetch full &&
1295 git -C full-checkout commit --allow-empty -m "for pull merge" &&
1296 git -C sparse-index commit --allow-empty -m "for pull merge" &&
1297 ensure_not_expanded pull full base
1300 test_expect_success
'ls-files' '
1303 # Use a smaller sparse-checkout for reduced output
1304 test_sparse_match git sparse-checkout set &&
1306 # Behavior agrees by default. Sparse index is expanded.
1307 test_all_match git ls-files &&
1309 # With --sparse, the sparse index data changes behavior.
1310 git -C sparse-index ls-files --sparse >actual &&
1312 cat >expect <<-\EOF &&
1326 test_cmp expect actual &&
1328 # With --sparse and no sparse index, nothing changes.
1329 git -C sparse-checkout ls-files >dense &&
1330 git -C sparse-checkout ls-files --sparse >sparse &&
1331 test_cmp dense sparse &&
1333 # Set up a strange condition of having a file edit
1334 # outside of the sparse-checkout cone. This is just
1335 # to verify that sparse-checkout and sparse-index
1336 # behave the same in this case.
1337 write_script edit-content <<-\EOF &&
1339 echo content >>folder1/a
1341 run_on_sparse ../edit-content &&
1343 # ls-files does not currently notice modified files whose
1344 # cache entries are marked SKIP_WORKTREE. This may change
1345 # in the future, but here we test that sparse index does
1346 # not accidentally create a change of behavior.
1347 test_sparse_match git ls-files --modified &&
1348 test_must_be_empty sparse-checkout-out &&
1349 test_must_be_empty sparse-index-out &&
1351 git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
1352 test_must_be_empty sparse-index-out &&
1354 # Add folder1 to the sparse-checkout cone and
1355 # check that ls-files shows the expanded files.
1356 test_sparse_match git sparse-checkout add folder1 &&
1357 test_sparse_match git ls-files --modified &&
1359 test_all_match git ls-files &&
1360 git -C sparse-index ls-files --sparse >actual &&
1362 cat >expect <<-\EOF &&
1378 test_cmp expect actual &&
1380 # Double-check index expansion is avoided
1381 ensure_not_expanded ls-files --sparse
1384 # NEEDSWORK: a sparse-checkout behaves differently from a full checkout
1385 # in this scenario, but it shouldn't.
1386 test_expect_success
'reset mixed and checkout orphan' '
1389 test_all_match git checkout rename-out-to-in &&
1391 # Sparse checkouts do not agree with full checkouts about
1392 # how to report a directory/file conflict during a reset.
1393 # This command would fail with test_all_match because the
1394 # full checkout reports "T folder1/0/1" while a sparse
1395 # checkout reports "D folder1/0/1". This matches because
1396 # the sparse checkouts skip "adding" the other side of
1398 test_sparse_match git reset --mixed HEAD~1 &&
1399 test_sparse_match git ls-files --stage &&
1400 test_sparse_match git status --porcelain=v2 &&
1402 # At this point, sparse-checkouts behave differently
1403 # from the full-checkout.
1404 test_sparse_match git checkout --orphan new-branch &&
1405 test_sparse_match git ls-files --stage &&
1406 test_sparse_match git status --porcelain=v2
1409 test_expect_success
'add everything with deep new file' '
1412 run_on_sparse git sparse-checkout set deep/deeper1/deepest &&
1414 run_on_all touch deep/deeper1/x &&
1415 test_all_match git add . &&
1416 test_all_match git status --porcelain=v2
1419 # NEEDSWORK: 'git checkout' behaves incorrectly in the case of
1420 # directory/file conflicts, even without sparse-checkout. Use this
1421 # test only as a documentation of the incorrect behavior, not a
1422 # measure of how it _should_ behave.
1423 test_expect_success
'checkout behaves oddly with df-conflict-1' '
1426 test_sparse_match git sparse-checkout disable &&
1428 write_script edit-content <<-\EOF &&
1429 echo content >>folder1/larger-content
1433 run_on_all ../edit-content &&
1434 test_all_match git status --porcelain=v2 &&
1436 git -C sparse-checkout sparse-checkout init --cone &&
1437 git -C sparse-index sparse-checkout init --cone --sparse-index &&
1439 test_all_match git status --porcelain=v2 &&
1441 # This checkout command should fail, because we have a staged
1442 # change to folder1/larger-content, but the destination changes
1443 # folder1 to a file.
1444 git -C full-checkout checkout df-conflict-1 \
1445 1>full-checkout-out \
1446 2>full-checkout-err &&
1447 git -C sparse-checkout checkout df-conflict-1 \
1448 1>sparse-checkout-out \
1449 2>sparse-checkout-err &&
1450 git -C sparse-index checkout df-conflict-1 \
1451 1>sparse-index-out \
1452 2>sparse-index-err &&
1454 # Instead, the checkout deletes the folder1 file and adds the
1455 # folder1/larger-content file, leaving all other paths that were
1456 # in folder1/ as deleted (without any warning).
1457 cat >expect <<-EOF &&
1459 A folder1/larger-content
1461 test_cmp expect full-checkout-out &&
1462 test_cmp expect sparse-checkout-out &&
1464 # The sparse-index reports no output
1465 test_must_be_empty sparse-index-out &&
1467 # stderr: Switched to branch df-conflict-1
1468 test_cmp full-checkout-err sparse-checkout-err &&
1469 test_cmp full-checkout-err sparse-checkout-err
1472 # NEEDSWORK: 'git checkout' behaves incorrectly in the case of
1473 # directory/file conflicts, even without sparse-checkout. Use this
1474 # test only as a documentation of the incorrect behavior, not a
1475 # measure of how it _should_ behave.
1476 test_expect_success
'checkout behaves oddly with df-conflict-2' '
1479 test_sparse_match git sparse-checkout disable &&
1481 write_script edit-content <<-\EOF &&
1482 echo content >>folder2/larger-content
1486 run_on_all ../edit-content &&
1487 test_all_match git status --porcelain=v2 &&
1489 git -C sparse-checkout sparse-checkout init --cone &&
1490 git -C sparse-index sparse-checkout init --cone --sparse-index &&
1492 test_all_match git status --porcelain=v2 &&
1494 # This checkout command should fail, because we have a staged
1495 # change to folder1/larger-content, but the destination changes
1496 # folder1 to a file.
1497 git -C full-checkout checkout df-conflict-2 \
1498 1>full-checkout-out \
1499 2>full-checkout-err &&
1500 git -C sparse-checkout checkout df-conflict-2 \
1501 1>sparse-checkout-out \
1502 2>sparse-checkout-err &&
1503 git -C sparse-index checkout df-conflict-2 \
1504 1>sparse-index-out \
1505 2>sparse-index-err &&
1507 # The full checkout deviates from the df-conflict-1 case here!
1508 # It drops the change to folder1/larger-content and leaves the
1509 # folder1 path as-is on disk. The sparse-index behaves the same.
1510 test_must_be_empty full-checkout-out &&
1511 test_must_be_empty sparse-index-out &&
1513 # In the sparse-checkout case, the checkout deletes the folder1
1514 # file and adds the folder1/larger-content file, leaving all other
1515 # paths that were in folder1/ as deleted (without any warning).
1516 cat >expect <<-EOF &&
1518 A folder2/larger-content
1520 test_cmp expect sparse-checkout-out &&
1522 # Switched to branch df-conflict-1
1523 test_cmp full-checkout-err sparse-checkout-err &&
1524 test_cmp full-checkout-err sparse-index-err