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