Merge branch 'jx/pkt-line-doc-count-fix'
[git] / t / t1091-sparse-checkout-builtin.sh
1 #!/bin/sh
2
3 test_description='sparse checkout builtin tests'
4
5 . ./test-lib.sh
6
7 list_files() {
8         # Do not replace this with 'ls "$1"', as "ls" with BSD-lineage
9         # enables "-A" by default for root and ends up including ".git" and
10         # such in its output. (Note, though, that running the test suite as
11         # root is generally not recommended.)
12         (cd "$1" && printf '%s\n' *)
13 }
14
15 check_files() {
16         list_files "$1" >actual &&
17         shift &&
18         printf "%s\n" $@ >expect &&
19         test_cmp expect actual
20 }
21
22 test_expect_success 'setup' '
23         git init repo &&
24         (
25                 cd repo &&
26                 echo "initial" >a &&
27                 mkdir folder1 folder2 deep &&
28                 mkdir deep/deeper1 deep/deeper2 &&
29                 mkdir deep/deeper1/deepest &&
30                 cp a folder1 &&
31                 cp a folder2 &&
32                 cp a deep &&
33                 cp a deep/deeper1 &&
34                 cp a deep/deeper2 &&
35                 cp a deep/deeper1/deepest &&
36                 git add . &&
37                 git commit -m "initial commit"
38         )
39 '
40
41 test_expect_success 'git sparse-checkout list (empty)' '
42         git -C repo sparse-checkout list >list 2>err &&
43         test_must_be_empty list &&
44         test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
45 '
46
47 test_expect_success 'git sparse-checkout list (populated)' '
48         test_when_finished rm -f repo/.git/info/sparse-checkout &&
49         cat >repo/.git/info/sparse-checkout <<-\EOF &&
50         /folder1/*
51         /deep/
52         **/a
53         !*bin*
54         EOF
55         cp repo/.git/info/sparse-checkout expect &&
56         git -C repo sparse-checkout list >list &&
57         test_cmp expect list
58 '
59
60 test_expect_success 'git sparse-checkout init' '
61         git -C repo sparse-checkout init &&
62         cat >expect <<-\EOF &&
63         /*
64         !/*/
65         EOF
66         test_cmp expect repo/.git/info/sparse-checkout &&
67         test_cmp_config -C repo true core.sparsecheckout &&
68         check_files repo a
69 '
70
71 test_expect_success 'git sparse-checkout list after init' '
72         git -C repo sparse-checkout list >actual &&
73         cat >expect <<-\EOF &&
74         /*
75         !/*/
76         EOF
77         test_cmp expect actual
78 '
79
80 test_expect_success 'init with existing sparse-checkout' '
81         echo "*folder*" >> repo/.git/info/sparse-checkout &&
82         git -C repo sparse-checkout init &&
83         cat >expect <<-\EOF &&
84         /*
85         !/*/
86         *folder*
87         EOF
88         test_cmp expect repo/.git/info/sparse-checkout &&
89         check_files repo a folder1 folder2
90 '
91
92 test_expect_success 'clone --sparse' '
93         git clone --sparse "file://$(pwd)/repo" clone &&
94         git -C clone sparse-checkout list >actual &&
95         cat >expect <<-\EOF &&
96         /*
97         !/*/
98         EOF
99         test_cmp expect actual &&
100         check_files clone a
101 '
102
103 test_expect_success 'set enables config' '
104         git init empty-config &&
105         (
106                 cd empty-config &&
107                 test_commit test file &&
108                 test_path_is_missing .git/config.worktree &&
109                 git sparse-checkout set nothing &&
110                 test_path_is_file .git/config.worktree &&
111                 test_cmp_config true core.sparseCheckout
112         )
113 '
114
115 test_expect_success 'set sparse-checkout using builtin' '
116         git -C repo sparse-checkout set "/*" "!/*/" "*folder*" &&
117         cat >expect <<-\EOF &&
118         /*
119         !/*/
120         *folder*
121         EOF
122         git -C repo sparse-checkout list >actual &&
123         test_cmp expect actual &&
124         test_cmp expect repo/.git/info/sparse-checkout &&
125         check_files repo a folder1 folder2
126 '
127
128 test_expect_success 'set sparse-checkout using --stdin' '
129         cat >expect <<-\EOF &&
130         /*
131         !/*/
132         /folder1/
133         /folder2/
134         EOF
135         git -C repo sparse-checkout set --stdin <expect &&
136         git -C repo sparse-checkout list >actual &&
137         test_cmp expect actual &&
138         test_cmp expect repo/.git/info/sparse-checkout &&
139         check_files repo "a folder1 folder2"
140 '
141
142 test_expect_success 'add to sparse-checkout' '
143         cat repo/.git/info/sparse-checkout >expect &&
144         cat >add <<-\EOF &&
145         pattern1
146         /folder1/
147         pattern2
148         EOF
149         cat add >>expect &&
150         git -C repo sparse-checkout add --stdin <add &&
151         git -C repo sparse-checkout list >actual &&
152         test_cmp expect actual &&
153         test_cmp expect repo/.git/info/sparse-checkout &&
154         check_files repo "a folder1 folder2"
155 '
156
157 test_expect_success 'cone mode: match patterns' '
158         git -C repo config --worktree core.sparseCheckoutCone true &&
159         rm -rf repo/a repo/folder1 repo/folder2 &&
160         git -C repo read-tree -mu HEAD 2>err &&
161         test_i18ngrep ! "disabling cone patterns" err &&
162         git -C repo reset --hard &&
163         check_files repo a folder1 folder2
164 '
165
166 test_expect_success 'cone mode: warn on bad pattern' '
167         test_when_finished mv sparse-checkout repo/.git/info/ &&
168         cp repo/.git/info/sparse-checkout . &&
169         echo "!/deep/deeper/*" >>repo/.git/info/sparse-checkout &&
170         git -C repo read-tree -mu HEAD 2>err &&
171         test_i18ngrep "unrecognized negative pattern" err
172 '
173
174 test_expect_success 'sparse-checkout disable' '
175         test_when_finished rm -rf repo/.git/info/sparse-checkout &&
176         git -C repo sparse-checkout disable &&
177         test_path_is_file repo/.git/info/sparse-checkout &&
178         git -C repo config --list >config &&
179         test_must_fail git config core.sparseCheckout &&
180         check_files repo a deep folder1 folder2
181 '
182
183 test_expect_success 'cone mode: init and set' '
184         git -C repo sparse-checkout init --cone &&
185         git -C repo config --list >config &&
186         test_i18ngrep "core.sparsecheckoutcone=true" config &&
187         list_files repo >dir  &&
188         echo a >expect &&
189         test_cmp expect dir &&
190         git -C repo sparse-checkout set deep/deeper1/deepest/ 2>err &&
191         test_must_be_empty err &&
192         check_files repo a deep &&
193         check_files repo/deep a deeper1 &&
194         check_files repo/deep/deeper1 a deepest &&
195         cat >expect <<-\EOF &&
196         /*
197         !/*/
198         /deep/
199         !/deep/*/
200         /deep/deeper1/
201         !/deep/deeper1/*/
202         /deep/deeper1/deepest/
203         EOF
204         test_cmp expect repo/.git/info/sparse-checkout &&
205         git -C repo sparse-checkout set --stdin 2>err <<-\EOF &&
206         folder1
207         folder2
208         EOF
209         test_must_be_empty err &&
210         check_files repo a folder1 folder2
211 '
212
213 test_expect_success 'cone mode: list' '
214         cat >expect <<-\EOF &&
215         folder1
216         folder2
217         EOF
218         git -C repo sparse-checkout set --stdin <expect &&
219         git -C repo sparse-checkout list >actual 2>err &&
220         test_must_be_empty err &&
221         test_cmp expect actual
222 '
223
224 test_expect_success 'cone mode: set with nested folders' '
225         git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
226         test_line_count = 0 err &&
227         cat >expect <<-\EOF &&
228         /*
229         !/*/
230         /deep/
231         EOF
232         test_cmp repo/.git/info/sparse-checkout expect
233 '
234
235 test_expect_success 'cone mode: add independent path' '
236         git -C repo sparse-checkout set deep/deeper1 &&
237         git -C repo sparse-checkout add folder1 &&
238         cat >expect <<-\EOF &&
239         /*
240         !/*/
241         /deep/
242         !/deep/*/
243         /deep/deeper1/
244         /folder1/
245         EOF
246         test_cmp expect repo/.git/info/sparse-checkout &&
247         check_files repo a deep folder1
248 '
249
250 test_expect_success 'cone mode: add sibling path' '
251         git -C repo sparse-checkout set deep/deeper1 &&
252         git -C repo sparse-checkout add deep/deeper2 &&
253         cat >expect <<-\EOF &&
254         /*
255         !/*/
256         /deep/
257         !/deep/*/
258         /deep/deeper1/
259         /deep/deeper2/
260         EOF
261         test_cmp expect repo/.git/info/sparse-checkout &&
262         check_files repo a deep
263 '
264
265 test_expect_success 'cone mode: add parent path' '
266         git -C repo sparse-checkout set deep/deeper1 folder1 &&
267         git -C repo sparse-checkout add deep &&
268         cat >expect <<-\EOF &&
269         /*
270         !/*/
271         /deep/
272         /folder1/
273         EOF
274         test_cmp expect repo/.git/info/sparse-checkout &&
275         check_files repo a deep folder1
276 '
277
278 test_expect_success 'not-up-to-date does not block rest of sparsification' '
279         test_when_finished git -C repo sparse-checkout disable &&
280         test_when_finished git -C repo reset --hard &&
281         git -C repo sparse-checkout set deep &&
282
283         echo update >repo/deep/deeper2/a &&
284         cp repo/.git/info/sparse-checkout expect &&
285         test_write_lines "!/deep/*/" "/deep/deeper1/" >>expect &&
286
287         git -C repo sparse-checkout set deep/deeper1 2>err &&
288
289         test_i18ngrep "The following paths are not up to date" err &&
290         test_cmp expect repo/.git/info/sparse-checkout &&
291         check_files repo/deep a deeper1 deeper2 &&
292         check_files repo/deep/deeper1 a deepest &&
293         check_files repo/deep/deeper1/deepest a &&
294         check_files repo/deep/deeper2 a
295 '
296
297 test_expect_success 'revert to old sparse-checkout on empty update' '
298         git init empty-test &&
299         (
300                 echo >file &&
301                 git add file &&
302                 git commit -m "test" &&
303                 git sparse-checkout set nothing 2>err &&
304                 test_i18ngrep ! "Sparse checkout leaves no entry on working directory" err &&
305                 test_i18ngrep ! ".git/index.lock" err &&
306                 git sparse-checkout set file
307         )
308 '
309
310 test_expect_success 'fail when lock is taken' '
311         test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
312         touch repo/.git/info/sparse-checkout.lock &&
313         test_must_fail git -C repo sparse-checkout set deep 2>err &&
314         test_i18ngrep "Unable to create .*\.lock" err
315 '
316
317 test_expect_success '.gitignore should not warn about cone mode' '
318         git -C repo config --worktree core.sparseCheckoutCone true &&
319         echo "**/bin/*" >repo/.gitignore &&
320         git -C repo reset --hard 2>err &&
321         test_i18ngrep ! "disabling cone patterns" err
322 '
323
324 test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
325         git clone repo dirty &&
326         echo dirty >dirty/folder1/a &&
327
328         git -C dirty sparse-checkout init 2>err &&
329         test_i18ngrep "warning.*The following paths are not up to date" err &&
330
331         git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
332         test_i18ngrep "warning.*The following paths are not up to date" err &&
333         test_path_is_file dirty/folder1/a &&
334
335         git -C dirty sparse-checkout disable 2>err &&
336         test_must_be_empty err &&
337
338         git -C dirty reset --hard &&
339         git -C dirty sparse-checkout init &&
340         git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
341         test_path_is_missing dirty/folder1/a &&
342         git -C dirty sparse-checkout disable &&
343         test_path_is_file dirty/folder1/a
344 '
345
346 test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged status' '
347         git clone repo unmerged &&
348
349         cat >input <<-EOF &&
350         0 0000000000000000000000000000000000000000      folder1/a
351         100644 $(git -C unmerged rev-parse HEAD:folder1/a) 1    folder1/a
352         EOF
353         git -C unmerged update-index --index-info <input &&
354
355         git -C unmerged sparse-checkout init 2>err &&
356         test_i18ngrep "warning.*The following paths are unmerged" err &&
357
358         git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
359         test_i18ngrep "warning.*The following paths are unmerged" err &&
360         test_path_is_file dirty/folder1/a &&
361
362         git -C unmerged sparse-checkout disable 2>err &&
363         test_i18ngrep "warning.*The following paths are unmerged" err &&
364
365         git -C unmerged reset --hard &&
366         git -C unmerged sparse-checkout init &&
367         git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* &&
368         git -C unmerged sparse-checkout disable
369 '
370
371 test_expect_success 'sparse-checkout reapply' '
372         git clone repo tweak &&
373
374         echo dirty >tweak/deep/deeper2/a &&
375
376         cat >input <<-EOF &&
377         0 0000000000000000000000000000000000000000      folder1/a
378         100644 $(git -C tweak rev-parse HEAD:folder1/a) 1       folder1/a
379         EOF
380         git -C tweak update-index --index-info <input &&
381
382         git -C tweak sparse-checkout init --cone 2>err &&
383         test_i18ngrep "warning.*The following paths are not up to date" err &&
384         test_i18ngrep "warning.*The following paths are unmerged" err &&
385
386         git -C tweak sparse-checkout set folder2 deep/deeper1 2>err &&
387         test_i18ngrep "warning.*The following paths are not up to date" err &&
388         test_i18ngrep "warning.*The following paths are unmerged" err &&
389
390         git -C tweak sparse-checkout reapply 2>err &&
391         test_i18ngrep "warning.*The following paths are not up to date" err &&
392         test_path_is_file tweak/deep/deeper2/a &&
393         test_i18ngrep "warning.*The following paths are unmerged" err &&
394         test_path_is_file tweak/folder1/a &&
395
396         git -C tweak checkout HEAD deep/deeper2/a &&
397         git -C tweak sparse-checkout reapply 2>err &&
398         test_i18ngrep ! "warning.*The following paths are not up to date" err &&
399         test_path_is_missing tweak/deep/deeper2/a &&
400         test_i18ngrep "warning.*The following paths are unmerged" err &&
401         test_path_is_file tweak/folder1/a &&
402
403         git -C tweak add folder1/a &&
404         git -C tweak sparse-checkout reapply 2>err &&
405         test_must_be_empty err &&
406         test_path_is_missing tweak/deep/deeper2/a &&
407         test_path_is_missing tweak/folder1/a &&
408
409         git -C tweak sparse-checkout disable
410 '
411
412 test_expect_success 'cone mode: set with core.ignoreCase=true' '
413         rm repo/.git/info/sparse-checkout &&
414         git -C repo sparse-checkout init --cone &&
415         git -C repo -c core.ignoreCase=true sparse-checkout set folder1 &&
416         cat >expect <<-\EOF &&
417         /*
418         !/*/
419         /folder1/
420         EOF
421         test_cmp expect repo/.git/info/sparse-checkout &&
422         check_files repo a folder1
423 '
424
425 test_expect_success 'interaction with submodules' '
426         git clone repo super &&
427         (
428                 cd super &&
429                 mkdir modules &&
430                 git submodule add ../repo modules/child &&
431                 git add . &&
432                 git commit -m "add submodule" &&
433                 git sparse-checkout init --cone &&
434                 git sparse-checkout set folder1
435         ) &&
436         check_files super a folder1 modules &&
437         check_files super/modules/child a deep folder1 folder2
438 '
439
440 test_expect_success 'different sparse-checkouts with worktrees' '
441         git -C repo worktree add --detach ../worktree &&
442         check_files worktree "a deep folder1 folder2" &&
443         git -C worktree sparse-checkout init --cone &&
444         git -C repo sparse-checkout set folder1 &&
445         git -C worktree sparse-checkout set deep/deeper1 &&
446         check_files repo a folder1 &&
447         check_files worktree a deep
448 '
449
450 test_expect_success 'set using filename keeps file on-disk' '
451         git -C repo sparse-checkout set a deep &&
452         cat >expect <<-\EOF &&
453         /*
454         !/*/
455         /a/
456         /deep/
457         EOF
458         test_cmp expect repo/.git/info/sparse-checkout &&
459         check_files repo a deep
460 '
461
462 check_read_tree_errors () {
463         REPO=$1
464         FILES=$2
465         ERRORS=$3
466         git -C $REPO -c core.sparseCheckoutCone=false read-tree -mu HEAD 2>err &&
467         test_must_be_empty err &&
468         check_files $REPO "$FILES" &&
469         git -C $REPO read-tree -mu HEAD 2>err &&
470         if test -z "$ERRORS"
471         then
472                 test_must_be_empty err
473         else
474                 test_i18ngrep "$ERRORS" err
475         fi &&
476         check_files $REPO $FILES
477 }
478
479 test_expect_success 'pattern-checks: /A/**' '
480         cat >repo/.git/info/sparse-checkout <<-\EOF &&
481         /*
482         !/*/
483         /folder1/**
484         EOF
485         check_read_tree_errors repo "a folder1" "disabling cone pattern matching"
486 '
487
488 test_expect_success 'pattern-checks: /A/**/B/' '
489         cat >repo/.git/info/sparse-checkout <<-\EOF &&
490         /*
491         !/*/
492         /deep/**/deepest
493         EOF
494         check_read_tree_errors repo "a deep" "disabling cone pattern matching" &&
495         check_files repo/deep "deeper1" &&
496         check_files repo/deep/deeper1 "deepest"
497 '
498
499 test_expect_success 'pattern-checks: too short' '
500         cat >repo/.git/info/sparse-checkout <<-\EOF &&
501         /*
502         !/*/
503         /
504         EOF
505         check_read_tree_errors repo "a" "disabling cone pattern matching"
506 '
507 test_expect_success 'pattern-checks: not too short' '
508         cat >repo/.git/info/sparse-checkout <<-\EOF &&
509         /*
510         !/*/
511         /b/
512         EOF
513         git -C repo read-tree -mu HEAD 2>err &&
514         test_must_be_empty err &&
515         check_files repo a
516 '
517
518 test_expect_success 'pattern-checks: trailing "*"' '
519         cat >repo/.git/info/sparse-checkout <<-\EOF &&
520         /*
521         !/*/
522         /a*
523         EOF
524         check_read_tree_errors repo "a" "disabling cone pattern matching"
525 '
526
527 test_expect_success 'pattern-checks: starting "*"' '
528         cat >repo/.git/info/sparse-checkout <<-\EOF &&
529         /*
530         !/*/
531         *eep/
532         EOF
533         check_read_tree_errors repo "a deep" "disabling cone pattern matching"
534 '
535
536 test_expect_success 'pattern-checks: contained glob characters' '
537         for c in "[a]" "\\" "?" "*"
538         do
539                 cat >repo/.git/info/sparse-checkout <<-EOF &&
540                 /*
541                 !/*/
542                 something$c-else/
543                 EOF
544                 check_read_tree_errors repo "a" "disabling cone pattern matching"
545         done
546 '
547
548 test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' '
549         git clone repo escaped &&
550         TREEOID=$(git -C escaped rev-parse HEAD:folder1) &&
551         NEWTREE=$(git -C escaped mktree <<-EOF
552         $(git -C escaped ls-tree HEAD)
553         040000 tree $TREEOID    zbad\\dir
554         040000 tree $TREEOID    zdoes*exist
555         040000 tree $TREEOID    zglob[!a]?
556         EOF
557         ) &&
558         COMMIT=$(git -C escaped commit-tree $NEWTREE -p HEAD) &&
559         git -C escaped reset --hard $COMMIT &&
560         check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
561         git -C escaped sparse-checkout init --cone &&
562         git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
563         cat >expect <<-\EOF &&
564         /*
565         !/*/
566         /zbad\\dir/
567         !/zbad\\dir/*/
568         /zbad\\dir/bogus/
569         /zdoes\*exist/
570         /zdoes\*not\*exist/
571         /zglob\[!a]\?/
572         EOF
573         test_cmp expect escaped/.git/info/sparse-checkout &&
574         check_read_tree_errors escaped "a zbad\\dir zdoes*exist zglob[!a]?" &&
575         git -C escaped ls-tree -d --name-only HEAD >list-expect &&
576         git -C escaped sparse-checkout set --stdin <list-expect &&
577         cat >expect <<-\EOF &&
578         /*
579         !/*/
580         /deep/
581         /folder1/
582         /folder2/
583         /zbad\\dir/
584         /zdoes\*exist/
585         /zglob\[!a]\?/
586         EOF
587         test_cmp expect escaped/.git/info/sparse-checkout &&
588         check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
589         git -C escaped sparse-checkout list >list-actual &&
590         test_cmp list-expect list-actual
591 '
592
593 test_expect_success MINGW 'cone mode replaces backslashes with slashes' '
594         git -C repo sparse-checkout set deep\\deeper1 &&
595         cat >expect <<-\EOF &&
596         /*
597         !/*/
598         /deep/
599         !/deep/*/
600         /deep/deeper1/
601         EOF
602         test_cmp expect repo/.git/info/sparse-checkout &&
603         check_files repo a deep &&
604         check_files repo/deep a deeper1
605 '
606
607 test_done