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