1#!/bin/sh
2
3test_description='git add in sparse checked out working trees'
4
5. ./test-lib.sh
6
7SPARSE_ENTRY_BLOB=""
8
9# Optionally take a printf format string to write to the sparse_entry file
10setup_sparse_entry () {
11	# 'sparse_entry' might already be in the index with the skip-worktree
12	# bit set. Remove it so that the subsequent git add can update it.
13	git update-index --force-remove sparse_entry &&
14	if test $# -eq 1
15	then
16		printf "$1" >sparse_entry
17	else
18		>sparse_entry
19	fi &&
20	git add sparse_entry &&
21	git update-index --skip-worktree sparse_entry &&
22	git commit --allow-empty -m "ensure sparse_entry exists at HEAD" &&
23	SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry)
24}
25
26test_sparse_entry_unchanged () {
27	echo "100644 $SPARSE_ENTRY_BLOB 0	sparse_entry" >expected &&
28	git ls-files --stage sparse_entry >actual &&
29	test_cmp expected actual
30}
31
32setup_gitignore () {
33	test_when_finished rm -f .gitignore &&
34	cat >.gitignore <<-EOF
35	*
36	!/sparse_entry
37	EOF
38}
39
40test_sparse_entry_unstaged () {
41	git diff --staged -- sparse_entry >diff &&
42	test_must_be_empty diff
43}
44
45test_expect_success 'setup' "
46	cat >sparse_error_header <<-EOF &&
47	The following paths and/or pathspecs matched paths that exist
48	outside of your sparse-checkout definition, so will not be
49	updated in the index:
50	EOF
51
52	cat >sparse_hint <<-EOF &&
53	hint: If you intend to update such entries, try one of the following:
54	hint: * Use the --sparse option.
55	hint: * Disable or modify the sparsity rules.
56	hint: Disable this message with \"git config advice.updateSparsePath false\"
57	EOF
58
59	echo sparse_entry | cat sparse_error_header - >sparse_entry_error &&
60	cat sparse_entry_error sparse_hint >error_and_hint
61"
62
63test_expect_success 'git add does not remove sparse entries' '
64	setup_sparse_entry &&
65	rm sparse_entry &&
66	test_must_fail git add sparse_entry 2>stderr &&
67	test_sparse_entry_unstaged &&
68	test_cmp error_and_hint stderr &&
69	test_sparse_entry_unchanged
70'
71
72test_expect_success 'git add -A does not remove sparse entries' '
73	setup_sparse_entry &&
74	rm sparse_entry &&
75	setup_gitignore &&
76	git add -A 2>stderr &&
77	test_must_be_empty stderr &&
78	test_sparse_entry_unchanged
79'
80
81test_expect_success 'git add . does not remove sparse entries' '
82	setup_sparse_entry &&
83	rm sparse_entry &&
84	setup_gitignore &&
85	test_must_fail git add . 2>stderr &&
86	test_sparse_entry_unstaged &&
87
88	cat sparse_error_header >expect &&
89	echo . >>expect &&
90	cat sparse_hint >>expect &&
91
92	test_cmp expect stderr &&
93	test_sparse_entry_unchanged
94'
95
96for opt in "" -f -u --ignore-removal --dry-run
97do
98	test_expect_success "git add${opt:+ $opt} does not update sparse entries" '
99		setup_sparse_entry &&
100		echo modified >sparse_entry &&
101		test_must_fail git add $opt sparse_entry 2>stderr &&
102		test_sparse_entry_unstaged &&
103		test_cmp error_and_hint stderr &&
104		test_sparse_entry_unchanged
105	'
106done
107
108test_expect_success 'git add --refresh does not update sparse entries' '
109	setup_sparse_entry &&
110	git ls-files --debug sparse_entry | grep mtime >before &&
111	test-tool chmtime -60 sparse_entry &&
112	test_must_fail git add --refresh sparse_entry 2>stderr &&
113	test_sparse_entry_unstaged &&
114	test_cmp error_and_hint stderr &&
115	git ls-files --debug sparse_entry | grep mtime >after &&
116	test_cmp before after
117'
118
119test_expect_success 'git add --chmod does not update sparse entries' '
120	setup_sparse_entry &&
121	test_must_fail git add --chmod=+x sparse_entry 2>stderr &&
122	test_sparse_entry_unstaged &&
123	test_cmp error_and_hint stderr &&
124	test_sparse_entry_unchanged &&
125	! test -x sparse_entry
126'
127
128test_expect_success 'git add --renormalize does not update sparse entries' '
129	test_config core.autocrlf false &&
130	setup_sparse_entry "LINEONE\r\nLINETWO\r\n" &&
131	echo "sparse_entry text=auto" >.gitattributes &&
132	test_must_fail git add --renormalize sparse_entry 2>stderr &&
133	test_sparse_entry_unstaged &&
134	test_cmp error_and_hint stderr &&
135	test_sparse_entry_unchanged
136'
137
138test_expect_success 'git add --dry-run --ignore-missing warn on sparse path' '
139	setup_sparse_entry &&
140	rm sparse_entry &&
141	test_must_fail git add --dry-run --ignore-missing sparse_entry 2>stderr &&
142	test_sparse_entry_unstaged &&
143	test_cmp error_and_hint stderr &&
144	test_sparse_entry_unchanged
145'
146
147test_expect_success 'do not advice about sparse entries when they do not match the pathspec' '
148	setup_sparse_entry &&
149	test_must_fail git add nonexistent 2>stderr &&
150	grep "fatal: pathspec .nonexistent. did not match any files" stderr &&
151	! grep -F -f sparse_error_header stderr
152'
153
154test_expect_success 'do not warn when pathspec matches dense entries' '
155	setup_sparse_entry &&
156	echo modified >sparse_entry &&
157	>dense_entry &&
158	git add "*_entry" 2>stderr &&
159	test_must_be_empty stderr &&
160	test_sparse_entry_unchanged &&
161	git ls-files --error-unmatch dense_entry
162'
163
164test_expect_success 'git add fails outside of sparse-checkout definition' '
165	test_when_finished git sparse-checkout disable &&
166	test_commit a &&
167	git sparse-checkout init &&
168	git sparse-checkout set a &&
169	echo >>sparse_entry &&
170
171	git update-index --no-skip-worktree sparse_entry &&
172	test_must_fail git add sparse_entry &&
173	test_sparse_entry_unstaged &&
174
175	test_must_fail git add --chmod=+x sparse_entry &&
176	test_sparse_entry_unstaged &&
177
178	test_must_fail git add --renormalize sparse_entry &&
179	test_sparse_entry_unstaged &&
180
181	# Avoid munging CRLFs to avoid an error message
182	git -c core.autocrlf=input add --sparse sparse_entry 2>stderr &&
183	test_must_be_empty stderr &&
184	test-tool read-cache --table >actual &&
185	grep "^100644 blob.*sparse_entry\$" actual &&
186
187	git add --sparse --chmod=+x sparse_entry 2>stderr &&
188	test_must_be_empty stderr &&
189	test-tool read-cache --table >actual &&
190	grep "^100755 blob.*sparse_entry\$" actual &&
191
192	git reset &&
193
194	# This will print a message over stderr on Windows.
195	git add --sparse --renormalize sparse_entry &&
196	git status --porcelain >actual &&
197	grep "^M  sparse_entry\$" actual
198'
199
200test_expect_success 'add obeys advice.updateSparsePath' '
201	setup_sparse_entry &&
202	test_must_fail git -c advice.updateSparsePath=false add sparse_entry 2>stderr &&
203	test_sparse_entry_unstaged &&
204	test_cmp sparse_entry_error stderr
205
206'
207
208test_expect_success 'add allows sparse entries with --sparse' '
209	git sparse-checkout set a &&
210	echo modified >sparse_entry &&
211	test_must_fail git add sparse_entry &&
212	test_sparse_entry_unchanged &&
213	git add --sparse sparse_entry 2>stderr &&
214	test_must_be_empty stderr
215'
216
217test_expect_success 'can add files from non-sparse dir' '
218	git sparse-checkout set w !/x y/ &&
219	mkdir -p w x/y &&
220	touch w/f x/y/f &&
221	git add w/f x/y/f 2>stderr &&
222	test_must_be_empty stderr
223'
224
225test_expect_success 'refuse to add non-skip-worktree file from sparse dir' '
226	git sparse-checkout set !/x y/ !x/y/z &&
227	mkdir -p x/y/z &&
228	touch x/y/z/f &&
229	test_must_fail git add x/y/z/f 2>stderr &&
230	echo x/y/z/f | cat sparse_error_header - sparse_hint >expect &&
231	test_cmp expect stderr
232'
233
234test_done
235