The seventh batch
[git/debian.git] / t / t1092-sparse-checkout-compatibility.sh
blobf3a059e5af557aacf113909aa06e089d9fd43e8c
1 #!/bin/sh
3 test_description='compare full workdir to sparse workdir'
5 GIT_TEST_SPLIT_INDEX=0
6 GIT_TEST_SPARSE_INDEX=
8 . ./test-lib.sh
10 test_expect_success 'setup' '
11 git init initial-repo &&
13 GIT_TEST_SPARSE_INDEX=0 &&
14 cd initial-repo &&
15 echo a >a &&
16 echo "after deep" >e &&
17 echo "after folder1" >g &&
18 echo "after x" >z &&
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 &&
26 cp a folder1 &&
27 cp a folder2 &&
28 cp a x &&
29 cp a deep &&
30 cp a deep/before &&
31 cp a deep/deeper1 &&
32 cp a deep/deeper2 &&
33 cp a deep/later &&
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 &&
42 >folder1- &&
43 >folder1.x &&
44 >folder10 &&
45 cp -r deep/deeper1/0 folder1 &&
46 cp -r deep/deeper1/0 folder2 &&
47 echo >>folder1/0/0/0 &&
48 echo >>folder2/0/1 &&
49 git add . &&
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
57 done &&
59 git checkout -b rename-base base &&
60 cat >folder1/larger-content <<-\EOF &&
61 matching
62 lines
63 help
64 inexact
65 renames
66 EOF
67 cp folder1/larger-content folder2/ &&
68 cp folder1/larger-content deep/deeper1/ &&
69 git add . &&
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 &&
76 echo >>folder2/0/1 &&
77 echo stuff >>deep/deeper1/a &&
78 git add . &&
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 &&
84 rm folder2/0/1 &&
85 mkdir folder2/0/1 &&
86 echo >>folder2/0/1/1 &&
87 mv folder1/larger-content deep/deeper1/edited-content &&
88 echo >>deep/deeper1/edited-content &&
89 git add . &&
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 &&
94 echo >>folder2/0/1 &&
95 rm -rf folder1/0/0 &&
96 echo >>folder1/0/0 &&
97 mv deep/deeper1/larger-content folder1/edited-content &&
98 echo >>folder1/edited-content &&
99 git add . &&
100 git commit -m "rename deep/deeper1/... to folder1/..." &&
102 git checkout -b df-conflict-1 base &&
103 rm -rf folder1 &&
104 echo content >folder1 &&
105 git add . &&
106 git commit -m "dir to file" &&
108 git checkout -b df-conflict-2 base &&
109 rm -rf folder2 &&
110 echo content >folder2 &&
111 git add . &&
112 git commit -m "dir to file" &&
114 git checkout -b fd-conflict base &&
115 rm a &&
116 mkdir a &&
117 echo content >a/a &&
118 git add . &&
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 &&
127 git add . &&
128 git commit -m "$side" || return 1
129 done &&
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 &&
138 git reset --hard
142 init_repos () {
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
163 run_on_sparse () {
165 cd sparse-checkout &&
166 GIT_PROGRESS_DELAY=100000 "$@" >../sparse-checkout-out 2>../sparse-checkout-err
167 ) &&
169 cd sparse-index &&
170 GIT_PROGRESS_DELAY=100000 "$@" >../sparse-index-out 2>../sparse-index-err
174 run_on_all () {
176 cd full-checkout &&
177 GIT_PROGRESS_DELAY=100000 "$@" >../full-checkout-out 2>../full-checkout-err
178 ) &&
179 run_on_sparse "$@"
182 test_all_match () {
183 run_on_all "$@" &&
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 () {
197 file=$1 &&
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
203 done
206 test_expect_success 'sparse-index contents' '
207 init_repos &&
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 \
214 || return 1
215 done &&
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 \
224 || return 1
225 done &&
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 \
234 || return 1
235 done &&
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' '
243 init_repos &&
244 test_sparse_match git ls-files --stage
247 test_expect_success 'status with options' '
248 init_repos &&
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' '
264 init_repos &&
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' '
272 init_repos &&
274 write_script edit-contents <<-\EOF &&
275 echo text >>$1
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' '
308 init_repos &&
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' '
316 init_repos &&
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' '
330 init_repos &&
332 write_script edit-file <<-\EOF &&
333 echo $1 >$2
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' '
358 init_repos &&
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 &&
368 echo text >>$1
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' '
401 init_repos &&
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' '
416 init_repos &&
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' '
433 init_repos &&
435 for branch in rename-out-to-out \
436 rename-out-to-in \
437 rename-in-to-out \
438 df-conflict-1 \
439 fd-conflict
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
446 done
449 test_expect_success 'diff with directory/file conflicts' '
450 init_repos &&
452 for branch in rename-out-to-out \
453 rename-out-to-in \
454 rename-in-to-out \
455 df-conflict-1 \
456 df-conflict-2 \
457 fd-conflict
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
466 done
469 test_expect_success 'log with pathspec outside sparse definition' '
470 init_repos &&
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' '
484 init_repos &&
486 for file in a \
487 deep/a \
488 deep/deeper1/a \
489 deep/deeper1/deepest/a
491 test_all_match git blame $file
492 done
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' '
499 init_repos &&
500 test_sparse_match git sparse-checkout set &&
502 for file in a \
503 deep/a \
504 deep/deeper1/a \
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
515 done
518 test_expect_success 'checkout and reset (mixed)' '
519 init_repos &&
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)' '
535 init_repos &&
537 write_script edit-contents <<-\EOF &&
538 echo text >>$1
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)' '
552 init_repos &&
554 write_script edit-contents <<-\EOF &&
555 echo text >>$1
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' '
569 init_repos &&
571 write_script edit-contents <<-\EOF &&
572 echo text >>$1
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' '
591 init_repos &&
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' '
604 init_repos &&
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' '
631 init_repos &&
633 write_script edit-contents <<-\EOF &&
634 echo text >>$1
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
647 # index entry
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' '
661 init_repos &&
663 write_script edit-contents <<-\EOF &&
664 echo text >>$1
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
683 # from the index.
684 test_expect_success 'update-index --remove outside sparse definition' '
685 init_repos &&
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 &&
691 D folder1/a
693 test_sparse_match git diff --cached --name-status &&
694 test_cmp expect sparse-checkout-out &&
696 # Reset the state
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 &&
705 # Reset the state
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
710 # with --remove
711 test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
712 cat >expect <<-EOF &&
713 D folder1/a
715 test_sparse_match git diff --cached --name-status &&
716 test_cmp expect sparse-checkout-out
719 test_expect_success 'update-index with directories' '
720 init_repos &&
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' '
742 init_repos &&
744 test_all_match git checkout -b test-reupdate &&
746 # Update HEAD without modifying the index to introduce a difference in
747 # folder1/a
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 &&
758 D folder1/a
760 test_sparse_match git diff --name-status &&
761 test_cmp expect sparse-checkout-out
764 test_expect_success 'update-index --cacheinfo' '
765 init_repos &&
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 &&
782 MD folder1/a
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 &&
791 S folder1/a
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' '
798 init_repos &&
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
807 done
810 test_expect_success 'merge with conflict outside cone' '
811 init_repos &&
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' '
845 init_repos &&
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
885 done
888 test_expect_success 'merge with outside renames' '
889 init_repos &&
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
897 done
900 # Sparse-index fails to convert the index in the
901 # final 'git cherry-pick' command.
902 test_expect_success 'cherry-pick with conflicts' '
903 init_repos &&
905 write_script edit-conflict <<-\EOF &&
906 echo $1 >conflict
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' '
923 init_repos &&
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 &&
929 echo test >>new-a &&
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' '
937 init_repos &&
939 # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
940 # an error
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 &&
951 echo test >new-a &&
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' '
962 init_repos &&
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' '
979 init_repos &&
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' '
991 init_repos &&
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' '
1040 init_repos &&
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' '
1063 init_repos &&
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.
1071 rm trace2.txt &&
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' '
1078 init_repos &&
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.
1083 rm -f trace2.txt &&
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
1092 # index.
1093 rm -f trace2.txt &&
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`.
1101 rm -f trace2.txt &&
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 () {
1109 rm -f trace2.txt &&
1110 echo >>sparse-index/untracked.txt &&
1112 if test "$1" = "!"
1113 then
1114 shift &&
1115 test_must_fail env \
1116 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
1117 git -C sparse-index "$@" || return 1
1118 else
1119 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
1120 git -C sparse-index "$@" || return 1
1121 fi &&
1122 test_region ! index ensure_full_index trace2.txt
1125 test_expect_success 'sparse-index is not expanded' '
1126 init_repos &&
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
1159 done &&
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
1193 done
1197 test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
1198 init_repos &&
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
1205 done &&
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' '
1215 init_repos &&
1217 write_script edit-contents <<-\EOF &&
1218 echo text >>$1
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
1249 # `test_all_match`
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' '
1261 init_repos &&
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' '
1279 init_repos &&
1281 for file in a \
1282 deep/a \
1283 deep/deeper1/a \
1284 deep/deeper1/deepest/a
1286 ensure_not_expanded blame $file
1287 done
1290 test_expect_success 'sparse index is not expanded: fetch/pull' '
1291 init_repos &&
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' '
1301 init_repos &&
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 &&
1314 deep/
1316 folder1-
1317 folder1.x
1318 folder1/
1319 folder10
1320 folder2/
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 &&
1338 mkdir folder1 &&
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 &&
1364 deep/
1366 folder1-
1367 folder1.x
1368 folder1/0/0/0
1369 folder1/0/1
1370 folder1/a
1371 folder10
1372 folder2/
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' '
1387 init_repos &&
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
1397 # the conflict.
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' '
1410 init_repos &&
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' '
1424 init_repos &&
1426 test_sparse_match git sparse-checkout disable &&
1428 write_script edit-content <<-\EOF &&
1429 echo content >>folder1/larger-content
1430 git add folder1
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 &&
1458 D folder1
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' '
1477 init_repos &&
1479 test_sparse_match git sparse-checkout disable &&
1481 write_script edit-content <<-\EOF &&
1482 echo content >>folder2/larger-content
1483 git add folder2
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 &&
1517 D folder2
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
1527 test_done