Raw
1 #!/bin/sh
2
3 test_description='compare full workdir to sparse workdir'
4
5 GIT_TEST_SPLIT_INDEX=0
6 GIT_TEST_SPARSE_INDEX=
7
8 . ./test-lib.sh
9
10 test_expect_success 'setup' '
11 git init initial-repo &&
12 (
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 before x &&
20 echo "before deep" >before/a &&
21 echo "before deep again" >before/b &&
22 mkdir deep/deeper1 deep/deeper2 deep/before deep/later &&
23 mkdir deep/deeper1/deepest &&
24 mkdir deep/deeper1/deepest2 &&
25 mkdir deep/deeper1/deepest3 &&
26 echo "after deeper1" >deep/e &&
27 echo "after deepest" >deep/deeper1/e &&
28 cp a folder1 &&
29 cp a folder2 &&
30 cp a x &&
31 cp a deep &&
32 cp a deep/before &&
33 cp a deep/deeper1 &&
34 cp a deep/deeper2 &&
35 cp a deep/later &&
36 cp a deep/deeper1/deepest &&
37 cp a deep/deeper1/deepest2 &&
38 cp a deep/deeper1/deepest3 &&
39 cp -r deep/deeper1/ deep/deeper2 &&
40 mkdir deep/deeper1/0 &&
41 mkdir deep/deeper1/0/0 &&
42 touch deep/deeper1/0/1 &&
43 touch deep/deeper1/0/0/0 &&
44 >folder1- &&
45 >folder1.x &&
46 >folder10 &&
47 cp -r deep/deeper1/0 folder1 &&
48 cp -r deep/deeper1/0 folder2 &&
49 echo >>folder1/0/0/0 &&
50 echo >>folder2/0/1 &&
51 git add . &&
52 git commit -m "initial commit" &&
53 git checkout -b base &&
54 for dir in folder1 folder2 deep
55 do
56 git checkout -b update-$dir base &&
57 echo "updated $dir" >$dir/a &&
58 git commit -a -m "update $dir" || return 1
59 done &&
60
61 git checkout -b rename-base base &&
62 cat >folder1/larger-content <<-\EOF &&
63 matching
64 lines
65 help
66 inexact
67 renames
68 EOF
69 cp folder1/larger-content folder2/ &&
70 cp folder1/larger-content deep/deeper1/ &&
71 git add . &&
72 git commit -m "add interesting rename content" &&
73
74 git checkout -b rename-out-to-out rename-base &&
75 mv folder1/a folder2/b &&
76 mv folder1/larger-content folder2/edited-content &&
77 echo >>folder2/edited-content &&
78 echo >>folder2/0/1 &&
79 echo stuff >>deep/deeper1/a &&
80 git add . &&
81 git commit -m "rename folder1/... to folder2/..." &&
82
83 git checkout -b rename-out-to-in rename-base &&
84 mv folder1/a deep/deeper1/b &&
85 echo more stuff >>deep/deeper1/a &&
86 rm folder2/0/1 &&
87 mkdir folder2/0/1 &&
88 echo >>folder2/0/1/1 &&
89 mv folder1/larger-content deep/deeper1/edited-content &&
90 echo >>deep/deeper1/edited-content &&
91 git add . &&
92 git commit -m "rename folder1/... to deep/deeper1/..." &&
93
94 git checkout -b rename-in-to-out rename-base &&
95 mv deep/deeper1/a folder1/b &&
96 echo >>folder2/0/1 &&
97 rm -rf folder1/0/0 &&
98 echo >>folder1/0/0 &&
99 mv deep/deeper1/larger-content folder1/edited-content &&
100 echo >>folder1/edited-content &&
101 git add . &&
102 git commit -m "rename deep/deeper1/... to folder1/..." &&
103
104 git checkout -b df-conflict-1 base &&
105 rm -rf folder1 &&
106 echo content >folder1 &&
107 git add . &&
108 git commit -m "dir to file" &&
109
110 git checkout -b df-conflict-2 base &&
111 rm -rf folder2 &&
112 echo content >folder2 &&
113 git add . &&
114 git commit -m "dir to file" &&
115
116 git checkout -b fd-conflict base &&
117 rm a &&
118 mkdir a &&
119 echo content >a/a &&
120 git add . &&
121 git commit -m "file to dir" &&
122
123 for side in left right
124 do
125 git checkout -b merge-$side base &&
126 echo $side >>deep/deeper2/a &&
127 echo $side >>folder1/a &&
128 echo $side >>folder2/a &&
129 git add . &&
130 git commit -m "$side" || return 1
131 done &&
132
133 git checkout -b deepest base &&
134 echo "updated deepest" >deep/deeper1/deepest/a &&
135 echo "updated deepest2" >deep/deeper1/deepest2/a &&
136 echo "updated deepest3" >deep/deeper1/deepest3/a &&
137 git commit -a -m "update deepest" &&
138
139 git checkout -f base &&
140 git reset --hard
141 )
142 '
143
144 init_repos () {
145 rm -rf full-checkout sparse-checkout sparse-index &&
146
147 # create repos in initial state
148 cp -r initial-repo full-checkout &&
149 git -C full-checkout reset --hard &&
150
151 cp -r initial-repo sparse-checkout &&
152 git -C sparse-checkout reset --hard &&
153
154 cp -r initial-repo sparse-index &&
155 git -C sparse-index reset --hard &&
156
157 # initialize sparse-checkout definitions
158 git -C sparse-checkout sparse-checkout init --cone &&
159 git -C sparse-checkout sparse-checkout set deep &&
160 git -C sparse-index sparse-checkout init --cone --sparse-index &&
161 test_cmp_config -C sparse-index true index.sparse &&
162 git -C sparse-index sparse-checkout set deep &&
163
164 # Disable this message to keep stderr the same.
165 git -C sparse-index config advice.sparseIndexExpanded false
166 }
167
168 init_repos_as_submodules () {
169 git reset --hard &&
170 init_repos &&
171 git submodule add ./full-checkout &&
172 git submodule add ./sparse-checkout &&
173 git submodule add ./sparse-index &&
174
175 git submodule status >actual &&
176 grep full-checkout actual &&
177 grep sparse-checkout actual &&
178 grep sparse-index actual
179 }
180
181 run_on_sparse () {
182 cat >run-on-sparse-input &&
183
184 (
185 cd sparse-checkout &&
186 GIT_PROGRESS_DELAY=100000 "$@" >../sparse-checkout-out 2>../sparse-checkout-err
187 ) <run-on-sparse-input &&
188 (
189 cd sparse-index &&
190 GIT_PROGRESS_DELAY=100000 "$@" >../sparse-index-out 2>../sparse-index-err
191 ) <run-on-sparse-input
192 }
193
194 run_on_all () {
195 cat >run-on-all-input &&
196
197 (
198 cd full-checkout &&
199 GIT_PROGRESS_DELAY=100000 "$@" >../full-checkout-out 2>../full-checkout-err
200 ) <run-on-all-input &&
201 run_on_sparse "$@" <run-on-all-input
202 }
203
204 test_all_match () {
205 run_on_all "$@" &&
206 test_cmp full-checkout-out sparse-checkout-out &&
207 test_cmp full-checkout-out sparse-index-out &&
208 test_cmp full-checkout-err sparse-checkout-err &&
209 test_cmp full-checkout-err sparse-index-err
210 }
211
212 test_sparse_match () {
213 run_on_sparse "$@" &&
214 test_cmp sparse-checkout-out sparse-index-out &&
215 test_cmp sparse-checkout-err sparse-index-err
216 }
217
218 test_sparse_unstaged () {
219 file=$1 &&
220 for repo in sparse-checkout sparse-index
221 do
222 # Skip "unmerged" paths
223 git -C $repo diff --staged --diff-filter=u -- "$file" >diff &&
224 test_must_be_empty diff || return 1
225 done
226 }
227
228 # Usage: test_sparse_checkout_set "<c1> ... <cN>" "<s1> ... <sM>"
229 # Verifies that "git sparse-checkout set <c1> ... <cN>" succeeds and
230 # leaves the sparse index in a state where <s1> ... <sM> are sparse
231 # directories (and <c1> ... <cN> are not).
232 test_sparse_checkout_set () {
233 CONE_DIRS=$1 &&
234 SPARSE_DIRS=$2 &&
235 git -C sparse-index sparse-checkout set --skip-checks $CONE_DIRS &&
236 git -C sparse-index ls-files --sparse --stage >cache &&
237
238 # Check that the directories outside of the sparse-checkout cone
239 # have sparse directory entries.
240 for dir in $SPARSE_DIRS
241 do
242 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
243 grep "040000 $TREE 0 $dir/" cache \
244 || return 1
245 done &&
246
247 # Check that the directories in the sparse-checkout cone
248 # are not sparse directory entries.
249 for dir in $CONE_DIRS
250 do
251 # Allow TREE to not exist because
252 # $dir does not exist at HEAD.
253 TREE=$(git -C sparse-index rev-parse HEAD:$dir) ||
254 ! grep "040000 $TREE 0 $dir/" cache \
255 || return 1
256 done
257 }
258
259 test_expect_success 'sparse-index contents' '
260 init_repos &&
261
262 # Remove deep, add three other directories.
263 test_sparse_checkout_set \
264 "folder1 folder2 x" \
265 "before deep" &&
266
267 # Remove folder1, add deep
268 test_sparse_checkout_set \
269 "deep folder2 x" \
270 "before folder1" &&
271
272 # Replace deep with deep/deeper2 (dropping deep/deeper1)
273 # Add folder1
274 test_sparse_checkout_set \
275 "deep/deeper2 folder1 folder2 x" \
276 "before deep/deeper1" &&
277
278 # Replace deep/deeper2 with deep/deeper1
279 # Replace folder1 with folder1/0/0
280 # Replace folder2 with non-existent folder2/2/3
281 # Add non-existent "bogus"
282 test_sparse_checkout_set \
283 "bogus deep/deeper1 folder1/0/0 folder2/2/3 x" \
284 "before deep/deeper2 folder2/0" &&
285
286 # Drop down to only files at root
287 test_sparse_checkout_set \
288 "" \
289 "before deep folder1 folder2 x" &&
290
291 # Disabling the sparse-index replaces tree entries with full ones
292 git -C sparse-index sparse-checkout init --no-sparse-index &&
293 test_sparse_match git ls-files --stage --sparse
294 '
295
296 test_expect_success 'expanded in-memory index matches full index' '
297 init_repos &&
298 test_sparse_match git ls-files --stage
299 '
300
301 test_expect_success 'root directory cannot be sparse' '
302 init_repos &&
303
304 # Remove all in-cone files and directories from the index, collapse index
305 # with `git sparse-checkout reapply`
306 git -C sparse-index rm -r . &&
307 git -C sparse-index sparse-checkout reapply &&
308
309 # Verify sparse directories still present, root directory is not sparse
310 cat >expect <<-EOF &&
311 before/
312 folder1/
313 folder2/
314 x/
315 EOF
316 git -C sparse-index ls-files --sparse >actual &&
317 test_cmp expect actual
318 '
319
320 test_expect_success 'status with options' '
321 init_repos &&
322 test_sparse_match ls &&
323 test_all_match git status --porcelain=v2 &&
324 test_all_match git status --porcelain=v2 -z -u &&
325 test_all_match git status --porcelain=v2 -uno &&
326 run_on_all touch README.md &&
327 test_all_match git status --porcelain=v2 &&
328 test_all_match git status --porcelain=v2 -z -u &&
329 test_all_match git status --porcelain=v2 -uno &&
330 test_all_match git add README.md &&
331 test_all_match git status --porcelain=v2 &&
332 test_all_match git status --porcelain=v2 -z -u &&
333 test_all_match git status --porcelain=v2 -uno
334 '
335
336 test_expect_success 'status with diff in unexpanded sparse directory' '
337 init_repos &&
338 test_all_match git checkout rename-base &&
339 test_all_match git reset --soft rename-out-to-out &&
340 test_all_match git status --porcelain=v2
341 '
342
343 test_expect_success 'status reports sparse-checkout' '
344 init_repos &&
345 git -C sparse-checkout status >full &&
346 git -C sparse-index status >sparse &&
347 test_grep "You are in a sparse checkout with " full &&
348 test_grep "You are in a sparse checkout." sparse
349 '
350
351 test_expect_success 'add, commit, checkout' '
352 init_repos &&
353
354 write_script edit-contents <<-\EOF &&
355 echo text >>$1
356 EOF
357 run_on_all ../edit-contents README.md &&
358
359 test_all_match git add README.md &&
360 test_all_match git status --porcelain=v2 &&
361 test_all_match git commit -m "Add README.md" &&
362
363 test_all_match git checkout HEAD~1 &&
364 test_all_match git checkout - &&
365
366 run_on_all ../edit-contents README.md &&
367
368 test_all_match git add -A &&
369 test_all_match git status --porcelain=v2 &&
370 test_all_match git commit -m "Extend README.md" &&
371
372 test_all_match git checkout HEAD~1 &&
373 test_all_match git checkout - &&
374
375 run_on_all ../edit-contents deep/newfile &&
376
377 test_all_match git status --porcelain=v2 -uno &&
378 test_all_match git status --porcelain=v2 &&
379 test_all_match git add . &&
380 test_all_match git status --porcelain=v2 &&
381 test_all_match git commit -m "add deep/newfile" &&
382
383 test_all_match git checkout HEAD~1 &&
384 test_all_match git checkout -
385 '
386
387 test_expect_success 'git add, checkout, and reset with -p' '
388 init_repos &&
389
390 write_script edit-contents <<-\EOF &&
391 echo text >>$1
392 EOF
393
394 # Does not expand when edits are within sparse checkout.
395 run_on_all ../edit-contents deep/a &&
396 run_on_all ../edit-contents deep/deeper1/a &&
397
398 test_write_lines y n >in &&
399 run_on_all git add -p <in &&
400 test_all_match git status --porcelain=v2 &&
401 test_all_match git reset -p <in &&
402
403 test_write_lines u 1 "" q >in &&
404 run_on_all git add -i <in &&
405 test_all_match git status --porcelain=v2 &&
406 test_all_match git reset --hard &&
407
408 run_on_sparse mkdir -p folder1 &&
409 run_on_all ../edit-contents folder1/a &&
410 test_write_lines y n y >in &&
411 run_on_all git add -p <in &&
412 test_sparse_match git status --porcelain=v2 &&
413 test_sparse_match git reset &&
414 test_write_lines u 2 3 "" q >in &&
415 run_on_all git add -i <in &&
416 test_sparse_match git status --porcelain=v2 &&
417
418 run_on_all git add --sparse folder1 &&
419 run_on_all git commit -m "take changes" &&
420 test_write_lines y n y >in &&
421 test_sparse_match git checkout HEAD~1 --patch <in &&
422 test_sparse_match git status --porcelain=v2
423 '
424
425 test_expect_success 'deep changes during checkout' '
426 init_repos &&
427
428 test_sparse_match git sparse-checkout set deep/deeper1/deepest &&
429 test_all_match git checkout deepest &&
430 test_all_match git checkout base
431 '
432
433 test_expect_success 'checkout with modified sparse directory' '
434 init_repos &&
435
436 test_all_match git checkout rename-in-to-out -- . &&
437 test_sparse_match git sparse-checkout reapply &&
438 test_all_match git checkout base
439 '
440
441 test_expect_success 'checkout orphan then non-orphan' '
442 init_repos &&
443
444 test_all_match git checkout --orphan test-orphan &&
445 test_all_match git status --porcelain=v2 &&
446 test_all_match git checkout base &&
447 test_all_match git status --porcelain=v2
448 '
449
450 test_expect_success 'add outside sparse cone' '
451 init_repos &&
452
453 run_on_sparse mkdir folder1 &&
454 run_on_sparse ../edit-contents folder1/a &&
455 run_on_sparse ../edit-contents folder1/newfile &&
456 test_sparse_match test_must_fail git add folder1/a &&
457 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
458 test_sparse_unstaged folder1/a &&
459 test_sparse_match test_must_fail git add folder1/newfile &&
460 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
461 test_sparse_unstaged folder1/newfile
462 '
463
464 test_expect_success 'commit including unstaged changes' '
465 init_repos &&
466
467 write_script edit-file <<-\EOF &&
468 echo $1 >$2
469 EOF
470
471 run_on_all ../edit-file 1 a &&
472 run_on_all ../edit-file 1 deep/a &&
473
474 test_all_match git commit -m "-a" -a &&
475 test_all_match git status --porcelain=v2 &&
476
477 run_on_all ../edit-file 2 a &&
478 run_on_all ../edit-file 2 deep/a &&
479
480 test_all_match git commit -m "--include" --include deep/a &&
481 test_all_match git status --porcelain=v2 &&
482 test_all_match git commit -m "--include" --include a &&
483 test_all_match git status --porcelain=v2 &&
484
485 run_on_all ../edit-file 3 a &&
486 run_on_all ../edit-file 3 deep/a &&
487
488 test_all_match git commit -m "--amend" -a --amend &&
489 test_all_match git status --porcelain=v2
490 '
491
492 test_expect_success 'status/add: outside sparse cone' '
493 init_repos &&
494
495 # folder1 is at HEAD, but outside the sparse cone
496 run_on_sparse mkdir folder1 &&
497 cp initial-repo/folder1/a sparse-checkout/folder1/a &&
498 cp initial-repo/folder1/a sparse-index/folder1/a &&
499
500 test_sparse_match git status &&
501
502 write_script edit-contents <<-\EOF &&
503 echo text >>$1
504 EOF
505 run_on_all ../edit-contents folder1/a &&
506 run_on_all ../edit-contents folder1/new &&
507
508 test_sparse_match git status --porcelain=v2 &&
509
510 # Adding the path outside of the sparse-checkout cone should fail.
511 test_sparse_match test_must_fail git add folder1/a &&
512 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
513 test_sparse_unstaged folder1/a &&
514 test_all_match git add --refresh folder1/a &&
515 test_must_be_empty sparse-checkout-err &&
516 test_sparse_unstaged folder1/a &&
517 test_sparse_match test_must_fail git add folder1/new &&
518 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
519 test_sparse_unstaged folder1/new &&
520 test_sparse_match git add --sparse folder1/a &&
521 test_sparse_match git add --sparse folder1/new &&
522
523 test_all_match git add --sparse . &&
524 test_all_match git status --porcelain=v2 &&
525 test_all_match git commit -m folder1/new &&
526 test_all_match git rev-parse HEAD^{tree} &&
527
528 run_on_all ../edit-contents folder1/newer &&
529 test_all_match git add --sparse folder1/ &&
530 test_all_match git status --porcelain=v2 &&
531 test_all_match git commit -m folder1/newer &&
532 test_all_match git rev-parse HEAD^{tree}
533 '
534
535 test_expect_success 'checkout and reset --hard' '
536 init_repos &&
537
538 test_all_match git checkout update-folder1 &&
539 test_all_match git status --porcelain=v2 &&
540
541 test_all_match git checkout update-deep &&
542 test_all_match git status --porcelain=v2 &&
543
544 test_all_match git checkout -b reset-test &&
545 test_all_match git reset --hard deepest &&
546 test_all_match git reset --hard update-folder1 &&
547 test_all_match git reset --hard update-folder2
548 '
549
550 test_expect_success 'diff --cached' '
551 init_repos &&
552
553 write_script edit-contents <<-\EOF &&
554 echo text >>README.md
555 EOF
556 run_on_all ../edit-contents &&
557
558 test_all_match git diff &&
559 test_all_match git diff --cached &&
560 test_all_match git add README.md &&
561 test_all_match git diff &&
562 test_all_match git diff --cached
563 '
564
565 # NEEDSWORK: sparse-checkout behaves differently from full-checkout when
566 # running this test with 'df-conflict-2' after 'df-conflict-1'.
567 test_expect_success 'diff with renames and conflicts' '
568 init_repos &&
569
570 for branch in rename-out-to-out \
571 rename-out-to-in \
572 rename-in-to-out \
573 df-conflict-1 \
574 fd-conflict
575 do
576 test_all_match git checkout rename-base &&
577 test_all_match git checkout $branch -- . &&
578 test_all_match git status --porcelain=v2 &&
579 test_all_match git diff --cached --no-renames &&
580 test_all_match git diff --cached --find-renames || return 1
581 done
582 '
583
584 test_expect_success 'diff with directory/file conflicts' '
585 init_repos &&
586
587 for branch in rename-out-to-out \
588 rename-out-to-in \
589 rename-in-to-out \
590 df-conflict-1 \
591 df-conflict-2 \
592 fd-conflict
593 do
594 git -C full-checkout reset --hard &&
595 test_sparse_match git reset --hard &&
596 test_all_match git checkout $branch &&
597 test_all_match git checkout rename-base -- . &&
598 test_all_match git status --porcelain=v2 &&
599 test_all_match git diff --cached --no-renames &&
600 test_all_match git diff --cached --find-renames || return 1
601 done
602 '
603
604 test_expect_success 'log with pathspec outside sparse definition' '
605 init_repos &&
606
607 test_all_match git log -- a &&
608 test_all_match git log -- folder1/a &&
609 test_all_match git log -- folder2/a &&
610 test_all_match git log -- deep/a &&
611 test_all_match git log -- deep/deeper1/a &&
612 test_all_match git log -- deep/deeper1/deepest/a &&
613
614 test_all_match git checkout update-folder1 &&
615 test_all_match git log -- folder1/a
616 '
617
618 test_expect_success 'blame with pathspec inside sparse definition' '
619 init_repos &&
620
621 for file in a \
622 deep/a \
623 deep/deeper1/a \
624 deep/deeper1/deepest/a
625 do
626 test_all_match git blame $file || return 1
627 done
628 '
629
630 # Without a revision specified, blame will error if passed any file that
631 # is not present in the working directory (even if the file is tracked).
632 # Here we just verify that this is also true with sparse checkouts.
633 test_expect_success 'blame with pathspec outside sparse definition' '
634 init_repos &&
635 test_sparse_match git sparse-checkout set &&
636
637 for file in \
638 deep/a \
639 deep/deeper1/a \
640 deep/deeper1/deepest/a
641 do
642 test_sparse_match test_must_fail git blame $file &&
643 cat >expect <<-EOF &&
644 fatal: Cannot lstat '"'"'$file'"'"': No such file or directory
645 EOF
646 # We compare sparse-checkout-err and sparse-index-err in
647 # `test_sparse_match`. Given we know they are the same, we
648 # only check the content of sparse-index-err here.
649 test_cmp expect sparse-index-err || return 1
650 done
651 '
652
653 test_expect_success 'checkout and reset (mixed)' '
654 init_repos &&
655
656 test_all_match git checkout -b reset-test update-deep &&
657 test_all_match git reset deepest &&
658
659 # Because skip-worktree is preserved, resetting to update-folder1
660 # will show worktree changes for folder1/a in full-checkout, but not
661 # in sparse-checkout or sparse-index.
662 git -C full-checkout reset update-folder1 >full-checkout-out &&
663 test_sparse_match git reset update-folder1 &&
664 test_grep "M folder1/a" full-checkout-out &&
665 test_grep ! "M folder1/a" sparse-checkout-out &&
666 run_on_sparse test_path_is_missing folder1
667 '
668
669 test_expect_success 'checkout and reset (merge)' '
670 init_repos &&
671
672 write_script edit-contents <<-\EOF &&
673 echo text >>$1
674 EOF
675
676 test_all_match git checkout -b reset-test update-deep &&
677 run_on_all ../edit-contents a &&
678 test_all_match git reset --merge deepest &&
679 test_all_match git status --porcelain=v2 &&
680
681 test_all_match git reset --hard update-deep &&
682 run_on_all ../edit-contents deep/a &&
683 test_all_match test_must_fail git reset --merge deepest
684 '
685
686 test_expect_success 'checkout and reset (keep)' '
687 init_repos &&
688
689 write_script edit-contents <<-\EOF &&
690 echo text >>$1
691 EOF
692
693 test_all_match git checkout -b reset-test update-deep &&
694 run_on_all ../edit-contents a &&
695 test_all_match git reset --keep deepest &&
696 test_all_match git status --porcelain=v2 &&
697
698 test_all_match git reset --hard update-deep &&
699 run_on_all ../edit-contents deep/a &&
700 test_all_match test_must_fail git reset --keep deepest
701 '
702
703 test_expect_success 'reset with pathspecs inside sparse definition' '
704 init_repos &&
705
706 write_script edit-contents <<-\EOF &&
707 echo text >>$1
708 EOF
709
710 test_all_match git checkout -b reset-test update-deep &&
711 run_on_all ../edit-contents deep/a &&
712
713 test_all_match git reset base -- deep/a &&
714 test_all_match git status --porcelain=v2 &&
715
716 test_all_match git reset base -- nonexistent-file &&
717 test_all_match git status --porcelain=v2 &&
718
719 test_all_match git reset deepest -- deep &&
720 test_all_match git status --porcelain=v2
721 '
722
723 # Although the working tree differs between full and sparse checkouts after
724 # reset, the state of the index is the same.
725 test_expect_success 'reset with pathspecs outside sparse definition' '
726 init_repos &&
727 test_all_match git checkout -b reset-test base &&
728
729 test_sparse_match git reset update-folder1 -- folder1 &&
730 git -C full-checkout reset update-folder1 -- folder1 &&
731 test_all_match git ls-files -s -- folder1 &&
732
733 test_sparse_match git reset update-folder2 -- folder2/a &&
734 git -C full-checkout reset update-folder2 -- folder2/a &&
735 test_all_match git ls-files -s -- folder2/a
736 '
737
738 test_expect_success 'reset with wildcard pathspec' '
739 init_repos &&
740
741 test_all_match git reset update-deep -- deep\* &&
742 test_all_match git ls-files -s -- deep &&
743
744 test_all_match git reset deepest -- deep\*\*\* &&
745 test_all_match git ls-files -s -- deep &&
746
747 # The following `git reset`s result in updating the index on files with
748 # `skip-worktree` enabled. To avoid failing due to discrepancies in reported
749 # "modified" files, `test_sparse_match` reset is performed separately from
750 # "full-checkout" reset, then the index contents of all repos are verified.
751
752 test_sparse_match git reset update-folder1 -- \*/a &&
753 git -C full-checkout reset update-folder1 -- \*/a &&
754 test_all_match git ls-files -s -- deep/a folder1/a &&
755
756 test_sparse_match git reset update-folder2 -- folder\* &&
757 git -C full-checkout reset update-folder2 -- folder\* &&
758 test_all_match git ls-files -s -- folder10 folder1 folder2 &&
759
760 test_sparse_match git reset base -- folder1/\* &&
761 git -C full-checkout reset base -- folder1/\* &&
762 test_all_match git ls-files -s -- folder1
763 '
764
765 test_expect_success 'reset hard with removed sparse dir' '
766 init_repos &&
767
768 run_on_all git rm -r --sparse folder1 &&
769 test_all_match git status --porcelain=v2 &&
770
771 test_all_match git reset --hard &&
772 test_all_match git status --porcelain=v2 &&
773
774 cat >expect <<-\EOF &&
775 folder1/
776 EOF
777
778 git -C sparse-index ls-files --sparse folder1 >out &&
779 test_cmp expect out
780 '
781
782 test_expect_success 'update-index modify outside sparse definition' '
783 init_repos &&
784
785 write_script edit-contents <<-\EOF &&
786 echo text >>$1
787 EOF
788
789 # Create & modify folder1/a
790 # Note that this setup is a manual way of reaching the erroneous
791 # condition in which a `skip-worktree` enabled, outside-of-cone file
792 # exists on disk. It is used here to ensure `update-index` is stable
793 # and behaves predictably if such a condition occurs.
794 run_on_sparse mkdir -p folder1 &&
795 run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
796 run_on_all ../edit-contents folder1/a &&
797
798 # If file has skip-worktree enabled, but the file is present, it is
799 # treated the same as if skip-worktree is disabled
800 test_all_match git status --porcelain=v2 &&
801 test_all_match git update-index folder1/a &&
802 test_all_match git status --porcelain=v2 &&
803
804 # When skip-worktree is disabled (even on files outside sparse cone), file
805 # is updated in the index
806 test_sparse_match git update-index --no-skip-worktree folder1/a &&
807 test_all_match git status --porcelain=v2 &&
808 test_all_match git update-index folder1/a &&
809 test_all_match git status --porcelain=v2
810 '
811
812 test_expect_success 'update-index --add outside sparse definition' '
813 init_repos &&
814
815 write_script edit-contents <<-\EOF &&
816 echo text >>$1
817 EOF
818
819 # Create folder1, add new file
820 run_on_sparse mkdir -p folder1 &&
821 run_on_all ../edit-contents folder1/b &&
822
823 # The *untracked* out-of-cone file is added to the index because it does
824 # not have a `skip-worktree` bit to signal that it should be ignored
825 # (unlike in `git add`, which will fail due to the file being outside
826 # the sparse checkout definition).
827 test_all_match git update-index --add folder1/b &&
828 test_all_match git status --porcelain=v2
829 '
830
831 # NEEDSWORK: `--remove`, unlike the rest of `update-index`, does not ignore
832 # `skip-worktree` entries by default and will remove them from the index.
833 # The `--ignore-skip-worktree-entries` flag must be used in conjunction with
834 # `--remove` to ignore the `skip-worktree` entries and prevent their removal
835 # from the index.
836 test_expect_success 'update-index --remove outside sparse definition' '
837 init_repos &&
838
839 # When --ignore-skip-worktree-entries is _not_ specified:
840 # out-of-cone, not-on-disk files are removed from the index
841 test_sparse_match git update-index --remove folder1/a &&
842 cat >expect <<-EOF &&
843 D folder1/a
844 EOF
845 test_sparse_match git diff --cached --name-status &&
846 test_cmp expect sparse-checkout-out &&
847
848 test_sparse_match git diff-index --cached HEAD &&
849
850 # Reset the state
851 test_all_match git reset --hard &&
852
853 # When --ignore-skip-worktree-entries is specified, out-of-cone
854 # (skip-worktree) files are ignored
855 test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
856 test_sparse_match git diff --cached --name-status &&
857 test_must_be_empty sparse-checkout-out &&
858
859 test_sparse_match git diff-index --cached HEAD &&
860
861 # Reset the state
862 test_all_match git reset --hard &&
863
864 # --force-remove supersedes --ignore-skip-worktree-entries, removing
865 # a skip-worktree file from the index (and disk) when both are specified
866 # with --remove
867 test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
868 cat >expect <<-EOF &&
869 D folder1/a
870 EOF
871 test_sparse_match git diff --cached --name-status &&
872 test_cmp expect sparse-checkout-out &&
873
874 test_sparse_match git diff-index --cached HEAD
875 '
876
877 test_expect_success 'update-index with directories' '
878 init_repos &&
879
880 # update-index will exit silently when provided with a directory name
881 # containing a trailing slash
882 test_all_match git update-index deep/ folder1/ &&
883 test_grep "Ignoring path deep/" sparse-checkout-err &&
884 test_grep "Ignoring path folder1/" sparse-checkout-err &&
885
886 # When update-index is given a directory name WITHOUT a trailing slash, it will
887 # behave in different ways depending on the status of the directory on disk:
888 # * if it exists, the command exits with an error ("add individual files instead")
889 # * if it does NOT exist (e.g., in a sparse-checkout), it is assumed to be a
890 # file and either triggers an error ("does not exist and --remove not passed")
891 # or is ignored completely (when using --remove)
892 test_all_match test_must_fail git update-index deep &&
893 run_on_all test_must_fail git update-index folder1 &&
894 test_must_fail git -C full-checkout update-index --remove folder1 &&
895 test_sparse_match git update-index --remove folder1 &&
896 test_all_match git status --porcelain=v2
897 '
898
899 test_expect_success 'update-index --again file outside sparse definition' '
900 init_repos &&
901
902 test_all_match git checkout -b test-reupdate &&
903
904 # Update HEAD without modifying the index to introduce a difference in
905 # folder1/a
906 test_sparse_match git reset --soft update-folder1 &&
907
908 # Because folder1/a differs in the index vs HEAD,
909 # `git update-index --no-skip-worktree --again` will effectively perform
910 # `git update-index --no-skip-worktree folder1/a` and remove the skip-worktree
911 # flag from folder1/a
912 test_sparse_match git update-index --no-skip-worktree --again &&
913 test_sparse_match git status --porcelain=v2 &&
914
915 cat >expect <<-EOF &&
916 D folder1/a
917 EOF
918 test_sparse_match git diff --name-status &&
919 test_cmp expect sparse-checkout-out
920 '
921
922 test_expect_success 'update-index --cacheinfo' '
923 init_repos &&
924
925 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
926 folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
927 folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
928
929 test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
930 test_all_match git status --porcelain=v2 &&
931
932 # Cannot add sparse directory, even in sparse index case
933 test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
934
935 # Sparse match only: the new outside-of-cone entry is added *without* skip-worktree,
936 # so `git status` reports it as "deleted" in the worktree
937 test_sparse_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
938 test_sparse_match git status --porcelain=v2 &&
939 cat >expect <<-EOF &&
940 MD folder1/a
941 EOF
942 test_sparse_match git status --short -- folder1/a &&
943 test_cmp expect sparse-checkout-out &&
944
945 # To return folder1/a to "normal" for a sparse checkout (ignored &
946 # outside-of-cone), add the skip-worktree flag.
947 test_sparse_match git update-index --skip-worktree folder1/a &&
948 cat >expect <<-EOF &&
949 S folder1/a
950 EOF
951 test_sparse_match git ls-files -t -- folder1/a &&
952 test_cmp expect sparse-checkout-out
953 '
954
955 for MERGE_TREES in "base HEAD update-folder2" \
956 "update-folder1 update-folder2" \
957 "update-folder2"
958 do
959 test_expect_success "'read-tree -mu $MERGE_TREES' with files outside sparse definition" '
960 init_repos &&
961
962 # Although the index matches, without --no-sparse-checkout, outside-of-
963 # definition files will not exist on disk for sparse checkouts
964 test_all_match git read-tree -mu $MERGE_TREES &&
965 test_all_match git status --porcelain=v2 &&
966 test_path_is_missing sparse-checkout/folder2 &&
967 test_path_is_missing sparse-index/folder2 &&
968
969 test_all_match git read-tree --reset -u HEAD &&
970 test_all_match git status --porcelain=v2 &&
971
972 test_all_match git read-tree -mu --no-sparse-checkout $MERGE_TREES &&
973 test_all_match git status --porcelain=v2 &&
974 test_cmp sparse-checkout/folder2/a sparse-index/folder2/a &&
975 test_cmp sparse-checkout/folder2/a full-checkout/folder2/a
976
977 '
978 done
979
980 test_expect_success 'read-tree --merge with edit/edit conflicts in sparse directories' '
981 init_repos &&
982
983 # Merge of multiple changes to same directory (but not same files) should
984 # succeed
985 test_all_match git read-tree -mu base rename-base update-folder1 &&
986 test_all_match git status --porcelain=v2 &&
987
988 test_all_match git reset --hard &&
989
990 test_all_match git read-tree -mu rename-base update-folder2 &&
991 test_all_match git status --porcelain=v2 &&
992
993 test_all_match git reset --hard &&
994
995 test_all_match test_must_fail git read-tree -mu base update-folder1 rename-out-to-in &&
996 test_all_match test_must_fail git read-tree -mu rename-out-to-in update-folder1
997 '
998
999 test_expect_success 'read-tree --prefix' '
1000 init_repos &&
1001
1002 # If files differing between the index and target <commit-ish> exist
1003 # inside the prefix, `read-tree --prefix` should fail
1004 test_all_match test_must_fail git read-tree --prefix=deep/ deepest &&
1005 test_all_match test_must_fail git read-tree --prefix=folder1/ update-folder1 &&
1006
1007 # If no differing index entries exist matching the prefix,
1008 # `read-tree --prefix` updates the index successfully
1009 test_all_match git rm -rf deep/deeper1/deepest/ &&
1010 test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest &&
1011 test_all_match git status --porcelain=v2 &&
1012
1013 run_on_all git rm -rf --sparse folder1/ &&
1014 test_all_match git read-tree --prefix=folder1/ -u update-folder1 &&
1015 test_all_match git status --porcelain=v2 &&
1016
1017 test_all_match git rm -rf --sparse folder2/0 &&
1018 test_all_match git read-tree --prefix=folder2/0/ -u rename-out-to-out &&
1019 test_all_match git status --porcelain=v2
1020 '
1021
1022 test_expect_success 'read-tree --merge with directory-file conflicts' '
1023 init_repos &&
1024
1025 test_all_match git checkout -b test-branch rename-base &&
1026
1027 # Although the index matches, without --no-sparse-checkout, outside-of-
1028 # definition files will not exist on disk for sparse checkouts
1029 test_sparse_match git read-tree -mu rename-out-to-out &&
1030 test_sparse_match git status --porcelain=v2 &&
1031 test_path_is_missing sparse-checkout/folder2 &&
1032 test_path_is_missing sparse-index/folder2 &&
1033
1034 test_sparse_match git read-tree --reset -u HEAD &&
1035 test_sparse_match git status --porcelain=v2 &&
1036
1037 test_sparse_match git read-tree -mu --no-sparse-checkout rename-out-to-out &&
1038 test_sparse_match git status --porcelain=v2 &&
1039 test_cmp sparse-checkout/folder2/0/1 sparse-index/folder2/0/1
1040 '
1041
1042 test_expect_success 'merge, cherry-pick, and rebase' '
1043 init_repos &&
1044
1045 for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge"
1046 do
1047 test_all_match git checkout -B temp update-deep &&
1048 test_all_match git $OPERATION update-folder1 &&
1049 test_all_match git rev-parse HEAD^{tree} &&
1050 test_all_match git $OPERATION update-folder2 &&
1051 test_all_match git rev-parse HEAD^{tree} || return 1
1052 done
1053 '
1054
1055 test_expect_success 'merge with conflict outside cone' '
1056 init_repos &&
1057
1058 test_all_match git checkout -b merge-tip merge-left &&
1059 test_all_match git status --porcelain=v2 &&
1060 test_all_match test_must_fail git merge -m merge merge-right &&
1061 test_all_match git status --porcelain=v2 &&
1062
1063 # Resolve the conflict in different ways:
1064 # 1. Revert to the base
1065 test_all_match git checkout base -- deep/deeper2/a &&
1066 test_all_match git status --porcelain=v2 &&
1067
1068 # 2. Add the file with conflict markers
1069 test_sparse_match test_must_fail git add folder1/a &&
1070 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
1071 test_sparse_unstaged folder1/a &&
1072 test_all_match git add --sparse folder1/a &&
1073 test_all_match git status --porcelain=v2 &&
1074
1075 # 3. Rename the file to another sparse filename and
1076 # accept conflict markers as resolved content.
1077 run_on_all mv folder2/a folder2/z &&
1078 test_sparse_match test_must_fail git add folder2 &&
1079 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
1080 test_sparse_unstaged folder2/z &&
1081 test_all_match git add --sparse folder2 &&
1082 test_all_match git status --porcelain=v2 &&
1083
1084 test_all_match git merge --continue &&
1085 test_all_match git status --porcelain=v2 &&
1086 test_all_match git rev-parse HEAD^{tree}
1087 '
1088
1089 test_expect_success 'cherry-pick/rebase with conflict outside cone' '
1090 init_repos &&
1091
1092 for OPERATION in cherry-pick rebase
1093 do
1094 test_all_match git checkout -B tip &&
1095 test_all_match git reset --hard merge-left &&
1096 test_all_match git status --porcelain=v2 &&
1097 test_all_match test_must_fail git $OPERATION merge-right &&
1098 test_all_match git status --porcelain=v2 &&
1099
1100 # Resolve the conflict in different ways:
1101 # 1. Revert to the base
1102 test_all_match git checkout base -- deep/deeper2/a &&
1103 test_all_match git status --porcelain=v2 &&
1104
1105 # 2. Add the file with conflict markers
1106 # NEEDSWORK: Even though the merge conflict removed the
1107 # SKIP_WORKTREE bit from the index entry for folder1/a, we should
1108 # warn that this is a problematic add.
1109 test_sparse_match test_must_fail git add folder1/a &&
1110 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
1111 test_sparse_unstaged folder1/a &&
1112 test_all_match git add --sparse folder1/a &&
1113 test_all_match git status --porcelain=v2 &&
1114
1115 # 3. Rename the file to another sparse filename and
1116 # accept conflict markers as resolved content.
1117 # NEEDSWORK: This mode now fails, because folder2/z is
1118 # outside of the sparse-checkout cone and does not match an
1119 # existing index entry with the SKIP_WORKTREE bit cleared.
1120 run_on_all mv folder2/a folder2/z &&
1121 test_sparse_match test_must_fail git add folder2 &&
1122 test_grep "Disable or modify the sparsity rules" sparse-checkout-err &&
1123 test_sparse_unstaged folder2/z &&
1124 test_all_match git add --sparse folder2 &&
1125 test_all_match git status --porcelain=v2 &&
1126
1127 test_all_match git $OPERATION --continue &&
1128 test_all_match git status --porcelain=v2 &&
1129 test_all_match git rev-parse HEAD^{tree} || return 1
1130 done
1131 '
1132
1133 test_expect_success 'merge with outside renames' '
1134 init_repos &&
1135
1136 for type in out-to-out out-to-in in-to-out
1137 do
1138 test_all_match git reset --hard &&
1139 test_all_match git checkout -f -b merge-$type update-deep &&
1140 test_all_match git merge -m "$type" rename-$type &&
1141 test_all_match git rev-parse HEAD^{tree} || return 1
1142 done
1143 '
1144
1145 # Sparse-index fails to convert the index in the
1146 # final 'git cherry-pick' command.
1147 test_expect_success 'cherry-pick with conflicts' '
1148 init_repos &&
1149
1150 write_script edit-conflict <<-\EOF &&
1151 echo $1 >conflict
1152 EOF
1153
1154 test_all_match git checkout -b to-cherry-pick &&
1155 run_on_all ../edit-conflict ABC &&
1156 test_all_match git add conflict &&
1157 test_all_match git commit -m "conflict to pick" &&
1158
1159 test_all_match git checkout -B base HEAD~1 &&
1160 run_on_all ../edit-conflict DEF &&
1161 test_all_match git add conflict &&
1162 test_all_match git commit -m "conflict in base" &&
1163
1164 test_all_match test_must_fail git cherry-pick to-cherry-pick
1165 '
1166
1167 test_expect_success 'stash' '
1168 init_repos &&
1169
1170 write_script edit-contents <<-\EOF &&
1171 echo text >>$1
1172 EOF
1173
1174 # Stash a sparse directory (folder1)
1175 test_all_match git checkout -b test-branch rename-base &&
1176 test_all_match git reset --soft rename-out-to-out &&
1177 test_all_match git stash &&
1178 test_all_match git status --porcelain=v2 &&
1179
1180 # Apply the sparse directory stash without reinstating the index
1181 test_all_match git stash apply -q &&
1182 test_all_match git status --porcelain=v2 &&
1183
1184 # Reset to state where stash can be applied
1185 test_sparse_match git sparse-checkout reapply &&
1186 test_all_match git reset --hard rename-out-to-out &&
1187
1188 # Apply the sparse directory stash *with* reinstating the index
1189 test_all_match git stash apply --index -q &&
1190 test_all_match git status --porcelain=v2 &&
1191
1192 # Reset to state where we will get a conflict applying the stash
1193 test_sparse_match git sparse-checkout reapply &&
1194 test_all_match git reset --hard update-folder1 &&
1195
1196 # Apply the sparse directory stash with conflicts
1197 test_all_match test_must_fail git stash apply --index -q &&
1198 test_all_match test_must_fail git stash apply -q &&
1199 test_all_match git status --porcelain=v2 &&
1200
1201 # Reset to base branch
1202 test_sparse_match git sparse-checkout reapply &&
1203 test_all_match git reset --hard base &&
1204
1205 # Stash & unstash an untracked file outside of the sparse checkout
1206 # definition.
1207 run_on_sparse mkdir -p folder1 &&
1208 run_on_all ../edit-contents folder1/new &&
1209 test_all_match git stash -u &&
1210 test_all_match git status --porcelain=v2 &&
1211
1212 test_all_match git stash pop -q &&
1213 test_all_match git status --porcelain=v2
1214 '
1215
1216 test_expect_success 'checkout-index inside sparse definition' '
1217 init_repos &&
1218
1219 run_on_all rm -f deep/a &&
1220 test_all_match git checkout-index -- deep/a &&
1221 test_all_match git status --porcelain=v2 &&
1222
1223 echo test >>new-a &&
1224 run_on_all cp ../new-a a &&
1225 test_all_match test_must_fail git checkout-index -- a &&
1226 test_all_match git checkout-index -f -- a &&
1227 test_all_match git status --porcelain=v2
1228 '
1229
1230 test_expect_success 'checkout-index outside sparse definition' '
1231 init_repos &&
1232
1233 # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
1234 # an error
1235 test_sparse_match test_must_fail git checkout-index -- folder1/a &&
1236 test_grep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
1237 test_path_is_missing folder1/a &&
1238
1239 # With --ignore-skip-worktree-bits, outside-of-cone files are checked out
1240 test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
1241 test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
1242 test_cmp sparse-checkout/folder1/a full-checkout/folder1/a &&
1243
1244 run_on_sparse rm -rf folder1 &&
1245 echo test >new-a &&
1246 run_on_sparse mkdir -p folder1 &&
1247 run_on_all cp ../new-a folder1/a &&
1248
1249 test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
1250 test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a &&
1251 test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
1252 test_cmp sparse-checkout/folder1/a full-checkout/folder1/a
1253 '
1254
1255 test_expect_success 'checkout-index with folders' '
1256 init_repos &&
1257
1258 # Inside checkout definition
1259 test_all_match test_must_fail git checkout-index -f -- deep/ &&
1260
1261 # Outside checkout definition
1262 # Note: although all tests fail (as expected), the messaging differs. For
1263 # non-sparse index checkouts, the error is that the "file" does not appear
1264 # in the index; for sparse checkouts, the error is explicitly that the
1265 # entry is a sparse directory.
1266 run_on_all test_must_fail git checkout-index -f -- folder1/ &&
1267 test_cmp full-checkout-err sparse-checkout-err &&
1268 ! test_cmp full-checkout-err sparse-index-err &&
1269 test_grep "is a sparse directory" sparse-index-err
1270 '
1271
1272 test_expect_success 'checkout-index --all' '
1273 init_repos &&
1274
1275 test_all_match git checkout-index --all &&
1276 test_sparse_match test_path_is_missing folder1 &&
1277
1278 # --ignore-skip-worktree-bits will cause `skip-worktree` files to be
1279 # checked out, causing the outside-of-cone `folder1` to exist on-disk
1280 test_all_match git checkout-index --ignore-skip-worktree-bits --all &&
1281 test_all_match test_path_exists folder1
1282 '
1283
1284 test_expect_success 'clean' '
1285 init_repos &&
1286
1287 echo bogus >>.gitignore &&
1288 run_on_all cp ../.gitignore . &&
1289 test_all_match git add .gitignore &&
1290 test_all_match git commit -m "ignore bogus files" &&
1291
1292 run_on_sparse mkdir folder1 &&
1293 run_on_all mkdir -p deep/untracked-deep &&
1294 run_on_all touch folder1/bogus &&
1295 run_on_all touch folder1/untracked &&
1296 run_on_all touch deep/untracked-deep/bogus &&
1297 run_on_all touch deep/untracked-deep/untracked &&
1298
1299 test_all_match git status --porcelain=v2 &&
1300 test_all_match git clean -f &&
1301 test_all_match git status --porcelain=v2 &&
1302 test_sparse_match ls &&
1303 test_sparse_match ls folder1 &&
1304 run_on_all test_path_exists folder1/bogus &&
1305 run_on_all test_path_is_missing folder1/untracked &&
1306 run_on_all test_path_exists deep/untracked-deep/bogus &&
1307 run_on_all test_path_exists deep/untracked-deep/untracked &&
1308
1309 test_all_match git clean -fd &&
1310 test_all_match git status --porcelain=v2 &&
1311 test_sparse_match ls &&
1312 test_sparse_match ls folder1 &&
1313 run_on_all test_path_exists folder1/bogus &&
1314 run_on_all test_path_exists deep/untracked-deep/bogus &&
1315 run_on_all test_path_is_missing deep/untracked-deep/untracked &&
1316
1317 test_all_match git clean -xf &&
1318 test_all_match git status --porcelain=v2 &&
1319 test_sparse_match ls &&
1320 test_sparse_match ls folder1 &&
1321 run_on_all test_path_is_missing folder1/bogus &&
1322 run_on_all test_path_exists deep/untracked-deep/bogus &&
1323
1324 test_all_match git clean -xdf &&
1325 test_all_match git status --porcelain=v2 &&
1326 test_sparse_match ls &&
1327 test_sparse_match ls folder1 &&
1328 run_on_all test_path_is_missing deep/untracked-deep/bogus &&
1329
1330 test_sparse_match test_path_is_dir folder1
1331 '
1332
1333 for builtin in show rev-parse
1334 do
1335 test_expect_success "$builtin (cached blobs/trees)" "
1336 init_repos &&
1337
1338 test_all_match git $builtin :a &&
1339 test_all_match git $builtin :deep/a &&
1340 test_sparse_match git $builtin :folder1/a &&
1341
1342 # The error message differs depending on whether
1343 # the directory exists in the worktree.
1344 test_all_match test_must_fail git $builtin :deep/ &&
1345 test_must_fail git -C full-checkout $builtin :folder1/ &&
1346 test_sparse_match test_must_fail git $builtin :folder1/ &&
1347
1348 # Change the sparse cone for an extra case:
1349 run_on_sparse git sparse-checkout set deep/deeper1 &&
1350
1351 # deep/deeper2 is a sparse directory in the sparse index.
1352 test_sparse_match test_must_fail git $builtin :deep/deeper2/ &&
1353
1354 # deep/deeper2/deepest is not in the sparse index, but
1355 # will trigger an index expansion.
1356 test_sparse_match test_must_fail git $builtin :deep/deeper2/deepest/
1357 "
1358 done
1359
1360 test_expect_success 'submodule handling' '
1361 init_repos &&
1362
1363 test_sparse_match git sparse-checkout add modules &&
1364 test_all_match mkdir modules &&
1365 test_all_match touch modules/a &&
1366 test_all_match git add modules &&
1367 test_all_match git commit -m "add modules directory" &&
1368
1369 test_config_global protocol.file.allow always &&
1370
1371 run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
1372 test_all_match git commit -m "add submodule" &&
1373
1374 # having a submodule prevents "modules" from collapse
1375 test_sparse_match git sparse-checkout set deep/deeper1 &&
1376 git -C sparse-index ls-files --sparse --stage >cache &&
1377 test_grep "100644 .* modules/a" cache &&
1378 test_grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache
1379 '
1380
1381 test_expect_success 'git apply functionality' '
1382 init_repos &&
1383
1384 test_all_match git checkout base &&
1385
1386 git -C full-checkout diff base..merge-right -- deep >patch-in-sparse &&
1387 git -C full-checkout diff base..merge-right -- folder2 >patch-outside &&
1388
1389 # Apply a patch to a file inside the sparse definition
1390 test_all_match git apply --index --stat ../patch-in-sparse &&
1391 test_all_match git status --porcelain=v2 &&
1392
1393 # Apply a patch to a file outside the sparse definition
1394 test_sparse_match test_must_fail git apply ../patch-outside &&
1395 test_grep "No such file or directory" sparse-checkout-err &&
1396
1397 # But it works with --index and --cached
1398 test_all_match git apply --index --stat ../patch-outside &&
1399 test_all_match git status --porcelain=v2 &&
1400 test_all_match git reset --hard &&
1401 test_all_match git apply --cached --stat ../patch-outside &&
1402 test_all_match git status --porcelain=v2
1403 '
1404
1405 # When working with a sparse index, some commands will need to expand the
1406 # index to operate properly. If those commands also write the index back
1407 # to disk, they need to convert the index to sparse before writing.
1408 # This test verifies that both of these events are logged in trace2 logs.
1409 test_expect_success 'sparse-index is expanded and converted back' '
1410 init_repos &&
1411
1412 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
1413 git -C sparse-index reset -- folder1/a &&
1414 test_region index convert_to_sparse trace2.txt &&
1415 test_region index ensure_full_index trace2.txt &&
1416
1417 # ls-files expands on read, but does not write.
1418 rm trace2.txt &&
1419 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1420 git -C sparse-index ls-files &&
1421 test_region index ensure_full_index trace2.txt
1422 '
1423
1424 test_expect_success 'index.sparse disabled inline uses full index' '
1425 init_repos &&
1426
1427 # When index.sparse is disabled inline with `git status`, the
1428 # index is expanded at the beginning of the execution then never
1429 # converted back to sparse. It is then written to disk as a full index.
1430 rm -f trace2.txt &&
1431 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1432 git -C sparse-index -c index.sparse=false status &&
1433 ! test_region index convert_to_sparse trace2.txt &&
1434 test_region index ensure_full_index trace2.txt &&
1435
1436 # Since index.sparse is set to true at a repo level, the index
1437 # is converted from full to sparse when read, then never expanded
1438 # over the course of `git status`. It is written to disk as a sparse
1439 # index.
1440 rm -f trace2.txt &&
1441 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1442 git -C sparse-index status &&
1443 test_region index convert_to_sparse trace2.txt &&
1444 ! test_region index ensure_full_index trace2.txt &&
1445
1446 # Now that the index has been written to disk as sparse, it is not
1447 # converted to sparse (or expanded to full) when read by `git status`.
1448 rm -f trace2.txt &&
1449 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
1450 git -C sparse-index status &&
1451 ! test_region index convert_to_sparse trace2.txt &&
1452 ! test_region index ensure_full_index trace2.txt
1453 '
1454
1455 run_sparse_index_trace2 () {
1456 rm -f trace2.txt &&
1457 if test -z "$WITHOUT_UNTRACKED_TXT"
1458 then
1459 echo >>sparse-index/untracked.txt
1460 fi &&
1461
1462 if test "$1" = "!"
1463 then
1464 shift &&
1465 test_must_fail env \
1466 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
1467 git -C sparse-index "$@" \
1468 >sparse-index-out \
1469 2>sparse-index-error || return 1
1470 else
1471 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
1472 git -C sparse-index "$@" \
1473 >sparse-index-out \
1474 2>sparse-index-error || return 1
1475 fi
1476 }
1477
1478 ensure_expanded () {
1479 run_sparse_index_trace2 "$@" &&
1480 test_region index ensure_full_index trace2.txt
1481 }
1482
1483 ensure_not_expanded () {
1484 run_sparse_index_trace2 "$@" &&
1485 test_region ! index ensure_full_index trace2.txt
1486 }
1487
1488 test_expect_success 'sparse-index is not expanded' '
1489 init_repos &&
1490
1491 ensure_not_expanded status &&
1492 ensure_not_expanded ls-files --sparse &&
1493 ensure_not_expanded commit --allow-empty -m empty &&
1494 echo >>sparse-index/a &&
1495 ensure_not_expanded commit -a -m a &&
1496 echo >>sparse-index/a &&
1497 ensure_not_expanded commit --include a -m a &&
1498 echo >>sparse-index/deep/deeper1/a &&
1499 ensure_not_expanded commit --include deep/deeper1/a -m deeper &&
1500 ensure_not_expanded checkout rename-out-to-out &&
1501 ensure_not_expanded checkout - &&
1502 ensure_not_expanded switch rename-out-to-out &&
1503 ensure_not_expanded switch - &&
1504 ensure_not_expanded reset --hard &&
1505 ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 &&
1506 ensure_not_expanded reset --hard &&
1507 ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 &&
1508
1509 ensure_not_expanded ls-files deep/deeper1 &&
1510
1511 echo >>sparse-index/README.md &&
1512 ensure_not_expanded add -A &&
1513 echo >>sparse-index/extra.txt &&
1514 ensure_not_expanded add extra.txt &&
1515 echo >>sparse-index/untracked.txt &&
1516 ensure_not_expanded add . &&
1517
1518 ensure_not_expanded checkout-index -f a &&
1519 ensure_not_expanded checkout-index -f --all &&
1520 for ref in update-deep update-folder1 update-folder2 update-deep
1521 do
1522 echo >>sparse-index/README.md &&
1523 ensure_not_expanded reset --hard $ref || return 1
1524 done &&
1525
1526 ensure_not_expanded reset --mixed base &&
1527 ensure_not_expanded reset --hard update-deep &&
1528 ensure_not_expanded reset --keep base &&
1529 ensure_not_expanded reset --merge update-deep &&
1530 ensure_not_expanded reset --hard &&
1531
1532 ensure_not_expanded reset base -- deep/a &&
1533 ensure_not_expanded reset base -- nonexistent-file &&
1534 ensure_not_expanded reset deepest -- deep &&
1535
1536 # Although folder1 is outside the sparse definition, it exists as a
1537 # directory entry in the index, so the pathspec will not force the
1538 # index to be expanded.
1539 ensure_not_expanded reset deepest -- folder1 &&
1540 ensure_not_expanded reset deepest -- folder1/ &&
1541
1542 # Wildcard identifies only in-cone files, no index expansion
1543 ensure_not_expanded reset deepest -- deep/\* &&
1544
1545 # Wildcard identifies only full sparse directories, no index expansion
1546 ensure_not_expanded reset deepest -- folder\* &&
1547
1548 ensure_not_expanded clean -fd &&
1549
1550 ensure_not_expanded checkout -f update-deep &&
1551 test_config -C sparse-index pull.twohead ort &&
1552 (
1553 for OPERATION in "merge -m merge" cherry-pick rebase
1554 do
1555 ensure_not_expanded merge -m merge update-folder1 &&
1556 ensure_not_expanded merge -m merge update-folder2 || return 1
1557 done
1558 )
1559 '
1560
1561 test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
1562 init_repos &&
1563
1564 for side in right left
1565 do
1566 git -C sparse-index checkout -b expand-$side base &&
1567 echo $side >sparse-index/deep/a &&
1568 git -C sparse-index commit -a -m "$side" || return 1
1569 done &&
1570
1571 (
1572 git -C sparse-index config pull.twohead ort &&
1573 ensure_not_expanded ! merge -m merged expand-right
1574 )
1575 '
1576
1577 test_expect_success 'sparse-index is not expanded: stash' '
1578 init_repos &&
1579
1580 echo >>sparse-index/a &&
1581 ensure_not_expanded stash &&
1582 ensure_not_expanded stash list &&
1583 ensure_not_expanded stash show stash@{0} &&
1584 ensure_not_expanded stash apply stash@{0} &&
1585 ensure_not_expanded stash drop stash@{0} &&
1586
1587 echo >>sparse-index/deep/new &&
1588 ensure_not_expanded stash -u &&
1589 (
1590 WITHOUT_UNTRACKED_TXT=1 &&
1591 ensure_not_expanded stash pop
1592 ) &&
1593
1594 ensure_not_expanded stash create &&
1595 oid=$(git -C sparse-index stash create) &&
1596 ensure_not_expanded stash store -m "test" $oid &&
1597 ensure_not_expanded reset --hard &&
1598 ensure_not_expanded stash pop
1599 '
1600
1601 test_expect_success 'sparse-index is not expanded: stash in-cone pathspec' '
1602 init_repos &&
1603
1604 echo unrelated >>sparse-index/deep/e &&
1605 echo literal >>sparse-index/deep/a &&
1606 ensure_not_expanded stash push -- deep/a &&
1607 test_grep ! literal sparse-index/deep/a &&
1608 test_grep unrelated sparse-index/deep/e &&
1609 ensure_not_expanded stash pop &&
1610 test_grep literal sparse-index/deep/a &&
1611
1612 echo prefixed >>sparse-index/deep/a &&
1613 ensure_not_expanded -C deep stash push -- a &&
1614 test_grep ! prefixed sparse-index/deep/a &&
1615 test_grep unrelated sparse-index/deep/e &&
1616 ensure_not_expanded stash pop &&
1617 test_grep prefixed sparse-index/deep/a &&
1618
1619 echo wildcard >>sparse-index/deep/a &&
1620 ensure_not_expanded stash push -- "deep/a*" &&
1621 test_grep ! wildcard sparse-index/deep/a &&
1622 test_grep unrelated sparse-index/deep/e &&
1623 ensure_not_expanded stash pop &&
1624 test_grep wildcard sparse-index/deep/a &&
1625
1626 echo pathspec-file >>sparse-index/deep/a &&
1627 echo deep/a >pathspec-file &&
1628 ensure_not_expanded stash push --pathspec-from-file=../pathspec-file &&
1629 test_grep ! pathspec-file sparse-index/deep/a &&
1630 test_grep unrelated sparse-index/deep/e &&
1631 ensure_not_expanded stash pop &&
1632 test_grep pathspec-file sparse-index/deep/a &&
1633
1634 echo multiple-a >>sparse-index/deep/a &&
1635 echo multiple-e >>sparse-index/deep/e &&
1636 ensure_not_expanded stash push -- deep/a deep/e &&
1637 test_grep ! multiple-a sparse-index/deep/a &&
1638 test_grep ! multiple-e sparse-index/deep/e &&
1639 ensure_not_expanded stash pop &&
1640 test_grep multiple-a sparse-index/deep/a &&
1641 test_grep multiple-e sparse-index/deep/e &&
1642
1643 echo staged >>sparse-index/deep/a &&
1644 git -C sparse-index add deep/a &&
1645 ensure_not_expanded stash push --staged -- deep/a &&
1646 test_grep ! staged sparse-index/deep/a &&
1647 test_grep unrelated sparse-index/deep/e &&
1648 ensure_not_expanded stash pop --index &&
1649 test_grep staged sparse-index/deep/a &&
1650 test_must_fail git -C sparse-index diff --cached --quiet -- deep/a &&
1651
1652 ensure_not_expanded ! stash push -- deep/does-not-exist &&
1653 test_grep "did not match any file" sparse-index-error
1654 '
1655
1656 test_expect_success 'describe tested on all' '
1657 init_repos &&
1658
1659 # Add tag to be read by describe
1660
1661 run_on_all git tag -a v1.0 -m "Version 1" &&
1662 test_all_match git describe --dirty &&
1663 run_on_all rm g &&
1664 test_all_match git describe --dirty
1665 '
1666
1667 test_expect_success 'ls-files filtering and expansion' '
1668 init_repos &&
1669
1670 # This filtering will hit a sparse directory midway
1671 # through the iteration.
1672 test_all_match git ls-files deep &&
1673
1674 # This pathspec will filter the index to only a sparse
1675 # directory.
1676 test_all_match git ls-files folder1
1677 '
1678
1679 test_expect_success 'sparse-index is not expanded: describe' '
1680 init_repos &&
1681
1682 # Add tag to be read by describe
1683
1684 git -C sparse-index tag -a v1.0 -m "Version 1" &&
1685
1686 ensure_not_expanded describe --dirty &&
1687 echo "test" >>sparse-index/g &&
1688 ensure_not_expanded describe --dirty &&
1689 ensure_not_expanded describe
1690 '
1691
1692 test_expect_success 'sparse index is not expanded: diff and diff-index' '
1693 init_repos &&
1694
1695 write_script edit-contents <<-\EOF &&
1696 echo text >>$1
1697 EOF
1698
1699 # Add file within cone
1700 test_sparse_match git sparse-checkout set deep &&
1701 run_on_all ../edit-contents deep/testfile &&
1702 test_all_match git add deep/testfile &&
1703 run_on_all ../edit-contents deep/testfile &&
1704
1705 test_all_match git diff &&
1706 test_all_match git diff --cached &&
1707 ensure_not_expanded diff &&
1708 ensure_not_expanded diff --cached &&
1709 ensure_not_expanded diff-index --cached HEAD &&
1710
1711 # Add file outside cone
1712 test_all_match git reset --hard &&
1713 run_on_all mkdir newdirectory &&
1714 run_on_all ../edit-contents newdirectory/testfile &&
1715 test_sparse_match git sparse-checkout set newdirectory &&
1716 test_all_match git add newdirectory/testfile &&
1717 run_on_all ../edit-contents newdirectory/testfile &&
1718 test_sparse_match git sparse-checkout set &&
1719
1720 test_all_match git diff &&
1721 test_all_match git diff --cached &&
1722 ensure_not_expanded diff &&
1723 ensure_not_expanded diff --cached &&
1724 ensure_not_expanded diff-index --cached HEAD &&
1725
1726 # Merge conflict outside cone
1727 # The sparse checkout will report a warning that is not in the
1728 # full checkout, so we use `run_on_all` instead of
1729 # `test_all_match`
1730 run_on_all git reset --hard &&
1731 test_all_match git checkout merge-left &&
1732 test_all_match test_must_fail git merge merge-right &&
1733
1734 test_all_match git diff &&
1735 test_all_match git diff --cached &&
1736 ensure_not_expanded diff &&
1737 ensure_not_expanded diff --cached &&
1738 ensure_not_expanded diff-index --cached HEAD
1739 '
1740
1741 test_expect_success 'sparse index is not expanded: show and rev-parse' '
1742 init_repos &&
1743
1744 ensure_not_expanded show :a &&
1745 ensure_not_expanded show :deep/a &&
1746 ensure_not_expanded rev-parse :a &&
1747 ensure_not_expanded rev-parse :deep/a
1748 '
1749
1750 test_expect_success 'sparse index is not expanded: update-index' '
1751 init_repos &&
1752
1753 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
1754 ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a &&
1755
1756 echo "test" >sparse-index/README.md &&
1757 echo "test2" >sparse-index/a &&
1758 rm -f sparse-index/deep/a &&
1759
1760 ensure_not_expanded update-index --add README.md &&
1761 ensure_not_expanded update-index a &&
1762 ensure_not_expanded update-index --remove deep/a &&
1763
1764 ensure_not_expanded reset --soft update-deep &&
1765 ensure_not_expanded update-index --add --remove --again
1766 '
1767
1768 test_expect_success 'sparse index is not expanded: blame' '
1769 init_repos &&
1770
1771 for file in a \
1772 deep/a \
1773 deep/deeper1/a \
1774 deep/deeper1/deepest/a
1775 do
1776 ensure_not_expanded blame $file || return 1
1777 done
1778 '
1779
1780 test_expect_success 'sparse index is not expanded: fetch/pull' '
1781 init_repos &&
1782
1783 git -C sparse-index remote add full "file://$(pwd)/full-checkout" &&
1784 ensure_not_expanded fetch full &&
1785 git -C full-checkout commit --allow-empty -m "for pull merge" &&
1786 git -C sparse-index commit --allow-empty -m "for pull merge" &&
1787 ensure_not_expanded pull full base
1788 '
1789
1790 test_expect_success 'sparse index is not expanded: read-tree' '
1791 init_repos &&
1792
1793 ensure_not_expanded checkout -b test-branch update-folder1 &&
1794 for MERGE_TREES in "base HEAD update-folder2" \
1795 "base HEAD rename-base" \
1796 "base update-folder2" \
1797 "base rename-base" \
1798 "update-folder2"
1799 do
1800 ensure_not_expanded read-tree -mu $MERGE_TREES &&
1801 ensure_not_expanded reset --hard || return 1
1802 done &&
1803
1804 rm -rf sparse-index/deep/deeper2 &&
1805 ensure_not_expanded add . &&
1806 ensure_not_expanded commit -m "test" &&
1807
1808 ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest
1809 '
1810
1811 test_expect_success 'ls-files' '
1812 init_repos &&
1813
1814 # Use a smaller sparse-checkout for reduced output
1815 test_sparse_match git sparse-checkout set &&
1816
1817 # Behavior agrees by default. Sparse index is expanded.
1818 test_all_match git ls-files &&
1819
1820 # With --sparse, the sparse index data changes behavior.
1821 git -C sparse-index ls-files --sparse >actual &&
1822
1823 cat >expect <<-\EOF &&
1824 a
1825 before/
1826 deep/
1827 e
1828 folder1-
1829 folder1.x
1830 folder1/
1831 folder10
1832 folder2/
1833 g
1834 x/
1835 z
1836 EOF
1837
1838 test_cmp expect actual &&
1839
1840 # With --sparse and no sparse index, nothing changes.
1841 git -C sparse-checkout ls-files >dense &&
1842 git -C sparse-checkout ls-files --sparse >sparse &&
1843 test_cmp dense sparse &&
1844
1845 # Set up a strange condition of having a file edit
1846 # outside of the sparse-checkout cone. We want to verify
1847 # that all modes handle this the same, and detect the
1848 # modification.
1849 write_script edit-content <<-\EOF &&
1850 mkdir -p folder1 &&
1851 echo content >>folder1/a
1852 EOF
1853 run_on_all ../edit-content &&
1854
1855 test_all_match git ls-files --modified &&
1856
1857 git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
1858 cat >expect <<-\EOF &&
1859 folder1/a
1860 EOF
1861 test_cmp expect sparse-index-out &&
1862
1863 # Add folder1 to the sparse-checkout cone and
1864 # check that ls-files shows the expanded files.
1865 test_sparse_match git sparse-checkout add folder1 &&
1866 test_all_match git ls-files --modified &&
1867
1868 test_all_match git ls-files &&
1869 git -C sparse-index ls-files --sparse >actual &&
1870
1871 cat >expect <<-\EOF &&
1872 a
1873 before/
1874 deep/
1875 e
1876 folder1-
1877 folder1.x
1878 folder1/0/0/0
1879 folder1/0/1
1880 folder1/a
1881 folder10
1882 folder2/
1883 g
1884 x/
1885 z
1886 EOF
1887
1888 test_cmp expect actual &&
1889
1890 # Double-check index expansion is avoided
1891 ensure_not_expanded ls-files --sparse
1892 '
1893
1894 test_expect_success 'sparse index is not expanded: sparse-checkout' '
1895 init_repos &&
1896
1897 ensure_not_expanded sparse-checkout set deep/deeper2 &&
1898 ensure_not_expanded sparse-checkout set deep/deeper1 &&
1899 ensure_not_expanded sparse-checkout set deep &&
1900 ensure_not_expanded sparse-checkout add folder1 &&
1901 ensure_not_expanded sparse-checkout set deep/deeper1 &&
1902 ensure_not_expanded sparse-checkout set folder2 &&
1903
1904 # Demonstrate that the checks that "folder1/a" is a file
1905 # do not cause a sparse-index expansion (since it is in the
1906 # sparse-checkout cone).
1907 echo >>sparse-index/folder2/a &&
1908 git -C sparse-index add folder2/a &&
1909
1910 ensure_not_expanded sparse-checkout add folder1 &&
1911
1912 # Skip checks here, since deep/deeper1 is inside a sparse directory
1913 # that must be expanded to check whether `deep/deeper1` is a file
1914 # or not.
1915 ensure_not_expanded sparse-checkout set --skip-checks deep/deeper1 &&
1916 ensure_not_expanded sparse-checkout set
1917 '
1918
1919 # NEEDSWORK: a sparse-checkout behaves differently from a full checkout
1920 # in this scenario, but it shouldn't.
1921 test_expect_success 'reset mixed and checkout orphan' '
1922 init_repos &&
1923
1924 test_all_match git checkout rename-out-to-in &&
1925
1926 # Sparse checkouts do not agree with full checkouts about
1927 # how to report a directory/file conflict during a reset.
1928 # This command would fail with test_all_match because the
1929 # full checkout reports "T folder1/0/1" while a sparse
1930 # checkout reports "D folder1/0/1". This matches because
1931 # the sparse checkouts skip "adding" the other side of
1932 # the conflict.
1933 test_sparse_match git reset --mixed HEAD~1 &&
1934 test_sparse_match git ls-files --stage &&
1935 test_sparse_match git status --porcelain=v2 &&
1936
1937 # At this point, sparse-checkouts behave differently
1938 # from the full-checkout.
1939 test_sparse_match git checkout --orphan new-branch &&
1940 test_sparse_match git ls-files --stage &&
1941 test_sparse_match git status --porcelain=v2
1942 '
1943
1944 test_expect_success 'add everything with deep new file' '
1945 init_repos &&
1946
1947 run_on_sparse git sparse-checkout set deep/deeper1/deepest &&
1948
1949 run_on_all touch deep/deeper1/x &&
1950 test_all_match git add . &&
1951 test_all_match git status --porcelain=v2
1952 '
1953
1954 # NEEDSWORK: 'git checkout' behaves incorrectly in the case of
1955 # directory/file conflicts, even without sparse-checkout. Use this
1956 # test only as a documentation of the incorrect behavior, not a
1957 # measure of how it _should_ behave.
1958 test_expect_success 'checkout behaves oddly with df-conflict-1' '
1959 init_repos &&
1960
1961 test_sparse_match git sparse-checkout disable &&
1962
1963 write_script edit-content <<-\EOF &&
1964 echo content >>folder1/larger-content
1965 git add folder1
1966 EOF
1967
1968 run_on_all ../edit-content &&
1969 test_all_match git status --porcelain=v2 &&
1970
1971 git -C sparse-checkout sparse-checkout init --cone &&
1972 git -C sparse-index sparse-checkout init --cone --sparse-index &&
1973
1974 test_all_match git status --porcelain=v2 &&
1975
1976 # This checkout command should fail, because we have a staged
1977 # change to folder1/larger-content, but the destination changes
1978 # folder1 to a file.
1979 git -C full-checkout checkout df-conflict-1 \
1980 1>full-checkout-out \
1981 2>full-checkout-err &&
1982 git -C sparse-checkout checkout df-conflict-1 \
1983 1>sparse-checkout-out \
1984 2>sparse-checkout-err &&
1985 git -C sparse-index checkout df-conflict-1 \
1986 1>sparse-index-out \
1987 2>sparse-index-err &&
1988
1989 # Instead, the checkout deletes the folder1 file and adds the
1990 # folder1/larger-content file, leaving all other paths that were
1991 # in folder1/ as deleted (without any warning).
1992 cat >expect <<-EOF &&
1993 D folder1
1994 A folder1/larger-content
1995 EOF
1996 test_cmp expect full-checkout-out &&
1997 test_cmp expect sparse-checkout-out &&
1998
1999 # The sparse-index reports no output
2000 test_must_be_empty sparse-index-out &&
2001
2002 # stderr: Switched to branch df-conflict-1
2003 test_cmp full-checkout-err sparse-checkout-err &&
2004 test_cmp full-checkout-err sparse-checkout-err
2005 '
2006
2007 # NEEDSWORK: 'git checkout' behaves incorrectly in the case of
2008 # directory/file conflicts, even without sparse-checkout. Use this
2009 # test only as a documentation of the incorrect behavior, not a
2010 # measure of how it _should_ behave.
2011 test_expect_success 'checkout behaves oddly with df-conflict-2' '
2012 init_repos &&
2013
2014 test_sparse_match git sparse-checkout disable &&
2015
2016 write_script edit-content <<-\EOF &&
2017 echo content >>folder2/larger-content
2018 git add folder2
2019 EOF
2020
2021 run_on_all ../edit-content &&
2022 test_all_match git status --porcelain=v2 &&
2023
2024 git -C sparse-checkout sparse-checkout init --cone &&
2025 git -C sparse-index sparse-checkout init --cone --sparse-index &&
2026
2027 test_all_match git status --porcelain=v2 &&
2028
2029 # This checkout command should fail, because we have a staged
2030 # change to folder1/larger-content, but the destination changes
2031 # folder1 to a file.
2032 git -C full-checkout checkout df-conflict-2 \
2033 1>full-checkout-out \
2034 2>full-checkout-err &&
2035 git -C sparse-checkout checkout df-conflict-2 \
2036 1>sparse-checkout-out \
2037 2>sparse-checkout-err &&
2038 git -C sparse-index checkout df-conflict-2 \
2039 1>sparse-index-out \
2040 2>sparse-index-err &&
2041
2042 # The full checkout deviates from the df-conflict-1 case here!
2043 # It drops the change to folder1/larger-content and leaves the
2044 # folder1 path as-is on disk. The sparse-index behaves the same.
2045 test_must_be_empty full-checkout-out &&
2046 test_must_be_empty sparse-index-out &&
2047
2048 # In the sparse-checkout case, the checkout deletes the folder1
2049 # file and adds the folder1/larger-content file, leaving all other
2050 # paths that were in folder1/ as deleted (without any warning).
2051 cat >expect <<-EOF &&
2052 D folder2
2053 A folder2/larger-content
2054 EOF
2055 test_cmp expect sparse-checkout-out &&
2056
2057 # Switched to branch df-conflict-1
2058 test_cmp full-checkout-err sparse-checkout-err &&
2059 test_cmp full-checkout-err sparse-index-err
2060 '
2061
2062 test_expect_success 'mv directory from out-of-cone to in-cone' '
2063 init_repos &&
2064
2065 # <source> as a sparse directory (or SKIP_WORKTREE_DIR without enabling
2066 # sparse index).
2067 test_all_match git mv --sparse folder1 deep &&
2068 test_all_match git status --porcelain=v2 &&
2069 test_sparse_match git ls-files -t &&
2070 git -C sparse-checkout ls-files -t >actual &&
2071 test_grep -e "H deep/folder1/0/0/0" actual &&
2072 test_grep -e "H deep/folder1/0/1" actual &&
2073 test_grep -e "H deep/folder1/a" actual &&
2074
2075 test_all_match git reset --hard &&
2076
2077 # <source> as a directory deeper than sparse index boundary (where
2078 # sparse index will expand).
2079 test_sparse_match git mv --sparse folder1/0 deep &&
2080 test_sparse_match git status --porcelain=v2 &&
2081 test_sparse_match git ls-files -t &&
2082 git -C sparse-checkout ls-files -t >actual &&
2083 test_grep -e "H deep/0/0/0" actual &&
2084 test_grep -e "H deep/0/1" actual
2085 '
2086
2087 test_expect_success 'rm pathspec inside sparse definition' '
2088 init_repos &&
2089
2090 test_all_match git rm deep/a &&
2091 test_all_match git status --porcelain=v2 &&
2092
2093 # test wildcard
2094 run_on_all git reset --hard &&
2095 test_all_match git rm deep/* &&
2096 test_all_match git status --porcelain=v2 &&
2097
2098 # test recursive rm
2099 run_on_all git reset --hard &&
2100 test_all_match git rm -r deep &&
2101 test_all_match git status --porcelain=v2
2102 '
2103
2104 test_expect_success 'rm pathspec outside sparse definition' '
2105 init_repos &&
2106
2107 for file in folder1/a folder1/0/1
2108 do
2109 test_sparse_match test_must_fail git rm $file &&
2110 test_sparse_match test_must_fail git rm --cached $file &&
2111 test_sparse_match git rm --sparse $file &&
2112 test_sparse_match git status --porcelain=v2 || return 1
2113 done &&
2114
2115 cat >folder1-full <<-EOF &&
2116 rm ${SQ}folder1/0/0/0${SQ}
2117 rm ${SQ}folder1/0/1${SQ}
2118 rm ${SQ}folder1/a${SQ}
2119 EOF
2120
2121 cat >folder1-sparse <<-EOF &&
2122 rm ${SQ}folder1/${SQ}
2123 EOF
2124
2125 # test wildcard
2126 run_on_sparse git reset --hard &&
2127 run_on_sparse git sparse-checkout reapply &&
2128 test_sparse_match test_must_fail git rm folder1/* &&
2129 run_on_sparse git rm --sparse folder1/* &&
2130 test_cmp folder1-full sparse-checkout-out &&
2131 test_cmp folder1-sparse sparse-index-out &&
2132 test_sparse_match git status --porcelain=v2 &&
2133
2134 # test recursive rm
2135 run_on_sparse git reset --hard &&
2136 run_on_sparse git sparse-checkout reapply &&
2137 test_sparse_match test_must_fail git rm --sparse folder1 &&
2138 run_on_sparse git rm --sparse -r folder1 &&
2139 test_cmp folder1-full sparse-checkout-out &&
2140 test_cmp folder1-sparse sparse-index-out &&
2141 test_sparse_match git status --porcelain=v2
2142 '
2143
2144 test_expect_success 'rm pathspec expands index when necessary' '
2145 init_repos &&
2146
2147 # in-cone pathspec (do not expand)
2148 ensure_not_expanded rm "deep/deep*" &&
2149 test_must_be_empty sparse-index-err &&
2150
2151 # out-of-cone pathspec (expand)
2152 ! ensure_not_expanded rm --sparse "folder1/a*" &&
2153 test_must_be_empty sparse-index-err &&
2154
2155 # pathspec that should expand index
2156 ! ensure_not_expanded rm "*/a" &&
2157 test_must_be_empty sparse-index-err &&
2158
2159 ! ensure_not_expanded rm "**a" &&
2160 test_must_be_empty sparse-index-err
2161 '
2162
2163 test_expect_success 'sparse index is not expanded: rm' '
2164 init_repos &&
2165
2166 ensure_not_expanded rm deep/a &&
2167
2168 # test in-cone wildcard
2169 git -C sparse-index reset --hard &&
2170 ensure_not_expanded rm deep/* &&
2171
2172 # test recursive rm
2173 git -C sparse-index reset --hard &&
2174 ensure_not_expanded rm -r deep
2175 '
2176
2177 test_expect_success 'sparse index is not expanded: prefixed wildcard pathspec' '
2178 init_repos &&
2179
2180 ensure_not_expanded -C deep rm --dry-run -- "a*" &&
2181 ensure_not_expanded -C deep reset base -- "a*"
2182 '
2183
2184 test_expect_success 'grep with and --cached' '
2185 init_repos &&
2186
2187 test_all_match git grep --cached a &&
2188 test_all_match git grep --cached a -- "folder1/*"
2189 '
2190
2191 test_expect_success 'grep is not expanded' '
2192 init_repos &&
2193
2194 ensure_not_expanded grep a &&
2195 ensure_not_expanded grep a -- deep/* &&
2196
2197 # All files within the folder1/* pathspec are sparse,
2198 # so this command does not find any matches
2199 ensure_not_expanded ! grep a -- folder1/* &&
2200
2201 # test out-of-cone pathspec with or without wildcard
2202 ensure_not_expanded grep --cached a -- "folder1/a" &&
2203 ensure_not_expanded grep --cached a -- "folder1/*" &&
2204
2205 # test in-cone pathspec with or without wildcard
2206 ensure_not_expanded grep --cached a -- "deep/a" &&
2207 ensure_not_expanded grep --cached a -- "deep/*"
2208 '
2209
2210 # NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
2211 # Git expands the index of the submodules unexpectedly. Even though `grep`
2212 # builtin is marked as "command_requires_full_index = 0", this config is only
2213 # useful for the superproject. Namely, the submodules have their own configs,
2214 # which are _not_ populated by the one-time sparse-index feature switch.
2215 test_expect_failure 'grep within submodules is not expanded' '
2216 init_repos_as_submodules &&
2217
2218 # do not use ensure_not_expanded() here, because `grep` should be
2219 # run in the superproject, not in "./sparse-index"
2220 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
2221 git grep --cached --recurse-submodules a -- "*/folder1/*" &&
2222 test_region ! index ensure_full_index trace2.txt
2223 '
2224
2225 # NEEDSWORK: this test is not actually testing the code. The design purpose
2226 # of this test is to verify the grep result when the submodules are using a
2227 # sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
2228 # because of the index expansion, we are now grepping the "folder1/a" blob.
2229 # Because of the problem stated above 'grep within submodules is not expanded',
2230 # we don't have the ideal test environment yet.
2231 test_expect_success 'grep sparse directory within submodules' '
2232 init_repos_as_submodules &&
2233
2234 cat >expect <<-\EOF &&
2235 full-checkout/folder1/a:a
2236 sparse-checkout/folder1/a:a
2237 sparse-index/folder1/a:a
2238 EOF
2239 git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
2240 test_cmp actual expect
2241 '
2242
2243 test_expect_success 'write-tree' '
2244 init_repos &&
2245
2246 test_all_match git write-tree &&
2247
2248 write_script edit-contents <<-\EOF &&
2249 echo text >>"$1"
2250 EOF
2251
2252 # make a change inside the sparse cone
2253 run_on_all ../edit-contents deep/a &&
2254 test_all_match git update-index deep/a &&
2255 test_all_match git write-tree &&
2256 test_all_match git status --porcelain=v2 &&
2257
2258 # make a change outside the sparse cone
2259 run_on_all mkdir -p folder1 &&
2260 run_on_all cp a folder1/a &&
2261 run_on_all ../edit-contents folder1/a &&
2262 test_all_match git update-index folder1/a &&
2263 test_all_match git write-tree &&
2264 test_all_match git status --porcelain=v2 &&
2265
2266 # check that SKIP_WORKTREE files are not materialized
2267 test_path_is_missing sparse-checkout/folder2/a &&
2268 test_path_is_missing sparse-index/folder2/a
2269 '
2270
2271 test_expect_success 'sparse-index is not expanded: write-tree' '
2272 init_repos &&
2273
2274 ensure_not_expanded write-tree &&
2275
2276 echo "test1" >>sparse-index/a &&
2277 git -C sparse-index update-index a &&
2278 ensure_not_expanded write-tree
2279 '
2280
2281 test_expect_success 'diff-files with pathspec inside sparse definition' '
2282 init_repos &&
2283
2284 write_script edit-contents <<-\EOF &&
2285 echo text >>"$1"
2286 EOF
2287
2288 run_on_all ../edit-contents deep/a &&
2289
2290 test_all_match git diff-files &&
2291
2292 test_all_match git diff-files -- deep/a &&
2293
2294 # test wildcard
2295 test_all_match git diff-files -- "deep/*"
2296 '
2297
2298 test_expect_success 'diff-files with pathspec outside sparse definition' '
2299 init_repos &&
2300
2301 test_sparse_match git diff-files -- folder2/a &&
2302
2303 write_script edit-contents <<-\EOF &&
2304 echo text >>"$1"
2305 EOF
2306
2307 # The directory "folder1" is outside the cone of interest
2308 # and will not exist in the sparse checkout repositories.
2309 # Create it as needed, add file "folder1/a" there with
2310 # contents that is different from the staged version.
2311 run_on_all mkdir -p folder1 &&
2312 run_on_all cp a folder1/a &&
2313
2314 run_on_all ../edit-contents folder1/a &&
2315 test_all_match git diff-files &&
2316 test_all_match git diff-files -- folder1/a &&
2317 test_all_match git diff-files -- "folder*/a"
2318 '
2319
2320 test_expect_success 'sparse index is not expanded: diff-files' '
2321 init_repos &&
2322
2323 write_script edit-contents <<-\EOF &&
2324 echo text >>"$1"
2325 EOF
2326
2327 run_on_all ../edit-contents deep/a &&
2328
2329 ensure_not_expanded diff-files &&
2330 ensure_not_expanded diff-files -- deep/a &&
2331 ensure_not_expanded diff-files -- "deep/*"
2332 '
2333
2334 test_expect_success 'diff-tree' '
2335 init_repos &&
2336
2337 # Test change inside sparse cone
2338 tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
2339 tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
2340 test_all_match git diff-tree $tree1 $tree2 &&
2341 test_all_match git diff-tree $tree1 $tree2 -- deep/a &&
2342 test_all_match git diff-tree HEAD update-deep &&
2343 test_all_match git diff-tree HEAD update-deep -- deep/a &&
2344
2345 # Test change outside sparse cone
2346 tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
2347 test_all_match git diff-tree $tree1 $tree3 &&
2348 test_all_match git diff-tree $tree1 $tree3 -- folder1/a &&
2349 test_all_match git diff-tree HEAD update-folder1 &&
2350 test_all_match git diff-tree HEAD update-folder1 -- folder1/a &&
2351
2352 # Check that SKIP_WORKTREE files are not materialized
2353 test_path_is_missing sparse-checkout/folder1/a &&
2354 test_path_is_missing sparse-index/folder1/a &&
2355 test_path_is_missing sparse-checkout/folder2/a &&
2356 test_path_is_missing sparse-index/folder2/a
2357 '
2358
2359 test_expect_success 'sparse-index is not expanded: diff-tree' '
2360 init_repos &&
2361
2362 tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
2363 tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
2364 tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
2365
2366 ensure_not_expanded diff-tree $tree1 $tree2 &&
2367 ensure_not_expanded diff-tree $tree1 $tree2 -- deep/a &&
2368 ensure_not_expanded diff-tree HEAD update-deep &&
2369 ensure_not_expanded diff-tree HEAD update-deep -- deep/a &&
2370 ensure_not_expanded diff-tree $tree1 $tree3 &&
2371 ensure_not_expanded diff-tree $tree1 $tree3 -- folder1/a &&
2372 ensure_not_expanded diff-tree HEAD update-folder1 &&
2373 ensure_not_expanded diff-tree HEAD update-folder1 -- folder1/a
2374 '
2375
2376 test_expect_success 'worktree' '
2377 init_repos &&
2378
2379 write_script edit-contents <<-\EOF &&
2380 echo text >>"$1"
2381 EOF
2382
2383 for repo in full-checkout sparse-checkout sparse-index
2384 do
2385 worktree=${repo}-wt &&
2386 git -C $repo worktree add ../$worktree &&
2387
2388 # Compare worktree content with "ls"
2389 (cd $repo && ls) >worktree_contents &&
2390 (cd $worktree && ls) >new_worktree_contents &&
2391 test_cmp worktree_contents new_worktree_contents &&
2392
2393 # Compare index content with "ls-files --sparse"
2394 git -C $repo ls-files --sparse >index_contents &&
2395 git -C $worktree ls-files --sparse >new_index_contents &&
2396 test_cmp index_contents new_index_contents &&
2397
2398 git -C $repo worktree remove ../$worktree || return 1
2399 done &&
2400
2401 test_all_match git worktree add .worktrees/hotfix &&
2402 run_on_all ../edit-contents .worktrees/hotfix/deep/a &&
2403 test_all_match test_must_fail git worktree remove .worktrees/hotfix
2404 '
2405
2406 test_expect_success 'worktree is not expanded' '
2407 init_repos &&
2408
2409 ensure_not_expanded worktree add .worktrees/hotfix &&
2410 ensure_not_expanded worktree remove .worktrees/hotfix
2411 '
2412
2413 test_expect_success 'check-attr with pathspec inside sparse definition' '
2414 init_repos &&
2415
2416 echo "a -crlf myAttr" >>.gitattributes &&
2417 run_on_all cp ../.gitattributes ./deep &&
2418
2419 test_all_match git check-attr -a -- deep/a &&
2420
2421 test_all_match git add deep/.gitattributes &&
2422 test_all_match git check-attr -a --cached -- deep/a
2423 '
2424
2425 test_expect_success 'check-attr with pathspec outside sparse definition' '
2426 init_repos &&
2427
2428 echo "a -crlf myAttr" >>.gitattributes &&
2429 run_on_sparse mkdir folder1 &&
2430 run_on_all cp ../.gitattributes ./folder1 &&
2431 run_on_all cp a folder1/a &&
2432
2433 test_all_match git check-attr -a -- folder1/a &&
2434
2435 git -C full-checkout add folder1/.gitattributes &&
2436 test_sparse_match git add --sparse folder1/.gitattributes &&
2437 test_all_match git commit -m "add .gitattributes" &&
2438 test_sparse_match git sparse-checkout reapply &&
2439 test_all_match git check-attr -a --cached -- folder1/a
2440 '
2441
2442 # NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
2443 # to an underlying issue in oneway_diff() within diff-lib.c.
2444 # 'do_oneway_diff()' is not called as expected for paths that could match
2445 # inside of a sparse directory. Specifically, the 'ce_path_match()' function
2446 # fails to recognize files inside a sparse directory (e.g., when 'folder1/'
2447 # is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
2448 # proceed with 'do_oneway_diff()' if the pathspec could match inside of a
2449 # sparse directory.
2450 test_expect_failure 'diff --check with pathspec outside sparse definition' '
2451 init_repos &&
2452
2453 write_script edit-contents <<-\EOF &&
2454 echo "a " >"$1"
2455 EOF
2456
2457 test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
2458
2459 echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
2460 run_on_all mkdir -p folder1 &&
2461 run_on_all cp ../.gitattributes ./folder1 &&
2462 test_all_match git add --sparse folder1/.gitattributes &&
2463 run_on_all ../edit-contents folder1/a &&
2464 test_all_match git add --sparse folder1/a &&
2465
2466 test_sparse_match git sparse-checkout reapply &&
2467 test_all_match test_must_fail git diff --check --cached -- folder1/a
2468 '
2469
2470 test_expect_success 'sparse-index is not expanded: check-attr' '
2471 init_repos &&
2472
2473 echo "a -crlf myAttr" >>.gitattributes &&
2474 mkdir ./sparse-index/folder1 &&
2475 cp ./sparse-index/a ./sparse-index/folder1/a &&
2476 cp .gitattributes ./sparse-index/deep &&
2477 cp .gitattributes ./sparse-index/folder1 &&
2478
2479 git -C sparse-index add deep/.gitattributes &&
2480 git -C sparse-index add --sparse folder1/.gitattributes &&
2481 ensure_not_expanded check-attr -a --cached -- deep/a &&
2482 ensure_not_expanded check-attr -a --cached -- folder1/a
2483 '
2484
2485 test_expect_success 'sparse-index is not expanded: git apply' '
2486 init_repos &&
2487
2488 git -C sparse-index checkout base &&
2489 git -C full-checkout diff base..merge-right -- deep >patch-in-sparse &&
2490 git -C full-checkout diff base..merge-right -- folder2 >patch-outside &&
2491
2492 # Apply a patch to a file inside the sparse definition
2493 ensure_not_expanded apply --index --stat ../patch-in-sparse &&
2494
2495 # Apply a patch to a file outside the sparse definition
2496 # Fails when caring about the worktree.
2497 ensure_not_expanded ! apply ../patch-outside &&
2498
2499 # Expands when using --index.
2500 ensure_expanded apply --index ../patch-outside &&
2501
2502 # Does not when index is partially expanded.
2503 git -C sparse-index reset --hard &&
2504 ensure_not_expanded apply --cached ../patch-outside &&
2505
2506 # Try again with a reset and collapsed index.
2507 git -C sparse-index reset --hard &&
2508 git -C sparse-index sparse-checkout reapply &&
2509
2510 # Expands when index is collapsed.
2511 ensure_expanded apply --cached ../patch-outside
2512 '
2513
2514 test_expect_success 'sparse-index is not expanded: git add -p' '
2515 init_repos &&
2516
2517 # Does not expand when edits are within sparse checkout.
2518 echo "new content" >sparse-index/deep/a &&
2519 echo "new content" >sparse-index/deep/deeper1/a &&
2520 test_write_lines y n >in &&
2521 ensure_not_expanded add -p <in &&
2522 git -C sparse-index reset &&
2523 ensure_not_expanded add -i <in &&
2524
2525 # -p does expand when edits are outside sparse checkout.
2526 mkdir -p sparse-index/folder1 &&
2527 echo "new content" >sparse-index/folder1/a &&
2528 test_write_lines y n y >in &&
2529 ensure_expanded add -p <in &&
2530
2531 # Fully reset the index.
2532 git -C sparse-index reset --hard &&
2533 git -C sparse-index sparse-checkout reapply &&
2534
2535 # -i does expand when edits are outside sparse checkout.
2536 mkdir -p sparse-index/folder1 &&
2537 echo "new content" >sparse-index/folder1/a &&
2538 test_write_lines u 2 3 "" q >in &&
2539 ensure_expanded add -i <in
2540 '
2541
2542 test_expect_success 'sparse-index is not expanded: checkout -p, reset -p' '
2543 init_repos &&
2544
2545 # Does not expand when edits are within sparse checkout.
2546 echo "new content" >sparse-index/deep/a &&
2547 echo "new content" >sparse-index/deep/deeper1/a &&
2548 git -C sparse-index commit -a -m "inside-changes" &&
2549
2550 test_write_lines y y >in &&
2551 ensure_not_expanded checkout HEAD~1 --patch <in &&
2552
2553 echo "new content" >sparse-index/deep/a &&
2554 echo "new content" >sparse-index/deep/deeper1/a &&
2555 git -C sparse-index add . &&
2556 ensure_not_expanded reset --patch <in &&
2557
2558 # -p does expand when edits are outside sparse checkout.
2559 mkdir -p sparse-index/folder1 &&
2560 echo "new content" >sparse-index/folder1/a &&
2561 git -C sparse-index add --sparse folder1 &&
2562 git -C sparse-index sparse-checkout reapply &&
2563 ensure_expanded reset --patch <in &&
2564
2565 # Fully reset the index.
2566 mkdir -p sparse-index/folder1 &&
2567 echo "new content" >sparse-index/folder1/a &&
2568 git -C sparse-index add --sparse folder1 &&
2569 git -C sparse-index commit -m "folder1 change" &&
2570 git -C sparse-index sparse-checkout reapply &&
2571 ensure_expanded checkout HEAD~1 --patch <in
2572 '
2573
2574 test_expect_success 'advice.sparseIndexExpanded' '
2575 init_repos &&
2576
2577 git -C sparse-index config --unset advice.sparseIndexExpanded &&
2578 git -C sparse-index sparse-checkout set deep/deeper1 &&
2579 mkdir -p sparse-index/deep/deeper2/deepest &&
2580 touch sparse-index/deep/deeper2/deepest/bogus &&
2581 git -C sparse-index status 2>err &&
2582 test_grep "The sparse index is expanding to a full index" err &&
2583
2584 git -C sparse-index sparse-checkout disable 2>err &&
2585 test_line_count = 0 err
2586 '
2587
2588 test_expect_success 'cat-file -p' '
2589 init_repos &&
2590 echo "new content" >>full-checkout/deep/a &&
2591 echo "new content" >>sparse-checkout/deep/a &&
2592 echo "new content" >>sparse-index/deep/a &&
2593 run_on_all git add deep/a &&
2594
2595 test_all_match git cat-file -p :deep/a &&
2596 ensure_not_expanded cat-file -p :deep/a &&
2597 test_all_match git cat-file -p :folder1/a &&
2598 ensure_expanded cat-file -p :folder1/a
2599 '
2600
2601 test_expect_success 'cat-file --batch' '
2602 init_repos &&
2603 echo "new content" >>full-checkout/deep/a &&
2604 echo "new content" >>sparse-checkout/deep/a &&
2605 echo "new content" >>sparse-index/deep/a &&
2606 run_on_all git add deep/a &&
2607
2608 echo ":deep/a" >in &&
2609 test_all_match git cat-file --batch <in &&
2610 ensure_not_expanded cat-file --batch <in &&
2611
2612 echo ":folder1/a" >in &&
2613 test_all_match git cat-file --batch <in &&
2614 ensure_expanded cat-file --batch <in &&
2615
2616 cat >in <<-\EOF &&
2617 :deep/a
2618 :folder1/a
2619 EOF
2620 test_all_match git cat-file --batch <in &&
2621 ensure_expanded cat-file --batch <in
2622 '
2623
2624 test_expect_success 'merge -s ours' '
2625 init_repos &&
2626
2627 test_all_match git rev-parse HEAD^{tree} &&
2628 test_all_match git merge -s ours merge-right &&
2629 test_all_match git rev-parse HEAD^{tree} &&
2630 test_all_match git rev-parse HEAD^2
2631 '
2632
2633 test_expect_success 'sparse-index is not expanded: merge-ours' '
2634 init_repos &&
2635 ensure_not_expanded merge -s ours merge-right
2636 '
2637
2638 test_expect_success 'restore --staged with sparse definition' '
2639 init_repos &&
2640
2641 # Stage changes within the sparse definition
2642 test_all_match git checkout -b restore-staged-1 base &&
2643 test_all_match git reset --soft update-deep &&
2644 test_all_match git restore --staged . &&
2645 test_all_match git status --porcelain=v2 &&
2646 test_all_match git diff --cached
2647 '
2648
2649 test_expect_success 'restore --staged with outside sparse definition' '
2650 init_repos &&
2651
2652 # Stage changes that include paths outside the sparse definition.
2653 # Although the working tree differs between full and sparse checkouts
2654 # after restore, the state of the index should be the same.
2655 test_all_match git checkout -b restore-staged-2 base &&
2656 test_all_match git reset --soft update-folder1 &&
2657 test_sparse_match git restore --staged . &&
2658 git -C full-checkout restore --staged . &&
2659 test_all_match git ls-files -s -- folder1 &&
2660 test_all_match git diff --cached -- folder1
2661 '
2662
2663 test_expect_success 'restore --staged with wildcards' '
2664 init_repos &&
2665
2666 test_all_match git checkout -b restore-staged-3 base &&
2667 test_all_match git reset --soft update-deep &&
2668 test_all_match git restore --staged "deep/*" &&
2669 test_all_match git status --porcelain=v2 &&
2670 test_all_match git diff --cached
2671 '
2672
2673 test_expect_success 'sparse-index is not expanded: restore --staged' '
2674 init_repos &&
2675
2676 git -C sparse-index checkout -b restore-staged-exp base &&
2677 git -C sparse-index reset --soft update-folder1 &&
2678 ensure_not_expanded restore --staged .
2679 '
2680
2681 test_expect_success 'sparse-index is not expanded: restore --source --staged' '
2682 init_repos &&
2683
2684 git -C sparse-index checkout -b restore-source-staged base &&
2685 ensure_not_expanded restore --source update-folder1 --staged .
2686 '
2687
2688 test_done