setup: split "extensions found" messages into singular and plural
[git] / t / t1092-sparse-checkout-compatibility.sh
1 #!/bin/sh
2
3 test_description='compare full workdir to sparse workdir'
4
5 GIT_TEST_SPLIT_INDEX=0
6 GIT_TEST_SPARSE_INDEX=
7
8 . ./test-lib.sh
9
10 test_expect_success 'setup' '
11         git init initial-repo &&
12         (
13                 GIT_TEST_SPARSE_INDEX=0 &&
14                 cd initial-repo &&
15                 echo a >a &&
16                 echo "after deep" >e &&
17                 echo "after folder1" >g &&
18                 echo "after x" >z &&
19                 mkdir folder1 folder2 deep x &&
20                 mkdir deep/deeper1 deep/deeper2 &&
21                 mkdir deep/deeper1/deepest &&
22                 echo "after deeper1" >deep/e &&
23                 echo "after deepest" >deep/deeper1/e &&
24                 cp a folder1 &&
25                 cp a folder2 &&
26                 cp a x &&
27                 cp a deep &&
28                 cp a deep/deeper1 &&
29                 cp a deep/deeper2 &&
30                 cp a deep/deeper1/deepest &&
31                 cp -r deep/deeper1/deepest deep/deeper2 &&
32                 git add . &&
33                 git commit -m "initial commit" &&
34                 git checkout -b base &&
35                 for dir in folder1 folder2 deep
36                 do
37                         git checkout -b update-$dir &&
38                         echo "updated $dir" >$dir/a &&
39                         git commit -a -m "update $dir" || return 1
40                 done &&
41
42                 git checkout -b rename-base base &&
43                 echo >folder1/larger-content <<-\EOF &&
44                 matching
45                 lines
46                 help
47                 inexact
48                 renames
49                 EOF
50                 cp folder1/larger-content folder2/ &&
51                 cp folder1/larger-content deep/deeper1/ &&
52                 git add . &&
53                 git commit -m "add interesting rename content" &&
54
55                 git checkout -b rename-out-to-out rename-base &&
56                 mv folder1/a folder2/b &&
57                 mv folder1/larger-content folder2/edited-content &&
58                 echo >>folder2/edited-content &&
59                 git add . &&
60                 git commit -m "rename folder1/... to folder2/..." &&
61
62                 git checkout -b rename-out-to-in rename-base &&
63                 mv folder1/a deep/deeper1/b &&
64                 mv folder1/larger-content deep/deeper1/edited-content &&
65                 echo >>deep/deeper1/edited-content &&
66                 git add . &&
67                 git commit -m "rename folder1/... to deep/deeper1/..." &&
68
69                 git checkout -b rename-in-to-out rename-base &&
70                 mv deep/deeper1/a folder1/b &&
71                 mv deep/deeper1/larger-content folder1/edited-content &&
72                 echo >>folder1/edited-content &&
73                 git add . &&
74                 git commit -m "rename deep/deeper1/... to folder1/..." &&
75
76                 git checkout -b deepest base &&
77                 echo "updated deepest" >deep/deeper1/deepest/a &&
78                 git commit -a -m "update deepest" &&
79
80                 git checkout -f base &&
81                 git reset --hard
82         )
83 '
84
85 init_repos () {
86         rm -rf full-checkout sparse-checkout sparse-index &&
87
88         # create repos in initial state
89         cp -r initial-repo full-checkout &&
90         git -C full-checkout reset --hard &&
91
92         cp -r initial-repo sparse-checkout &&
93         git -C sparse-checkout reset --hard &&
94
95         cp -r initial-repo sparse-index &&
96         git -C sparse-index reset --hard &&
97
98         # initialize sparse-checkout definitions
99         git -C sparse-checkout sparse-checkout init --cone &&
100         git -C sparse-checkout sparse-checkout set deep &&
101         git -C sparse-index sparse-checkout init --cone --sparse-index &&
102         test_cmp_config -C sparse-index true index.sparse &&
103         git -C sparse-index sparse-checkout set deep
104 }
105
106 run_on_sparse () {
107         (
108                 cd sparse-checkout &&
109                 "$@" >../sparse-checkout-out 2>../sparse-checkout-err
110         ) &&
111         (
112                 cd sparse-index &&
113                 "$@" >../sparse-index-out 2>../sparse-index-err
114         )
115 }
116
117 run_on_all () {
118         (
119                 cd full-checkout &&
120                 "$@" >../full-checkout-out 2>../full-checkout-err
121         ) &&
122         run_on_sparse "$@"
123 }
124
125 test_all_match () {
126         run_on_all "$@" &&
127         test_cmp full-checkout-out sparse-checkout-out &&
128         test_cmp full-checkout-out sparse-index-out &&
129         test_cmp full-checkout-err sparse-checkout-err &&
130         test_cmp full-checkout-err sparse-index-err
131 }
132
133 test_sparse_match () {
134         run_on_sparse "$@" &&
135         test_cmp sparse-checkout-out sparse-index-out &&
136         test_cmp sparse-checkout-err sparse-index-err
137 }
138
139 test_expect_success 'sparse-index contents' '
140         init_repos &&
141
142         test-tool -C sparse-index read-cache --table >cache &&
143         for dir in folder1 folder2 x
144         do
145                 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
146                 grep "040000 tree $TREE $dir/" cache \
147                         || return 1
148         done &&
149
150         git -C sparse-index sparse-checkout set folder1 &&
151
152         test-tool -C sparse-index read-cache --table >cache &&
153         for dir in deep folder2 x
154         do
155                 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
156                 grep "040000 tree $TREE $dir/" cache \
157                         || return 1
158         done &&
159
160         git -C sparse-index sparse-checkout set deep/deeper1 &&
161
162         test-tool -C sparse-index read-cache --table >cache &&
163         for dir in deep/deeper2 folder1 folder2 x
164         do
165                 TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
166                 grep "040000 tree $TREE $dir/" cache \
167                         || return 1
168         done &&
169
170         # Disabling the sparse-index removes tree entries with full ones
171         git -C sparse-index sparse-checkout init --no-sparse-index &&
172
173         test-tool -C sparse-index read-cache --table >cache &&
174         ! grep "040000 tree" cache &&
175         test_sparse_match test-tool read-cache --table
176 '
177
178 test_expect_success 'expanded in-memory index matches full index' '
179         init_repos &&
180         test_sparse_match test-tool read-cache --expand --table
181 '
182
183 test_expect_success 'status with options' '
184         init_repos &&
185         test_sparse_match ls &&
186         test_all_match git status --porcelain=v2 &&
187         test_all_match git status --porcelain=v2 -z -u &&
188         test_all_match git status --porcelain=v2 -uno &&
189         run_on_all touch README.md &&
190         test_all_match git status --porcelain=v2 &&
191         test_all_match git status --porcelain=v2 -z -u &&
192         test_all_match git status --porcelain=v2 -uno &&
193         test_all_match git add README.md &&
194         test_all_match git status --porcelain=v2 &&
195         test_all_match git status --porcelain=v2 -z -u &&
196         test_all_match git status --porcelain=v2 -uno
197 '
198
199 test_expect_success 'add, commit, checkout' '
200         init_repos &&
201
202         write_script edit-contents <<-\EOF &&
203         echo text >>$1
204         EOF
205         run_on_all ../edit-contents README.md &&
206
207         test_all_match git add README.md &&
208         test_all_match git status --porcelain=v2 &&
209         test_all_match git commit -m "Add README.md" &&
210
211         test_all_match git checkout HEAD~1 &&
212         test_all_match git checkout - &&
213
214         run_on_all ../edit-contents README.md &&
215
216         test_all_match git add -A &&
217         test_all_match git status --porcelain=v2 &&
218         test_all_match git commit -m "Extend README.md" &&
219
220         test_all_match git checkout HEAD~1 &&
221         test_all_match git checkout - &&
222
223         run_on_all ../edit-contents deep/newfile &&
224
225         test_all_match git status --porcelain=v2 -uno &&
226         test_all_match git status --porcelain=v2 &&
227         test_all_match git add . &&
228         test_all_match git status --porcelain=v2 &&
229         test_all_match git commit -m "add deep/newfile" &&
230
231         test_all_match git checkout HEAD~1 &&
232         test_all_match git checkout -
233 '
234
235 test_expect_success 'checkout and reset --hard' '
236         init_repos &&
237
238         test_all_match git checkout update-folder1 &&
239         test_all_match git status --porcelain=v2 &&
240
241         test_all_match git checkout update-deep &&
242         test_all_match git status --porcelain=v2 &&
243
244         test_all_match git checkout -b reset-test &&
245         test_all_match git reset --hard deepest &&
246         test_all_match git reset --hard update-folder1 &&
247         test_all_match git reset --hard update-folder2
248 '
249
250 test_expect_success 'diff --staged' '
251         init_repos &&
252
253         write_script edit-contents <<-\EOF &&
254         echo text >>README.md
255         EOF
256         run_on_all ../edit-contents &&
257
258         test_all_match git diff &&
259         test_all_match git diff --staged &&
260         test_all_match git add README.md &&
261         test_all_match git diff &&
262         test_all_match git diff --staged
263 '
264
265 test_expect_success 'diff with renames' '
266         init_repos &&
267
268         for branch in rename-out-to-out rename-out-to-in rename-in-to-out
269         do
270                 test_all_match git checkout rename-base &&
271                 test_all_match git checkout $branch -- .&&
272                 test_all_match git diff --staged --no-renames &&
273                 test_all_match git diff --staged --find-renames || return 1
274         done
275 '
276
277 test_expect_success 'log with pathspec outside sparse definition' '
278         init_repos &&
279
280         test_all_match git log -- a &&
281         test_all_match git log -- folder1/a &&
282         test_all_match git log -- folder2/a &&
283         test_all_match git log -- deep/a &&
284         test_all_match git log -- deep/deeper1/a &&
285         test_all_match git log -- deep/deeper1/deepest/a &&
286
287         test_all_match git checkout update-folder1 &&
288         test_all_match git log -- folder1/a
289 '
290
291 test_expect_success 'blame with pathspec inside sparse definition' '
292         init_repos &&
293
294         test_all_match git blame a &&
295         test_all_match git blame deep/a &&
296         test_all_match git blame deep/deeper1/a &&
297         test_all_match git blame deep/deeper1/deepest/a
298 '
299
300 # TODO: blame currently does not support blaming files outside of the
301 # sparse definition. It complains that the file doesn't exist locally.
302 test_expect_failure 'blame with pathspec outside sparse definition' '
303         init_repos &&
304
305         test_all_match git blame folder1/a &&
306         test_all_match git blame folder2/a &&
307         test_all_match git blame deep/deeper2/a &&
308         test_all_match git blame deep/deeper2/deepest/a
309 '
310
311 # TODO: reset currently does not behave as expected when in a
312 # sparse-checkout.
313 test_expect_failure 'checkout and reset (mixed)' '
314         init_repos &&
315
316         test_all_match git checkout -b reset-test update-deep &&
317         test_all_match git reset deepest &&
318         test_all_match git reset update-folder1 &&
319         test_all_match git reset update-folder2
320 '
321
322 # Ensure that sparse-index behaves identically to
323 # sparse-checkout with a full index.
324 test_expect_success 'checkout and reset (mixed) [sparse]' '
325         init_repos &&
326
327         test_sparse_match git checkout -b reset-test update-deep &&
328         test_sparse_match git reset deepest &&
329         test_sparse_match git reset update-folder1 &&
330         test_sparse_match git reset update-folder2
331 '
332
333 test_expect_success 'merge' '
334         init_repos &&
335
336         test_all_match git checkout -b merge update-deep &&
337         test_all_match git merge -m "folder1" update-folder1 &&
338         test_all_match git rev-parse HEAD^{tree} &&
339         test_all_match git merge -m "folder2" update-folder2 &&
340         test_all_match git rev-parse HEAD^{tree}
341 '
342
343 test_expect_success 'merge with outside renames' '
344         init_repos &&
345
346         for type in out-to-out out-to-in in-to-out
347         do
348                 test_all_match git reset --hard &&
349                 test_all_match git checkout -f -b merge-$type update-deep &&
350                 test_all_match git merge -m "$type" rename-$type &&
351                 test_all_match git rev-parse HEAD^{tree} || return 1
352         done
353 '
354
355 test_expect_success 'clean' '
356         init_repos &&
357
358         echo bogus >>.gitignore &&
359         run_on_all cp ../.gitignore . &&
360         test_all_match git add .gitignore &&
361         test_all_match git commit -m "ignore bogus files" &&
362
363         run_on_sparse mkdir folder1 &&
364         run_on_all touch folder1/bogus &&
365
366         test_all_match git status --porcelain=v2 &&
367         test_all_match git clean -f &&
368         test_all_match git status --porcelain=v2 &&
369         test_sparse_match ls &&
370         test_sparse_match ls folder1 &&
371
372         test_all_match git clean -xf &&
373         test_all_match git status --porcelain=v2 &&
374         test_sparse_match ls &&
375         test_sparse_match ls folder1 &&
376
377         test_all_match git clean -xdf &&
378         test_all_match git status --porcelain=v2 &&
379         test_sparse_match ls &&
380         test_sparse_match ls folder1 &&
381
382         test_sparse_match test_path_is_dir folder1
383 '
384
385 test_expect_success 'submodule handling' '
386         init_repos &&
387
388         test_all_match mkdir modules &&
389         test_all_match touch modules/a &&
390         test_all_match git add modules &&
391         test_all_match git commit -m "add modules directory" &&
392
393         run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
394         test_all_match git commit -m "add submodule" &&
395
396         # having a submodule prevents "modules" from collapse
397         test-tool -C sparse-index read-cache --table >cache &&
398         grep "100644 blob .*    modules/a" cache &&
399         grep "160000 commit $(git -C initial-repo rev-parse HEAD)       modules/sub" cache
400 '
401
402 test_expect_success 'sparse-index is expanded and converted back' '
403         init_repos &&
404
405         GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
406                 git -C sparse-index -c core.fsmonitor="" reset --hard &&
407         test_region index convert_to_sparse trace2.txt &&
408         test_region index ensure_full_index trace2.txt &&
409
410         rm trace2.txt &&
411         GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
412                 git -C sparse-index -c core.fsmonitor="" status -uno &&
413         test_region index ensure_full_index trace2.txt
414 '
415
416 test_done