Git fork
1#!/bin/sh
2
3test_description='compare full workdir to sparse workdir'
4
5GIT_TEST_SPLIT_INDEX=0
6GIT_TEST_SPARSE_INDEX=
7
8. ./test-lib.sh
9
10test_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
144init_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
168init_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
181run_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
194run_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
204test_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
212test_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
218test_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).
232test_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
259test_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
296test_expect_success 'expanded in-memory index matches full index' '
297 init_repos &&
298 test_sparse_match git ls-files --stage
299'
300
301test_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
320test_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
336test_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
343test_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
351test_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
387test_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
425test_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
433test_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
441test_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
450test_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 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 grep "Disable or modify the sparsity rules" sparse-checkout-err &&
461 test_sparse_unstaged folder1/newfile
462'
463
464test_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
492test_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 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 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
535test_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
550test_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'.
567test_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
584test_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
604test_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
618test_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.
633test_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
653test_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 grep "M folder1/a" full-checkout-out &&
665 ! grep "M folder1/a" sparse-checkout-out &&
666 run_on_sparse test_path_is_missing folder1
667'
668
669test_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
686test_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
703test_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.
725test_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
738test_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
765test_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
782test_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
812test_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.
836test_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
877test_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 grep "Ignoring path deep/" sparse-checkout-err &&
884 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
899test_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
922test_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
955for MERGE_TREES in "base HEAD update-folder2" \
956 "update-folder1 update-folder2" \
957 "update-folder2"
958do
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 '
978done
979
980test_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
999test_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
1022test_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
1042test_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
1055test_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 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 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
1089test_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 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 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
1133test_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.
1147test_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
1167test_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
1216test_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
1230test_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
1255test_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 grep "is a sparse directory" sparse-index-err
1270'
1271
1272test_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
1284test_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
1333for builtin in show rev-parse
1334do
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 "
1358done
1359
1360test_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 grep "100644 .* modules/a" cache &&
1378 grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache
1379'
1380
1381test_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 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.
1409test_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
1424test_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
1455run_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
1478ensure_expanded () {
1479 run_sparse_index_trace2 "$@" &&
1480 test_region index ensure_full_index trace2.txt
1481}
1482
1483ensure_not_expanded () {
1484 run_sparse_index_trace2 "$@" &&
1485 test_region ! index ensure_full_index trace2.txt
1486}
1487
1488test_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
1561test_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
1577test_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
1601test_expect_success 'describe tested on all' '
1602 init_repos &&
1603
1604 # Add tag to be read by describe
1605
1606 run_on_all git tag -a v1.0 -m "Version 1" &&
1607 test_all_match git describe --dirty &&
1608 run_on_all rm g &&
1609 test_all_match git describe --dirty
1610'
1611
1612test_expect_success 'ls-files filtering and expansion' '
1613 init_repos &&
1614
1615 # This filtering will hit a sparse directory midway
1616 # through the iteration.
1617 test_all_match git ls-files deep &&
1618
1619 # This pathspec will filter the index to only a sparse
1620 # directory.
1621 test_all_match git ls-files folder1
1622'
1623
1624test_expect_success 'sparse-index is not expanded: describe' '
1625 init_repos &&
1626
1627 # Add tag to be read by describe
1628
1629 git -C sparse-index tag -a v1.0 -m "Version 1" &&
1630
1631 ensure_not_expanded describe --dirty &&
1632 echo "test" >>sparse-index/g &&
1633 ensure_not_expanded describe --dirty &&
1634 ensure_not_expanded describe
1635'
1636
1637test_expect_success 'sparse index is not expanded: diff and diff-index' '
1638 init_repos &&
1639
1640 write_script edit-contents <<-\EOF &&
1641 echo text >>$1
1642 EOF
1643
1644 # Add file within cone
1645 test_sparse_match git sparse-checkout set deep &&
1646 run_on_all ../edit-contents deep/testfile &&
1647 test_all_match git add deep/testfile &&
1648 run_on_all ../edit-contents deep/testfile &&
1649
1650 test_all_match git diff &&
1651 test_all_match git diff --cached &&
1652 ensure_not_expanded diff &&
1653 ensure_not_expanded diff --cached &&
1654 ensure_not_expanded diff-index --cached HEAD &&
1655
1656 # Add file outside cone
1657 test_all_match git reset --hard &&
1658 run_on_all mkdir newdirectory &&
1659 run_on_all ../edit-contents newdirectory/testfile &&
1660 test_sparse_match git sparse-checkout set newdirectory &&
1661 test_all_match git add newdirectory/testfile &&
1662 run_on_all ../edit-contents newdirectory/testfile &&
1663 test_sparse_match git sparse-checkout set &&
1664
1665 test_all_match git diff &&
1666 test_all_match git diff --cached &&
1667 ensure_not_expanded diff &&
1668 ensure_not_expanded diff --cached &&
1669 ensure_not_expanded diff-index --cached HEAD &&
1670
1671 # Merge conflict outside cone
1672 # The sparse checkout will report a warning that is not in the
1673 # full checkout, so we use `run_on_all` instead of
1674 # `test_all_match`
1675 run_on_all git reset --hard &&
1676 test_all_match git checkout merge-left &&
1677 test_all_match test_must_fail git merge merge-right &&
1678
1679 test_all_match git diff &&
1680 test_all_match git diff --cached &&
1681 ensure_not_expanded diff &&
1682 ensure_not_expanded diff --cached &&
1683 ensure_not_expanded diff-index --cached HEAD
1684'
1685
1686test_expect_success 'sparse index is not expanded: show and rev-parse' '
1687 init_repos &&
1688
1689 ensure_not_expanded show :a &&
1690 ensure_not_expanded show :deep/a &&
1691 ensure_not_expanded rev-parse :a &&
1692 ensure_not_expanded rev-parse :deep/a
1693'
1694
1695test_expect_success 'sparse index is not expanded: update-index' '
1696 init_repos &&
1697
1698 deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
1699 ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a &&
1700
1701 echo "test" >sparse-index/README.md &&
1702 echo "test2" >sparse-index/a &&
1703 rm -f sparse-index/deep/a &&
1704
1705 ensure_not_expanded update-index --add README.md &&
1706 ensure_not_expanded update-index a &&
1707 ensure_not_expanded update-index --remove deep/a &&
1708
1709 ensure_not_expanded reset --soft update-deep &&
1710 ensure_not_expanded update-index --add --remove --again
1711'
1712
1713test_expect_success 'sparse index is not expanded: blame' '
1714 init_repos &&
1715
1716 for file in a \
1717 deep/a \
1718 deep/deeper1/a \
1719 deep/deeper1/deepest/a
1720 do
1721 ensure_not_expanded blame $file || return 1
1722 done
1723'
1724
1725test_expect_success 'sparse index is not expanded: fetch/pull' '
1726 init_repos &&
1727
1728 git -C sparse-index remote add full "file://$(pwd)/full-checkout" &&
1729 ensure_not_expanded fetch full &&
1730 git -C full-checkout commit --allow-empty -m "for pull merge" &&
1731 git -C sparse-index commit --allow-empty -m "for pull merge" &&
1732 ensure_not_expanded pull full base
1733'
1734
1735test_expect_success 'sparse index is not expanded: read-tree' '
1736 init_repos &&
1737
1738 ensure_not_expanded checkout -b test-branch update-folder1 &&
1739 for MERGE_TREES in "base HEAD update-folder2" \
1740 "base HEAD rename-base" \
1741 "base update-folder2" \
1742 "base rename-base" \
1743 "update-folder2"
1744 do
1745 ensure_not_expanded read-tree -mu $MERGE_TREES &&
1746 ensure_not_expanded reset --hard || return 1
1747 done &&
1748
1749 rm -rf sparse-index/deep/deeper2 &&
1750 ensure_not_expanded add . &&
1751 ensure_not_expanded commit -m "test" &&
1752
1753 ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest
1754'
1755
1756test_expect_success 'ls-files' '
1757 init_repos &&
1758
1759 # Use a smaller sparse-checkout for reduced output
1760 test_sparse_match git sparse-checkout set &&
1761
1762 # Behavior agrees by default. Sparse index is expanded.
1763 test_all_match git ls-files &&
1764
1765 # With --sparse, the sparse index data changes behavior.
1766 git -C sparse-index ls-files --sparse >actual &&
1767
1768 cat >expect <<-\EOF &&
1769 a
1770 before/
1771 deep/
1772 e
1773 folder1-
1774 folder1.x
1775 folder1/
1776 folder10
1777 folder2/
1778 g
1779 x/
1780 z
1781 EOF
1782
1783 test_cmp expect actual &&
1784
1785 # With --sparse and no sparse index, nothing changes.
1786 git -C sparse-checkout ls-files >dense &&
1787 git -C sparse-checkout ls-files --sparse >sparse &&
1788 test_cmp dense sparse &&
1789
1790 # Set up a strange condition of having a file edit
1791 # outside of the sparse-checkout cone. We want to verify
1792 # that all modes handle this the same, and detect the
1793 # modification.
1794 write_script edit-content <<-\EOF &&
1795 mkdir -p folder1 &&
1796 echo content >>folder1/a
1797 EOF
1798 run_on_all ../edit-content &&
1799
1800 test_all_match git ls-files --modified &&
1801
1802 git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
1803 cat >expect <<-\EOF &&
1804 folder1/a
1805 EOF
1806 test_cmp expect sparse-index-out &&
1807
1808 # Add folder1 to the sparse-checkout cone and
1809 # check that ls-files shows the expanded files.
1810 test_sparse_match git sparse-checkout add folder1 &&
1811 test_all_match git ls-files --modified &&
1812
1813 test_all_match git ls-files &&
1814 git -C sparse-index ls-files --sparse >actual &&
1815
1816 cat >expect <<-\EOF &&
1817 a
1818 before/
1819 deep/
1820 e
1821 folder1-
1822 folder1.x
1823 folder1/0/0/0
1824 folder1/0/1
1825 folder1/a
1826 folder10
1827 folder2/
1828 g
1829 x/
1830 z
1831 EOF
1832
1833 test_cmp expect actual &&
1834
1835 # Double-check index expansion is avoided
1836 ensure_not_expanded ls-files --sparse
1837'
1838
1839test_expect_success 'sparse index is not expanded: sparse-checkout' '
1840 init_repos &&
1841
1842 ensure_not_expanded sparse-checkout set deep/deeper2 &&
1843 ensure_not_expanded sparse-checkout set deep/deeper1 &&
1844 ensure_not_expanded sparse-checkout set deep &&
1845 ensure_not_expanded sparse-checkout add folder1 &&
1846 ensure_not_expanded sparse-checkout set deep/deeper1 &&
1847 ensure_not_expanded sparse-checkout set folder2 &&
1848
1849 # Demonstrate that the checks that "folder1/a" is a file
1850 # do not cause a sparse-index expansion (since it is in the
1851 # sparse-checkout cone).
1852 echo >>sparse-index/folder2/a &&
1853 git -C sparse-index add folder2/a &&
1854
1855 ensure_not_expanded sparse-checkout add folder1 &&
1856
1857 # Skip checks here, since deep/deeper1 is inside a sparse directory
1858 # that must be expanded to check whether `deep/deeper1` is a file
1859 # or not.
1860 ensure_not_expanded sparse-checkout set --skip-checks deep/deeper1 &&
1861 ensure_not_expanded sparse-checkout set
1862'
1863
1864# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
1865# in this scenario, but it shouldn't.
1866test_expect_success 'reset mixed and checkout orphan' '
1867 init_repos &&
1868
1869 test_all_match git checkout rename-out-to-in &&
1870
1871 # Sparse checkouts do not agree with full checkouts about
1872 # how to report a directory/file conflict during a reset.
1873 # This command would fail with test_all_match because the
1874 # full checkout reports "T folder1/0/1" while a sparse
1875 # checkout reports "D folder1/0/1". This matches because
1876 # the sparse checkouts skip "adding" the other side of
1877 # the conflict.
1878 test_sparse_match git reset --mixed HEAD~1 &&
1879 test_sparse_match git ls-files --stage &&
1880 test_sparse_match git status --porcelain=v2 &&
1881
1882 # At this point, sparse-checkouts behave differently
1883 # from the full-checkout.
1884 test_sparse_match git checkout --orphan new-branch &&
1885 test_sparse_match git ls-files --stage &&
1886 test_sparse_match git status --porcelain=v2
1887'
1888
1889test_expect_success 'add everything with deep new file' '
1890 init_repos &&
1891
1892 run_on_sparse git sparse-checkout set deep/deeper1/deepest &&
1893
1894 run_on_all touch deep/deeper1/x &&
1895 test_all_match git add . &&
1896 test_all_match git status --porcelain=v2
1897'
1898
1899# NEEDSWORK: 'git checkout' behaves incorrectly in the case of
1900# directory/file conflicts, even without sparse-checkout. Use this
1901# test only as a documentation of the incorrect behavior, not a
1902# measure of how it _should_ behave.
1903test_expect_success 'checkout behaves oddly with df-conflict-1' '
1904 init_repos &&
1905
1906 test_sparse_match git sparse-checkout disable &&
1907
1908 write_script edit-content <<-\EOF &&
1909 echo content >>folder1/larger-content
1910 git add folder1
1911 EOF
1912
1913 run_on_all ../edit-content &&
1914 test_all_match git status --porcelain=v2 &&
1915
1916 git -C sparse-checkout sparse-checkout init --cone &&
1917 git -C sparse-index sparse-checkout init --cone --sparse-index &&
1918
1919 test_all_match git status --porcelain=v2 &&
1920
1921 # This checkout command should fail, because we have a staged
1922 # change to folder1/larger-content, but the destination changes
1923 # folder1 to a file.
1924 git -C full-checkout checkout df-conflict-1 \
1925 1>full-checkout-out \
1926 2>full-checkout-err &&
1927 git -C sparse-checkout checkout df-conflict-1 \
1928 1>sparse-checkout-out \
1929 2>sparse-checkout-err &&
1930 git -C sparse-index checkout df-conflict-1 \
1931 1>sparse-index-out \
1932 2>sparse-index-err &&
1933
1934 # Instead, the checkout deletes the folder1 file and adds the
1935 # folder1/larger-content file, leaving all other paths that were
1936 # in folder1/ as deleted (without any warning).
1937 cat >expect <<-EOF &&
1938 D folder1
1939 A folder1/larger-content
1940 EOF
1941 test_cmp expect full-checkout-out &&
1942 test_cmp expect sparse-checkout-out &&
1943
1944 # The sparse-index reports no output
1945 test_must_be_empty sparse-index-out &&
1946
1947 # stderr: Switched to branch df-conflict-1
1948 test_cmp full-checkout-err sparse-checkout-err &&
1949 test_cmp full-checkout-err sparse-checkout-err
1950'
1951
1952# NEEDSWORK: 'git checkout' behaves incorrectly in the case of
1953# directory/file conflicts, even without sparse-checkout. Use this
1954# test only as a documentation of the incorrect behavior, not a
1955# measure of how it _should_ behave.
1956test_expect_success 'checkout behaves oddly with df-conflict-2' '
1957 init_repos &&
1958
1959 test_sparse_match git sparse-checkout disable &&
1960
1961 write_script edit-content <<-\EOF &&
1962 echo content >>folder2/larger-content
1963 git add folder2
1964 EOF
1965
1966 run_on_all ../edit-content &&
1967 test_all_match git status --porcelain=v2 &&
1968
1969 git -C sparse-checkout sparse-checkout init --cone &&
1970 git -C sparse-index sparse-checkout init --cone --sparse-index &&
1971
1972 test_all_match git status --porcelain=v2 &&
1973
1974 # This checkout command should fail, because we have a staged
1975 # change to folder1/larger-content, but the destination changes
1976 # folder1 to a file.
1977 git -C full-checkout checkout df-conflict-2 \
1978 1>full-checkout-out \
1979 2>full-checkout-err &&
1980 git -C sparse-checkout checkout df-conflict-2 \
1981 1>sparse-checkout-out \
1982 2>sparse-checkout-err &&
1983 git -C sparse-index checkout df-conflict-2 \
1984 1>sparse-index-out \
1985 2>sparse-index-err &&
1986
1987 # The full checkout deviates from the df-conflict-1 case here!
1988 # It drops the change to folder1/larger-content and leaves the
1989 # folder1 path as-is on disk. The sparse-index behaves the same.
1990 test_must_be_empty full-checkout-out &&
1991 test_must_be_empty sparse-index-out &&
1992
1993 # In the sparse-checkout case, the checkout deletes the folder1
1994 # file and adds the folder1/larger-content file, leaving all other
1995 # paths that were in folder1/ as deleted (without any warning).
1996 cat >expect <<-EOF &&
1997 D folder2
1998 A folder2/larger-content
1999 EOF
2000 test_cmp expect sparse-checkout-out &&
2001
2002 # Switched to branch df-conflict-1
2003 test_cmp full-checkout-err sparse-checkout-err &&
2004 test_cmp full-checkout-err sparse-index-err
2005'
2006
2007test_expect_success 'mv directory from out-of-cone to in-cone' '
2008 init_repos &&
2009
2010 # <source> as a sparse directory (or SKIP_WORKTREE_DIR without enabling
2011 # sparse index).
2012 test_all_match git mv --sparse folder1 deep &&
2013 test_all_match git status --porcelain=v2 &&
2014 test_sparse_match git ls-files -t &&
2015 git -C sparse-checkout ls-files -t >actual &&
2016 grep -e "H deep/folder1/0/0/0" actual &&
2017 grep -e "H deep/folder1/0/1" actual &&
2018 grep -e "H deep/folder1/a" actual &&
2019
2020 test_all_match git reset --hard &&
2021
2022 # <source> as a directory deeper than sparse index boundary (where
2023 # sparse index will expand).
2024 test_sparse_match git mv --sparse folder1/0 deep &&
2025 test_sparse_match git status --porcelain=v2 &&
2026 test_sparse_match git ls-files -t &&
2027 git -C sparse-checkout ls-files -t >actual &&
2028 grep -e "H deep/0/0/0" actual &&
2029 grep -e "H deep/0/1" actual
2030'
2031
2032test_expect_success 'rm pathspec inside sparse definition' '
2033 init_repos &&
2034
2035 test_all_match git rm deep/a &&
2036 test_all_match git status --porcelain=v2 &&
2037
2038 # test wildcard
2039 run_on_all git reset --hard &&
2040 test_all_match git rm deep/* &&
2041 test_all_match git status --porcelain=v2 &&
2042
2043 # test recursive rm
2044 run_on_all git reset --hard &&
2045 test_all_match git rm -r deep &&
2046 test_all_match git status --porcelain=v2
2047'
2048
2049test_expect_success 'rm pathspec outside sparse definition' '
2050 init_repos &&
2051
2052 for file in folder1/a folder1/0/1
2053 do
2054 test_sparse_match test_must_fail git rm $file &&
2055 test_sparse_match test_must_fail git rm --cached $file &&
2056 test_sparse_match git rm --sparse $file &&
2057 test_sparse_match git status --porcelain=v2 || return 1
2058 done &&
2059
2060 cat >folder1-full <<-EOF &&
2061 rm ${SQ}folder1/0/0/0${SQ}
2062 rm ${SQ}folder1/0/1${SQ}
2063 rm ${SQ}folder1/a${SQ}
2064 EOF
2065
2066 cat >folder1-sparse <<-EOF &&
2067 rm ${SQ}folder1/${SQ}
2068 EOF
2069
2070 # test wildcard
2071 run_on_sparse git reset --hard &&
2072 run_on_sparse git sparse-checkout reapply &&
2073 test_sparse_match test_must_fail git rm folder1/* &&
2074 run_on_sparse git rm --sparse folder1/* &&
2075 test_cmp folder1-full sparse-checkout-out &&
2076 test_cmp folder1-sparse sparse-index-out &&
2077 test_sparse_match git status --porcelain=v2 &&
2078
2079 # test recursive rm
2080 run_on_sparse git reset --hard &&
2081 run_on_sparse git sparse-checkout reapply &&
2082 test_sparse_match test_must_fail git rm --sparse folder1 &&
2083 run_on_sparse git rm --sparse -r folder1 &&
2084 test_cmp folder1-full sparse-checkout-out &&
2085 test_cmp folder1-sparse sparse-index-out &&
2086 test_sparse_match git status --porcelain=v2
2087'
2088
2089test_expect_success 'rm pathspec expands index when necessary' '
2090 init_repos &&
2091
2092 # in-cone pathspec (do not expand)
2093 ensure_not_expanded rm "deep/deep*" &&
2094 test_must_be_empty sparse-index-err &&
2095
2096 # out-of-cone pathspec (expand)
2097 ! ensure_not_expanded rm --sparse "folder1/a*" &&
2098 test_must_be_empty sparse-index-err &&
2099
2100 # pathspec that should expand index
2101 ! ensure_not_expanded rm "*/a" &&
2102 test_must_be_empty sparse-index-err &&
2103
2104 ! ensure_not_expanded rm "**a" &&
2105 test_must_be_empty sparse-index-err
2106'
2107
2108test_expect_success 'sparse index is not expanded: rm' '
2109 init_repos &&
2110
2111 ensure_not_expanded rm deep/a &&
2112
2113 # test in-cone wildcard
2114 git -C sparse-index reset --hard &&
2115 ensure_not_expanded rm deep/* &&
2116
2117 # test recursive rm
2118 git -C sparse-index reset --hard &&
2119 ensure_not_expanded rm -r deep
2120'
2121
2122test_expect_success 'grep with and --cached' '
2123 init_repos &&
2124
2125 test_all_match git grep --cached a &&
2126 test_all_match git grep --cached a -- "folder1/*"
2127'
2128
2129test_expect_success 'grep is not expanded' '
2130 init_repos &&
2131
2132 ensure_not_expanded grep a &&
2133 ensure_not_expanded grep a -- deep/* &&
2134
2135 # All files within the folder1/* pathspec are sparse,
2136 # so this command does not find any matches
2137 ensure_not_expanded ! grep a -- folder1/* &&
2138
2139 # test out-of-cone pathspec with or without wildcard
2140 ensure_not_expanded grep --cached a -- "folder1/a" &&
2141 ensure_not_expanded grep --cached a -- "folder1/*" &&
2142
2143 # test in-cone pathspec with or without wildcard
2144 ensure_not_expanded grep --cached a -- "deep/a" &&
2145 ensure_not_expanded grep --cached a -- "deep/*"
2146'
2147
2148# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
2149# Git expands the index of the submodules unexpectedly. Even though `grep`
2150# builtin is marked as "command_requires_full_index = 0", this config is only
2151# useful for the superproject. Namely, the submodules have their own configs,
2152# which are _not_ populated by the one-time sparse-index feature switch.
2153test_expect_failure 'grep within submodules is not expanded' '
2154 init_repos_as_submodules &&
2155
2156 # do not use ensure_not_expanded() here, because `grep` should be
2157 # run in the superproject, not in "./sparse-index"
2158 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
2159 git grep --cached --recurse-submodules a -- "*/folder1/*" &&
2160 test_region ! index ensure_full_index trace2.txt
2161'
2162
2163# NEEDSWORK: this test is not actually testing the code. The design purpose
2164# of this test is to verify the grep result when the submodules are using a
2165# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
2166# because of the index expansion, we are now grepping the "folder1/a" blob.
2167# Because of the problem stated above 'grep within submodules is not expanded',
2168# we don't have the ideal test environment yet.
2169test_expect_success 'grep sparse directory within submodules' '
2170 init_repos_as_submodules &&
2171
2172 cat >expect <<-\EOF &&
2173 full-checkout/folder1/a:a
2174 sparse-checkout/folder1/a:a
2175 sparse-index/folder1/a:a
2176 EOF
2177 git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
2178 test_cmp actual expect
2179'
2180
2181test_expect_success 'write-tree' '
2182 init_repos &&
2183
2184 test_all_match git write-tree &&
2185
2186 write_script edit-contents <<-\EOF &&
2187 echo text >>"$1"
2188 EOF
2189
2190 # make a change inside the sparse cone
2191 run_on_all ../edit-contents deep/a &&
2192 test_all_match git update-index deep/a &&
2193 test_all_match git write-tree &&
2194 test_all_match git status --porcelain=v2 &&
2195
2196 # make a change outside the sparse cone
2197 run_on_all mkdir -p folder1 &&
2198 run_on_all cp a folder1/a &&
2199 run_on_all ../edit-contents folder1/a &&
2200 test_all_match git update-index folder1/a &&
2201 test_all_match git write-tree &&
2202 test_all_match git status --porcelain=v2 &&
2203
2204 # check that SKIP_WORKTREE files are not materialized
2205 test_path_is_missing sparse-checkout/folder2/a &&
2206 test_path_is_missing sparse-index/folder2/a
2207'
2208
2209test_expect_success 'sparse-index is not expanded: write-tree' '
2210 init_repos &&
2211
2212 ensure_not_expanded write-tree &&
2213
2214 echo "test1" >>sparse-index/a &&
2215 git -C sparse-index update-index a &&
2216 ensure_not_expanded write-tree
2217'
2218
2219test_expect_success 'diff-files with pathspec inside sparse definition' '
2220 init_repos &&
2221
2222 write_script edit-contents <<-\EOF &&
2223 echo text >>"$1"
2224 EOF
2225
2226 run_on_all ../edit-contents deep/a &&
2227
2228 test_all_match git diff-files &&
2229
2230 test_all_match git diff-files -- deep/a &&
2231
2232 # test wildcard
2233 test_all_match git diff-files -- "deep/*"
2234'
2235
2236test_expect_success 'diff-files with pathspec outside sparse definition' '
2237 init_repos &&
2238
2239 test_sparse_match git diff-files -- folder2/a &&
2240
2241 write_script edit-contents <<-\EOF &&
2242 echo text >>"$1"
2243 EOF
2244
2245 # The directory "folder1" is outside the cone of interest
2246 # and will not exist in the sparse checkout repositories.
2247 # Create it as needed, add file "folder1/a" there with
2248 # contents that is different from the staged version.
2249 run_on_all mkdir -p folder1 &&
2250 run_on_all cp a folder1/a &&
2251
2252 run_on_all ../edit-contents folder1/a &&
2253 test_all_match git diff-files &&
2254 test_all_match git diff-files -- folder1/a &&
2255 test_all_match git diff-files -- "folder*/a"
2256'
2257
2258test_expect_success 'sparse index is not expanded: diff-files' '
2259 init_repos &&
2260
2261 write_script edit-contents <<-\EOF &&
2262 echo text >>"$1"
2263 EOF
2264
2265 run_on_all ../edit-contents deep/a &&
2266
2267 ensure_not_expanded diff-files &&
2268 ensure_not_expanded diff-files -- deep/a &&
2269 ensure_not_expanded diff-files -- "deep/*"
2270'
2271
2272test_expect_success 'diff-tree' '
2273 init_repos &&
2274
2275 # Test change inside sparse cone
2276 tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
2277 tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
2278 test_all_match git diff-tree $tree1 $tree2 &&
2279 test_all_match git diff-tree $tree1 $tree2 -- deep/a &&
2280 test_all_match git diff-tree HEAD update-deep &&
2281 test_all_match git diff-tree HEAD update-deep -- deep/a &&
2282
2283 # Test change outside sparse cone
2284 tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
2285 test_all_match git diff-tree $tree1 $tree3 &&
2286 test_all_match git diff-tree $tree1 $tree3 -- folder1/a &&
2287 test_all_match git diff-tree HEAD update-folder1 &&
2288 test_all_match git diff-tree HEAD update-folder1 -- folder1/a &&
2289
2290 # Check that SKIP_WORKTREE files are not materialized
2291 test_path_is_missing sparse-checkout/folder1/a &&
2292 test_path_is_missing sparse-index/folder1/a &&
2293 test_path_is_missing sparse-checkout/folder2/a &&
2294 test_path_is_missing sparse-index/folder2/a
2295'
2296
2297test_expect_success 'sparse-index is not expanded: diff-tree' '
2298 init_repos &&
2299
2300 tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
2301 tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
2302 tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
2303
2304 ensure_not_expanded diff-tree $tree1 $tree2 &&
2305 ensure_not_expanded diff-tree $tree1 $tree2 -- deep/a &&
2306 ensure_not_expanded diff-tree HEAD update-deep &&
2307 ensure_not_expanded diff-tree HEAD update-deep -- deep/a &&
2308 ensure_not_expanded diff-tree $tree1 $tree3 &&
2309 ensure_not_expanded diff-tree $tree1 $tree3 -- folder1/a &&
2310 ensure_not_expanded diff-tree HEAD update-folder1 &&
2311 ensure_not_expanded diff-tree HEAD update-folder1 -- folder1/a
2312'
2313
2314test_expect_success 'worktree' '
2315 init_repos &&
2316
2317 write_script edit-contents <<-\EOF &&
2318 echo text >>"$1"
2319 EOF
2320
2321 for repo in full-checkout sparse-checkout sparse-index
2322 do
2323 worktree=${repo}-wt &&
2324 git -C $repo worktree add ../$worktree &&
2325
2326 # Compare worktree content with "ls"
2327 (cd $repo && ls) >worktree_contents &&
2328 (cd $worktree && ls) >new_worktree_contents &&
2329 test_cmp worktree_contents new_worktree_contents &&
2330
2331 # Compare index content with "ls-files --sparse"
2332 git -C $repo ls-files --sparse >index_contents &&
2333 git -C $worktree ls-files --sparse >new_index_contents &&
2334 test_cmp index_contents new_index_contents &&
2335
2336 git -C $repo worktree remove ../$worktree || return 1
2337 done &&
2338
2339 test_all_match git worktree add .worktrees/hotfix &&
2340 run_on_all ../edit-contents .worktrees/hotfix/deep/a &&
2341 test_all_match test_must_fail git worktree remove .worktrees/hotfix
2342'
2343
2344test_expect_success 'worktree is not expanded' '
2345 init_repos &&
2346
2347 ensure_not_expanded worktree add .worktrees/hotfix &&
2348 ensure_not_expanded worktree remove .worktrees/hotfix
2349'
2350
2351test_expect_success 'check-attr with pathspec inside sparse definition' '
2352 init_repos &&
2353
2354 echo "a -crlf myAttr" >>.gitattributes &&
2355 run_on_all cp ../.gitattributes ./deep &&
2356
2357 test_all_match git check-attr -a -- deep/a &&
2358
2359 test_all_match git add deep/.gitattributes &&
2360 test_all_match git check-attr -a --cached -- deep/a
2361'
2362
2363test_expect_success 'check-attr with pathspec outside sparse definition' '
2364 init_repos &&
2365
2366 echo "a -crlf myAttr" >>.gitattributes &&
2367 run_on_sparse mkdir folder1 &&
2368 run_on_all cp ../.gitattributes ./folder1 &&
2369 run_on_all cp a folder1/a &&
2370
2371 test_all_match git check-attr -a -- folder1/a &&
2372
2373 git -C full-checkout add folder1/.gitattributes &&
2374 test_sparse_match git add --sparse folder1/.gitattributes &&
2375 test_all_match git commit -m "add .gitattributes" &&
2376 test_sparse_match git sparse-checkout reapply &&
2377 test_all_match git check-attr -a --cached -- folder1/a
2378'
2379
2380# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
2381# to an underlying issue in oneway_diff() within diff-lib.c.
2382# 'do_oneway_diff()' is not called as expected for paths that could match
2383# inside of a sparse directory. Specifically, the 'ce_path_match()' function
2384# fails to recognize files inside a sparse directory (e.g., when 'folder1/'
2385# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
2386# proceed with 'do_oneway_diff()' if the pathspec could match inside of a
2387# sparse directory.
2388test_expect_failure 'diff --check with pathspec outside sparse definition' '
2389 init_repos &&
2390
2391 write_script edit-contents <<-\EOF &&
2392 echo "a " >"$1"
2393 EOF
2394
2395 test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
2396
2397 echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
2398 run_on_all mkdir -p folder1 &&
2399 run_on_all cp ../.gitattributes ./folder1 &&
2400 test_all_match git add --sparse folder1/.gitattributes &&
2401 run_on_all ../edit-contents folder1/a &&
2402 test_all_match git add --sparse folder1/a &&
2403
2404 test_sparse_match git sparse-checkout reapply &&
2405 test_all_match test_must_fail git diff --check --cached -- folder1/a
2406'
2407
2408test_expect_success 'sparse-index is not expanded: check-attr' '
2409 init_repos &&
2410
2411 echo "a -crlf myAttr" >>.gitattributes &&
2412 mkdir ./sparse-index/folder1 &&
2413 cp ./sparse-index/a ./sparse-index/folder1/a &&
2414 cp .gitattributes ./sparse-index/deep &&
2415 cp .gitattributes ./sparse-index/folder1 &&
2416
2417 git -C sparse-index add deep/.gitattributes &&
2418 git -C sparse-index add --sparse folder1/.gitattributes &&
2419 ensure_not_expanded check-attr -a --cached -- deep/a &&
2420 ensure_not_expanded check-attr -a --cached -- folder1/a
2421'
2422
2423test_expect_success 'sparse-index is not expanded: git apply' '
2424 init_repos &&
2425
2426 git -C sparse-index checkout base &&
2427 git -C full-checkout diff base..merge-right -- deep >patch-in-sparse &&
2428 git -C full-checkout diff base..merge-right -- folder2 >patch-outside &&
2429
2430 # Apply a patch to a file inside the sparse definition
2431 ensure_not_expanded apply --index --stat ../patch-in-sparse &&
2432
2433 # Apply a patch to a file outside the sparse definition
2434 # Fails when caring about the worktree.
2435 ensure_not_expanded ! apply ../patch-outside &&
2436
2437 # Expands when using --index.
2438 ensure_expanded apply --index ../patch-outside &&
2439
2440 # Does not when index is partially expanded.
2441 git -C sparse-index reset --hard &&
2442 ensure_not_expanded apply --cached ../patch-outside &&
2443
2444 # Try again with a reset and collapsed index.
2445 git -C sparse-index reset --hard &&
2446 git -C sparse-index sparse-checkout reapply &&
2447
2448 # Expands when index is collapsed.
2449 ensure_expanded apply --cached ../patch-outside
2450'
2451
2452test_expect_success 'sparse-index is not expanded: git add -p' '
2453 init_repos &&
2454
2455 # Does not expand when edits are within sparse checkout.
2456 echo "new content" >sparse-index/deep/a &&
2457 echo "new content" >sparse-index/deep/deeper1/a &&
2458 test_write_lines y n >in &&
2459 ensure_not_expanded add -p <in &&
2460 git -C sparse-index reset &&
2461 ensure_not_expanded add -i <in &&
2462
2463 # -p does expand when edits are outside sparse checkout.
2464 mkdir -p sparse-index/folder1 &&
2465 echo "new content" >sparse-index/folder1/a &&
2466 test_write_lines y n y >in &&
2467 ensure_expanded add -p <in &&
2468
2469 # Fully reset the index.
2470 git -C sparse-index reset --hard &&
2471 git -C sparse-index sparse-checkout reapply &&
2472
2473 # -i does expand when edits are outside sparse checkout.
2474 mkdir -p sparse-index/folder1 &&
2475 echo "new content" >sparse-index/folder1/a &&
2476 test_write_lines u 2 3 "" q >in &&
2477 ensure_expanded add -i <in
2478'
2479
2480test_expect_success 'sparse-index is not expanded: checkout -p, reset -p' '
2481 init_repos &&
2482
2483 # Does not expand when edits are within sparse checkout.
2484 echo "new content" >sparse-index/deep/a &&
2485 echo "new content" >sparse-index/deep/deeper1/a &&
2486 git -C sparse-index commit -a -m "inside-changes" &&
2487
2488 test_write_lines y y >in &&
2489 ensure_not_expanded checkout HEAD~1 --patch <in &&
2490
2491 echo "new content" >sparse-index/deep/a &&
2492 echo "new content" >sparse-index/deep/deeper1/a &&
2493 git -C sparse-index add . &&
2494 ensure_not_expanded reset --patch <in &&
2495
2496 # -p does expand when edits are outside sparse checkout.
2497 mkdir -p sparse-index/folder1 &&
2498 echo "new content" >sparse-index/folder1/a &&
2499 git -C sparse-index add --sparse folder1 &&
2500 git -C sparse-index sparse-checkout reapply &&
2501 ensure_expanded reset --patch <in &&
2502
2503 # Fully reset the index.
2504 mkdir -p sparse-index/folder1 &&
2505 echo "new content" >sparse-index/folder1/a &&
2506 git -C sparse-index add --sparse folder1 &&
2507 git -C sparse-index commit -m "folder1 change" &&
2508 git -C sparse-index sparse-checkout reapply &&
2509 ensure_expanded checkout HEAD~1 --patch <in
2510'
2511
2512test_expect_success 'advice.sparseIndexExpanded' '
2513 init_repos &&
2514
2515 git -C sparse-index config --unset advice.sparseIndexExpanded &&
2516 git -C sparse-index sparse-checkout set deep/deeper1 &&
2517 mkdir -p sparse-index/deep/deeper2/deepest &&
2518 touch sparse-index/deep/deeper2/deepest/bogus &&
2519 git -C sparse-index status 2>err &&
2520 grep "The sparse index is expanding to a full index" err &&
2521
2522 git -C sparse-index sparse-checkout disable 2>err &&
2523 test_line_count = 0 err
2524'
2525
2526test_expect_success 'cat-file -p' '
2527 init_repos &&
2528 echo "new content" >>full-checkout/deep/a &&
2529 echo "new content" >>sparse-checkout/deep/a &&
2530 echo "new content" >>sparse-index/deep/a &&
2531 run_on_all git add deep/a &&
2532
2533 test_all_match git cat-file -p :deep/a &&
2534 ensure_not_expanded cat-file -p :deep/a &&
2535 test_all_match git cat-file -p :folder1/a &&
2536 ensure_expanded cat-file -p :folder1/a
2537'
2538
2539test_expect_success 'cat-file --batch' '
2540 init_repos &&
2541 echo "new content" >>full-checkout/deep/a &&
2542 echo "new content" >>sparse-checkout/deep/a &&
2543 echo "new content" >>sparse-index/deep/a &&
2544 run_on_all git add deep/a &&
2545
2546 echo ":deep/a" >in &&
2547 test_all_match git cat-file --batch <in &&
2548 ensure_not_expanded cat-file --batch <in &&
2549
2550 echo ":folder1/a" >in &&
2551 test_all_match git cat-file --batch <in &&
2552 ensure_expanded cat-file --batch <in &&
2553
2554 cat >in <<-\EOF &&
2555 :deep/a
2556 :folder1/a
2557 EOF
2558 test_all_match git cat-file --batch <in &&
2559 ensure_expanded cat-file --batch <in
2560'
2561
2562test_done