Merge branch 'ab/config-based-hooks-base' into seen
[git] / t / t7519-status-fsmonitor.sh
1 #!/bin/sh
2
3 test_description='git status with file system watcher'
4
5 . ./test-lib.sh
6
7 # Note, after "git reset --hard HEAD" no extensions exist other than 'TREE'
8 # "git update-index --fsmonitor" can be used to get the extension written
9 # before testing the results.
10
11 clean_repo () {
12         git reset --hard HEAD &&
13         git clean -fd
14 }
15
16 dirty_repo () {
17         : >untracked &&
18         : >dir1/untracked &&
19         : >dir2/untracked &&
20         echo 1 >modified &&
21         echo 2 >dir1/modified &&
22         echo 3 >dir2/modified &&
23         echo 4 >new &&
24         echo 5 >dir1/new &&
25         echo 6 >dir2/new
26 }
27
28 write_integration_script () {
29         write_script .git/hooks/fsmonitor-test<<-\EOF
30         if test "$#" -ne 2
31         then
32                 echo "$0: exactly 2 arguments expected"
33                 exit 2
34         fi
35         if test "$1" != 2
36         then
37                 echo "Unsupported core.fsmonitor hook version." >&2
38                 exit 1
39         fi
40         printf "last_update_token\0"
41         printf "untracked\0"
42         printf "dir1/untracked\0"
43         printf "dir2/untracked\0"
44         printf "modified\0"
45         printf "dir1/modified\0"
46         printf "dir2/modified\0"
47         printf "new\0"
48         printf "dir1/new\0"
49         printf "dir2/new\0"
50         EOF
51 }
52
53 test_lazy_prereq UNTRACKED_CACHE '
54         { git update-index --test-untracked-cache; ret=$?; } &&
55         test $ret -ne 1
56 '
57
58 test_expect_success 'setup' '
59         mkdir -p .git/hooks &&
60         : >tracked &&
61         : >modified &&
62         mkdir dir1 &&
63         : >dir1/tracked &&
64         : >dir1/modified &&
65         mkdir dir2 &&
66         : >dir2/tracked &&
67         : >dir2/modified &&
68         git -c core.fsmonitor= add . &&
69         git -c core.fsmonitor= commit -m initial &&
70         git config core.fsmonitor .git/hooks/fsmonitor-test &&
71         cat >.gitignore <<-\EOF
72         .gitignore
73         expect*
74         actual*
75         marker*
76         trace2*
77         EOF
78 '
79
80 # test that the fsmonitor extension is off by default
81 test_expect_success 'fsmonitor extension is off by default' '
82         test-tool dump-fsmonitor >actual &&
83         grep "^no fsmonitor" actual
84 '
85
86 # test that "update-index --fsmonitor" adds the fsmonitor extension
87 test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' '
88         git update-index --fsmonitor &&
89         test-tool dump-fsmonitor >actual &&
90         grep "^fsmonitor last update" actual
91 '
92
93 # test that "update-index --no-fsmonitor" removes the fsmonitor extension
94 test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' '
95         git update-index --no-fsmonitor &&
96         test-tool dump-fsmonitor >actual &&
97         grep "^no fsmonitor" actual
98 '
99
100 cat >expect <<EOF &&
101 h dir1/modified
102 H dir1/tracked
103 h dir2/modified
104 H dir2/tracked
105 h modified
106 H tracked
107 EOF
108
109 # test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
110 test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
111         write_script .git/hooks/fsmonitor-test<<-\EOF &&
112                 printf "last_update_token\0"
113         EOF
114         git update-index --fsmonitor &&
115         git update-index --fsmonitor-valid dir1/modified &&
116         git update-index --fsmonitor-valid dir2/modified &&
117         git update-index --fsmonitor-valid modified &&
118         git ls-files -f >actual &&
119         test_cmp expect actual
120 '
121
122 cat >expect <<EOF &&
123 H dir1/modified
124 H dir1/tracked
125 H dir2/modified
126 H dir2/tracked
127 H modified
128 H tracked
129 EOF
130
131 # test that "update-index --no-fsmonitor-valid" clears the fsmonitor valid bit
132 test_expect_success 'update-index --no-fsmonitor-valid" clears the fsmonitor valid bit' '
133         git update-index --no-fsmonitor-valid dir1/modified &&
134         git update-index --no-fsmonitor-valid dir2/modified &&
135         git update-index --no-fsmonitor-valid modified &&
136         git ls-files -f >actual &&
137         test_cmp expect actual
138 '
139
140 cat >expect <<EOF &&
141 H dir1/modified
142 H dir1/tracked
143 H dir2/modified
144 H dir2/tracked
145 H modified
146 H tracked
147 EOF
148
149 # test that all files returned by the script get flagged as invalid
150 test_expect_success 'all files returned by integration script get flagged as invalid' '
151         write_integration_script &&
152         dirty_repo &&
153         git update-index --fsmonitor &&
154         git ls-files -f >actual &&
155         test_cmp expect actual
156 '
157
158 cat >expect <<EOF &&
159 H dir1/modified
160 h dir1/new
161 H dir1/tracked
162 H dir2/modified
163 h dir2/new
164 H dir2/tracked
165 H modified
166 h new
167 H tracked
168 EOF
169
170 # test that newly added files are marked valid
171 test_expect_success 'newly added files are marked valid' '
172         write_script .git/hooks/fsmonitor-test<<-\EOF &&
173                 printf "last_update_token\0"
174         EOF
175         git add new &&
176         git add dir1/new &&
177         git add dir2/new &&
178         git ls-files -f >actual &&
179         test_cmp expect actual
180 '
181
182 cat >expect <<EOF &&
183 H dir1/modified
184 h dir1/new
185 h dir1/tracked
186 H dir2/modified
187 h dir2/new
188 h dir2/tracked
189 H modified
190 h new
191 h tracked
192 EOF
193
194 # test that all unmodified files get marked valid
195 test_expect_success 'all unmodified files get marked valid' '
196         # modified files result in update-index returning 1
197         test_must_fail git update-index --refresh --force-write-index &&
198         git ls-files -f >actual &&
199         test_cmp expect actual
200 '
201
202 cat >expect <<EOF &&
203 H dir1/modified
204 h dir1/tracked
205 h dir2/modified
206 h dir2/tracked
207 h modified
208 h tracked
209 EOF
210
211 # test that *only* files returned by the integration script get flagged as invalid
212 test_expect_success '*only* files returned by the integration script get flagged as invalid' '
213         write_script .git/hooks/fsmonitor-test<<-\EOF &&
214         printf "last_update_token\0"
215         printf "dir1/modified\0"
216         EOF
217         clean_repo &&
218         git update-index --refresh --force-write-index &&
219         echo 1 >modified &&
220         echo 2 >dir1/modified &&
221         echo 3 >dir2/modified &&
222         test_must_fail git update-index --refresh --force-write-index &&
223         git ls-files -f >actual &&
224         test_cmp expect actual
225 '
226
227 # Ensure commands that call refresh_index() to move the index back in time
228 # properly invalidate the fsmonitor cache
229 test_expect_success 'refresh_index() invalidates fsmonitor cache' '
230         clean_repo &&
231         dirty_repo &&
232         write_integration_script &&
233         git add . &&
234         write_script .git/hooks/fsmonitor-test<<-\EOF &&
235         EOF
236         git commit -m "to reset" &&
237         git reset HEAD~1 &&
238         git status >actual &&
239         git -c core.fsmonitor= status >expect &&
240         test_cmp expect actual
241 '
242
243 # test fsmonitor with and without preloadIndex
244 preload_values="false true"
245 for preload_val in $preload_values
246 do
247         test_expect_success "setup preloadIndex to $preload_val" '
248                 git config core.preloadIndex $preload_val &&
249                 if test $preload_val = true
250                 then
251                         GIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX
252                 else
253                         sane_unset GIT_TEST_PRELOAD_INDEX
254                 fi
255         '
256
257         # test fsmonitor with and without the untracked cache (if available)
258         uc_values="false"
259         test_have_prereq UNTRACKED_CACHE && uc_values="false true"
260         for uc_val in $uc_values
261         do
262                 test_expect_success "setup untracked cache to $uc_val" '
263                         git config core.untrackedcache $uc_val
264                 '
265
266                 # Status is well tested elsewhere so we'll just ensure that the results are
267                 # the same when using core.fsmonitor.
268                 test_expect_success 'compare status with and without fsmonitor' '
269                         write_integration_script &&
270                         clean_repo &&
271                         dirty_repo &&
272                         git add new &&
273                         git add dir1/new &&
274                         git add dir2/new &&
275                         git status >actual &&
276                         git -c core.fsmonitor= status >expect &&
277                         test_cmp expect actual
278                 '
279
280                 # Make sure it's actually skipping the check for modified and untracked
281                 # (if enabled) files unless it is told about them.
282                 test_expect_success "status doesn't detect unreported modifications" '
283                         write_script .git/hooks/fsmonitor-test<<-\EOF &&
284                         printf "last_update_token\0"
285                         :>marker
286                         EOF
287                         clean_repo &&
288                         git status &&
289                         test_path_is_file marker &&
290                         dirty_repo &&
291                         rm -f marker &&
292                         git status >actual &&
293                         test_path_is_file marker &&
294                         test_i18ngrep ! "Changes not staged for commit:" actual &&
295                         if test $uc_val = true
296                         then
297                                 test_i18ngrep ! "Untracked files:" actual
298                         fi &&
299                         if test $uc_val = false
300                         then
301                                 test_i18ngrep "Untracked files:" actual
302                         fi &&
303                         rm -f marker
304                 '
305         done
306 done
307
308 # test that splitting the index doesn't interfere
309 test_expect_success 'splitting the index results in the same state' '
310         write_integration_script &&
311         dirty_repo &&
312         git update-index --fsmonitor  &&
313         git ls-files -f >expect &&
314         test-tool dump-fsmonitor >&2 && echo &&
315         git update-index --fsmonitor --split-index &&
316         test-tool dump-fsmonitor >&2 && echo &&
317         git ls-files -f >actual &&
318         test_cmp expect actual
319 '
320
321 test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR' '
322         test_create_repo dot-git &&
323         (
324                 cd dot-git &&
325                 mkdir -p .git/hooks &&
326                 : >tracked &&
327                 : >modified &&
328                 mkdir dir1 &&
329                 : >dir1/tracked &&
330                 : >dir1/modified &&
331                 mkdir dir2 &&
332                 : >dir2/tracked &&
333                 : >dir2/modified &&
334                 write_integration_script &&
335                 git config core.fsmonitor .git/hooks/fsmonitor-test &&
336                 git update-index --untracked-cache &&
337                 git update-index --fsmonitor &&
338                 GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-before" \
339                 git status &&
340                 test-tool dump-untracked-cache >../before
341         ) &&
342         cat >>dot-git/.git/hooks/fsmonitor-test <<-\EOF &&
343         printf ".git\0"
344         printf ".git/index\0"
345         printf "dir1/.git\0"
346         printf "dir1/.git/index\0"
347         EOF
348         (
349                 cd dot-git &&
350                 GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-after" \
351                 git status &&
352                 test-tool dump-untracked-cache >../after
353         ) &&
354         grep "directory-invalidation" trace-before | cut -d"|" -f 9 >>before &&
355         grep "directory-invalidation" trace-after  | cut -d"|" -f 9 >>after &&
356         # UNTR extension unchanged, dir invalidation count unchanged
357         test_cmp before after
358 '
359
360 test_expect_success 'discard_index() also discards fsmonitor info' '
361         test_config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-all" &&
362         test_might_fail git update-index --refresh &&
363         test-tool read-cache-again --count=2 tracked >actual &&
364         printf "tracked is%s up to date\n" "" " not" >expect &&
365         test_cmp expect actual
366 '
367
368 # Test unstaging entries that:
369 #  - Are not flagged with CE_FSMONITOR_VALID
370 #  - Have a position in the index >= the number of entries present in the index
371 #    after unstaging.
372 test_expect_success 'status succeeds after staging/unstaging' '
373         test_create_repo fsmonitor-stage-unstage &&
374         (
375                 cd fsmonitor-stage-unstage &&
376                 test_commit initial &&
377                 git update-index --fsmonitor &&
378                 removed=$(test_seq 1 100 | sed "s/^/z/") &&
379                 touch $removed &&
380                 git add $removed &&
381                 git config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-env" &&
382                 FSMONITOR_LIST="$removed" git restore -S $removed &&
383                 FSMONITOR_LIST="$removed" git status
384         )
385 '
386
387 # Usage:
388 # check_sparse_index_behavior [!]
389 # If "!" is supplied, then we verify that we do not call ensure_full_index
390 # during a call to 'git status'. Otherwise, we verify that we _do_ call it.
391 check_sparse_index_behavior () {
392         git status --porcelain=v2 >expect &&
393         git sparse-checkout init --cone --sparse-index &&
394         git sparse-checkout set dir1 dir2 &&
395         GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
396                 git status --porcelain=v2 >actual &&
397         test_region $1 index ensure_full_index trace2.txt &&
398         test_cmp expect actual &&
399         rm trace2.txt &&
400         git sparse-checkout disable
401 }
402
403 test_expect_success 'status succeeds with sparse index' '
404         git reset --hard &&
405
406         test_config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-all" &&
407         check_sparse_index_behavior ! &&
408
409         write_script .git/hooks/fsmonitor-test<<-\EOF &&
410                 printf "last_update_token\0"
411         EOF
412         git config core.fsmonitor .git/hooks/fsmonitor-test &&
413         check_sparse_index_behavior ! &&
414
415         write_script .git/hooks/fsmonitor-test<<-\EOF &&
416                 printf "last_update_token\0"
417                 printf "dir1/modified\0"
418         EOF
419         check_sparse_index_behavior ! &&
420
421         cp -r dir1 dir1a &&
422         git add dir1a &&
423         git commit -m "add dir1a" &&
424
425         # This one modifies outside the sparse-checkout definition
426         # and hence we expect to expand the sparse-index.
427         write_script .git/hooks/fsmonitor-test<<-\EOF &&
428                 printf "last_update_token\0"
429                 printf "dir1a/modified\0"
430         EOF
431         check_sparse_index_behavior
432 '
433
434 test_done