Raw
1 #!/bin/sh
2
3 test_description='working-tree-encoding conversion via gitattributes'
4
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8 TEST_CREATE_REPO_NO_TEMPLATE=1
9 . ./test-lib.sh
10 . "$TEST_DIRECTORY/lib-encoding.sh"
11
12 GIT_TRACE_WORKING_TREE_ENCODING=1 && export GIT_TRACE_WORKING_TREE_ENCODING
13
14 if ! test_have_prereq ICONV
15 then
16 skip_all='skipping working tree encoding tests; iconv not available'
17 test_done
18 fi
19
20 test_expect_success 'setup test files' '
21 git config core.eol lf &&
22
23 text="hallo there!\ncan you read me?" &&
24 echo "*.utf16 text working-tree-encoding=utf-16" >.gitattributes &&
25 echo "*.utf16lebom text working-tree-encoding=UTF-16LE-BOM" >>.gitattributes &&
26 printf "$text" >test.utf8.raw &&
27 printf "$text" | write_utf16 >test.utf16.raw &&
28 printf "$text" | write_utf32 >test.utf32.raw &&
29 printf "\377\376" >test.utf16lebom.raw &&
30 printf "$text" | iconv -f UTF-8 -t UTF-16LE >>test.utf16lebom.raw &&
31
32 # Line ending tests
33 printf "one\ntwo\nthree\n" >lf.utf8.raw &&
34 printf "one\r\ntwo\r\nthree\r\n" >crlf.utf8.raw &&
35
36 # BOM tests
37 printf "\0a\0b\0c" >nobom.utf16be.raw &&
38 printf "a\0b\0c\0" >nobom.utf16le.raw &&
39 printf "\376\377\0a\0b\0c" >bebom.utf16be.raw &&
40 printf "\377\376a\0b\0c\0" >lebom.utf16le.raw &&
41 printf "\0\0\0a\0\0\0b\0\0\0c" >nobom.utf32be.raw &&
42 printf "a\0\0\0b\0\0\0c\0\0\0" >nobom.utf32le.raw &&
43 printf "\0\0\376\377\0\0\0a\0\0\0b\0\0\0c" >bebom.utf32be.raw &&
44 printf "\377\376\0\0a\0\0\0b\0\0\0c\0\0\0" >lebom.utf32le.raw &&
45
46 # Add only UTF-16 file, we will add the UTF-32 file later
47 cp test.utf16.raw test.utf16 &&
48 cp test.utf32.raw test.utf32 &&
49 cp test.utf16lebom.raw test.utf16lebom &&
50 git add .gitattributes test.utf16 test.utf16lebom &&
51 git commit -m initial
52 '
53
54 test_expect_success 'ensure UTF-8 is stored in Git' '
55 test_when_finished "rm -f test.utf16.git" &&
56
57 git cat-file -p :test.utf16 >test.utf16.git &&
58 test_cmp_bin test.utf8.raw test.utf16.git
59 '
60
61 test_expect_success 're-encode to UTF-16 on checkout' '
62 test_when_finished "rm -f test.utf16.raw" &&
63
64 rm test.utf16 &&
65 git checkout test.utf16 &&
66 test_cmp_bin test.utf16.raw test.utf16
67 '
68
69 test_expect_success 're-encode to UTF-16-LE-BOM on checkout' '
70 rm test.utf16lebom &&
71 git checkout test.utf16lebom &&
72 test_cmp_bin test.utf16lebom.raw test.utf16lebom
73 '
74
75 test_expect_success 'check $GIT_DIR/info/attributes support' '
76 test_when_finished "rm -f test.utf32.git" &&
77 test_when_finished "git reset --hard HEAD" &&
78
79 mkdir .git/info &&
80 echo "*.utf32 text working-tree-encoding=utf-32" >.git/info/attributes &&
81 git add test.utf32 &&
82
83 git cat-file -p :test.utf32 >test.utf32.git &&
84 test_cmp_bin test.utf8.raw test.utf32.git
85 '
86
87 for i in 16 32
88 do
89 test_expect_success "check prohibited UTF-${i} BOM" '
90 test_when_finished "git reset --hard HEAD" &&
91
92 echo "*.utf${i}be text working-tree-encoding=utf-${i}be" >>.gitattributes &&
93 echo "*.utf${i}le text working-tree-encoding=utf-${i}LE" >>.gitattributes &&
94
95 # Here we add a UTF-16 (resp. UTF-32) files with BOM (big/little-endian)
96 # but we tell Git to treat it as UTF-16BE/UTF-16LE (resp. UTF-32).
97 # In these cases the BOM is prohibited.
98 cp bebom.utf${i}be.raw bebom.utf${i}be &&
99 test_must_fail git add bebom.utf${i}be 2>err.out &&
100 test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
101 test_grep "use UTF-${i} as working-tree-encoding" err.out &&
102
103 cp lebom.utf${i}le.raw lebom.utf${i}be &&
104 test_must_fail git add lebom.utf${i}be 2>err.out &&
105 test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
106 test_grep "use UTF-${i} as working-tree-encoding" err.out &&
107
108 cp bebom.utf${i}be.raw bebom.utf${i}le &&
109 test_must_fail git add bebom.utf${i}le 2>err.out &&
110 test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
111 test_grep "use UTF-${i} as working-tree-encoding" err.out &&
112
113 cp lebom.utf${i}le.raw lebom.utf${i}le &&
114 test_must_fail git add lebom.utf${i}le 2>err.out &&
115 test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
116 test_grep "use UTF-${i} as working-tree-encoding" err.out
117 '
118
119 test_expect_success "check required UTF-${i} BOM" '
120 test_when_finished "git reset --hard HEAD" &&
121
122 echo "*.utf${i} text working-tree-encoding=utf-${i}" >>.gitattributes &&
123
124 cp nobom.utf${i}be.raw nobom.utf${i} &&
125 test_must_fail git add nobom.utf${i} 2>err.out &&
126 test_grep "fatal: BOM is required .* utf-${i}" err.out &&
127 test_grep "use UTF-${i}BE or UTF-${i}LE" err.out &&
128
129 cp nobom.utf${i}le.raw nobom.utf${i} &&
130 test_must_fail git add nobom.utf${i} 2>err.out &&
131 test_grep "fatal: BOM is required .* utf-${i}" err.out &&
132 test_grep "use UTF-${i}BE or UTF-${i}LE" err.out
133 '
134
135 test_expect_success "eol conversion for UTF-${i} encoded files on checkout" '
136 test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
137 test_when_finished "git reset --hard HEAD^" &&
138
139 write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
140 write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
141 cp crlf.utf${i}.raw eol.utf${i} &&
142
143 cat >expectIndexLF <<-EOF &&
144 i/lf w/-text attr/text eol.utf${i}
145 EOF
146
147 git add eol.utf${i} &&
148 git commit -m eol &&
149
150 # UTF-${i} with CRLF (Windows line endings)
151 rm eol.utf${i} &&
152 git -c core.eol=crlf checkout eol.utf${i} &&
153 test_cmp_bin crlf.utf${i}.raw eol.utf${i} &&
154
155 # Although the file has CRLF in the working tree,
156 # ensure LF in the index
157 git ls-files --eol eol.utf${i} >actual &&
158 test_cmp expectIndexLF actual &&
159
160 # UTF-${i} with LF (Unix line endings)
161 rm eol.utf${i} &&
162 git -c core.eol=lf checkout eol.utf${i} &&
163 test_cmp_bin lf.utf${i}.raw eol.utf${i} &&
164
165 # The file LF in the working tree, ensure LF in the index
166 git ls-files --eol eol.utf${i} >actual &&
167 test_cmp expectIndexLF actual
168 '
169 done
170
171 test_expect_success 'check unsupported encodings' '
172 test_when_finished "git reset --hard HEAD" &&
173
174 echo "*.set text working-tree-encoding" >.gitattributes &&
175 printf "set" >t.set &&
176 test_must_fail git add t.set 2>err.out &&
177 test_grep "true/false are no valid working-tree-encodings" err.out &&
178
179 echo "*.unset text -working-tree-encoding" >.gitattributes &&
180 printf "unset" >t.unset &&
181 git add t.unset &&
182
183 echo "*.empty text working-tree-encoding=" >.gitattributes &&
184 printf "empty" >t.empty &&
185 git add t.empty &&
186
187 echo "*.garbage text working-tree-encoding=garbage" >.gitattributes &&
188 printf "garbage" >t.garbage &&
189 test_must_fail git add t.garbage 2>err.out &&
190 test_grep "failed to encode" err.out
191 '
192
193 test_expect_success 'error if encoding round trip is not the same during refresh' '
194 BEFORE_STATE=$(git rev-parse HEAD) &&
195 test_when_finished "git reset --hard $BEFORE_STATE" &&
196
197 # Add and commit a UTF-16 file but skip the "working-tree-encoding"
198 # filter. Consequently, the in-repo representation is UTF-16 and not
199 # UTF-8. This simulates a Git version that has no working tree encoding
200 # support.
201 echo "*.utf16le text working-tree-encoding=utf-16le" >.gitattributes &&
202 echo "hallo" >nonsense.utf16le &&
203 TEST_HASH=$(git hash-object --no-filters -w nonsense.utf16le) &&
204 git update-index --add --cacheinfo 100644 $TEST_HASH nonsense.utf16le &&
205 COMMIT=$(git commit-tree -p $(git rev-parse HEAD) -m "plain commit" $(git write-tree)) &&
206 git update-ref refs/heads/main $COMMIT &&
207
208 test_must_fail git checkout HEAD^ 2>err.out &&
209 test_grep "error: .* overwritten by checkout:" err.out
210 '
211
212 test_expect_success 'error if encoding garbage is already in Git' '
213 BEFORE_STATE=$(git rev-parse HEAD) &&
214 test_when_finished "git reset --hard $BEFORE_STATE" &&
215
216 # Skip the UTF-16 filter for the added file
217 # This simulates a Git version that has no checkoutEncoding support
218 cp nobom.utf16be.raw nonsense.utf16 &&
219 TEST_HASH=$(git hash-object --no-filters -w nonsense.utf16) &&
220 git update-index --add --cacheinfo 100644 $TEST_HASH nonsense.utf16 &&
221 COMMIT=$(git commit-tree -p $(git rev-parse HEAD) -m "plain commit" $(git write-tree)) &&
222 git update-ref refs/heads/main $COMMIT &&
223
224 git diff 2>err.out &&
225 test_grep "error: BOM is required" err.out
226 '
227
228 test_lazy_prereq ICONV_SHIFT_JIS '
229 iconv -f UTF-8 -t SHIFT-JIS </dev/null
230 '
231
232 test_expect_success ICONV_SHIFT_JIS 'check roundtrip encoding' '
233 test_when_finished "rm -f roundtrip.shift roundtrip.utf16" &&
234 test_when_finished "git reset --hard HEAD" &&
235
236 text="hallo there!\nroundtrip test here!" &&
237 printf "$text" | iconv -f UTF-8 -t SHIFT-JIS >roundtrip.shift &&
238 printf "$text" | write_utf16 >roundtrip.utf16 &&
239 echo "*.shift text working-tree-encoding=SHIFT-JIS" >>.gitattributes &&
240
241 # SHIFT-JIS encoded files are round-trip checked by default...
242 GIT_TRACE=1 git add .gitattributes roundtrip.shift 2>&1 |
243 grep "Checking roundtrip encoding for SHIFT-JIS" &&
244 git reset &&
245
246 # ... unless we overwrite the Git config!
247 ! GIT_TRACE=1 git -c core.checkRoundtripEncoding=garbage \
248 add .gitattributes roundtrip.shift 2>&1 |
249 grep "Checking roundtrip encoding for SHIFT-JIS" &&
250 git reset &&
251
252 # UTF-16 encoded files should not be round-trip checked by default...
253 ! GIT_TRACE=1 git add roundtrip.utf16 2>&1 |
254 grep "Checking roundtrip encoding for UTF-16" &&
255 git reset &&
256
257 # ... unless we tell Git to check it!
258 GIT_TRACE=1 git -c core.checkRoundtripEncoding="UTF-16, UTF-32" \
259 add roundtrip.utf16 2>&1 |
260 grep "Checking roundtrip encoding for utf-16" &&
261 git reset &&
262
263 # ... unless we tell Git to check it!
264 # (here we also check that the casing of the encoding is irrelevant)
265 GIT_TRACE=1 git -c core.checkRoundtripEncoding="UTF-32, utf-16" \
266 add roundtrip.utf16 2>&1 |
267 grep "Checking roundtrip encoding for utf-16" &&
268 git reset
269 '
270
271 # $1: checkout encoding
272 # $2: test string
273 # $3: binary test string in checkout encoding
274 test_commit_utf8_checkout_other () {
275 encoding="$1"
276 orig_string="$2"
277 expect_bytes="$3"
278
279 test_expect_success "Commit UTF-8, checkout $encoding" '
280 test_when_finished "git checkout HEAD -- .gitattributes" &&
281
282 test_ext="commit_utf8_checkout_$encoding" &&
283 test_file="test.$test_ext" &&
284
285 # Commit as UTF-8
286 echo "*.$test_ext text working-tree-encoding=UTF-8" >.gitattributes &&
287 printf "$orig_string" >$test_file &&
288 git add $test_file &&
289 git commit -m "Test data" &&
290
291 # Checkout in tested encoding
292 rm $test_file &&
293 echo "*.$test_ext text working-tree-encoding=$encoding" >.gitattributes &&
294 git checkout HEAD -- $test_file &&
295
296 # Test
297 printf $expect_bytes >$test_file.raw &&
298 test_cmp_bin $test_file.raw $test_file
299 '
300 }
301
302 test_commit_utf8_checkout_other "UTF-8" "Test Тест" "\124\145\163\164\040\320\242\320\265\321\201\321\202"
303 test_commit_utf8_checkout_other "UTF-16LE" "Test Тест" "\124\000\145\000\163\000\164\000\040\000\042\004\065\004\101\004\102\004"
304 test_commit_utf8_checkout_other "UTF-16BE" "Test Тест" "\000\124\000\145\000\163\000\164\000\040\004\042\004\065\004\101\004\102"
305 test_commit_utf8_checkout_other "UTF-16LE-BOM" "Test Тест" "\377\376\124\000\145\000\163\000\164\000\040\000\042\004\065\004\101\004\102\004"
306 test_commit_utf8_checkout_other "UTF-16BE-BOM" "Test Тест" "\376\377\000\124\000\145\000\163\000\164\000\040\004\042\004\065\004\101\004\102"
307 test_commit_utf8_checkout_other "UTF-32LE" "Test Тест" "\124\000\000\000\145\000\000\000\163\000\000\000\164\000\000\000\040\000\000\000\042\004\000\000\065\004\000\000\101\004\000\000\102\004\000\000"
308 test_commit_utf8_checkout_other "UTF-32BE" "Test Тест" "\000\000\000\124\000\000\000\145\000\000\000\163\000\000\000\164\000\000\000\040\000\000\004\042\000\000\004\065\000\000\004\101\000\000\004\102"
309
310 test_done