| 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 |