Заставить Git читать файлы ANSI, содержащие NUL

Есть много-много мест, описывающих, как заставить Git читать файл как текст. Как правило, решение включает в себя добавление фильтра к .gitattributes для применения атрибута text к файлу (файлам). Примеры включают:

* text
* text=auto
* text diff merge
* text=auto diff merge

Но это решение, похоже, не работает, если файл содержит NUL. Вот пример текстового файла с кодировкой ANSI и завершающими нулевыми байтами:

введите здесь описание изображения

Он полностью читается как текстовый файл, но не Git. Каждый приведенный выше пример фильтра не сработает, и Git все равно идентифицирует его как двоичный. Я думаю, это связано с жестко закодированной проверкой NUL в первых 8000 символов (ref).

Конечно, как только я конвертирую файл в UTF-8, Git с радостью идентифицирует его как текст. Вот тот же файл после конвертации:

введите здесь описание изображения

Честно говоря, я не возражаю против не использования кодировки ANSI. Я просто пытаюсь избежать постоянного открытия файлов в Notepad++ только для того, чтобы исправить кодировку файла. Есть ли способ заставить Git автоматически обрабатывать преобразование кодировки?


person patricktokeeffe    schedule 18.09.2020    source источник
comment
Не существует основной однобайтовой кодировки (будь то так называемая ANSI или нет), где NUL — это что-то отличное от NUL. Тот же байт также является NUL в UTF-8. Ваш инструмент фактически неправильно удаляет эти символы при преобразовании их в UTF-8. И это по определению не текстовый файл, так как NUL никогда не допустим в текстовом файле, согласно POSIX.   -  person bk2204    schedule 18.09.2020


Ответы (1)


У вас тут пара проблем. Во-первых, это определенно не текстовые файлы, поскольку они содержат байт NUL. Никакая основная однобайтовая кодировка не позволяет байтам NUL представлять что-либо, кроме NUL, потому что C завершает свои строки этим байтом, и использование его для другой цели означало бы, что текст в этой кодировке не помещается в обычную строку C. По этой причине POSIX специально исключает файлы, содержащие байты NUL, из текстовых файлов.

Инструмент, который вы используете для преобразования файлов «ANSI» в UTF-8, на самом деле удаляет байты NUL, поэтому они работают. Байт NUL в UTF-8 означает то же самое, что и в вашей однобайтовой кодировке: NUL. Так что это работает, потому что ваш инструмент удаляет их, а не конвертирует должным образом.

Также неясно, что вы просите Git сделать в этом случае. Атрибут text просит Git выполнить нормализацию конца строки. Однако, если ваш файл содержит байты NUL, Git все равно будет думать, что это двоичный файл для целей сравнения и слияния, потому что атрибут text не контролирует это. Вам также понадобятся атрибуты diff и merge.

Конечно, если вы действительно не хотите или не нуждаетесь в байтах NUL, и они должны быть удобочитаемыми для человека, тогда вам действительно лучше просто удалить байты NUL и преобразовать в UTF-8. В 2020 году больше нет веских причин использовать однобайтовую кодировку. Если это то, что вы хотите сделать, вы можете удалить байты NUL и преобразовать в UTF-8, выполнив следующие действия (при условии, что вы используете Git Bash, WSL или систему Linux):

$ tr -d '\0' FILENAME | iconv -f WINDOWS-1252 -t UTF-8 > FILENAME.tmp && \
  mv FILENAME.tmp FILENAME

Это также предполагает, что используемая вами кодировка «ANSI» на самом деле является Windows-1252. IANA (реестр наборов символов) не знает никаких кодировок, называемых «ANSI», но Windows-1252 является наиболее распространенным набором символов, упоминаемым таким образом.

Наконец, вы можете указать кодировку рабочего дерева со значением working-tree-encoding в gitattributes, если вам абсолютно необходимо обрабатывать файлы, отличные от UTF-8. Однако это не решит вашу проблему с NUL, а UTF-8 — лучший выбор почти во всех ситуациях.

person bk2204    schedule 18.09.2020
comment
Нет нет. NULL разрешен в большинстве кодировок, и это не строка с завершением C. C выбрал такую ​​конвенцию, и она также называлась ASCIIZ. Другие протоколы просто используют новую строку как завершающую (или специальный переход). Pascal, Python (и многие другие языки) позволяют вам иметь NULL в строке, и это нормально для строк ASCII. Существуют кодировки UTF-8 (не официальные из Unicode, но совместимые), которые допускают NULL в строке и \0 для завершения строки. - person Giacomo Catenazzi; 18.09.2020
comment
Я не спорю ни о каком другом языке, кроме C. Я полностью осознаю, что NUL разрешен в строках многих языков. Однако это запрещено в текстовых файлах стандартом POSIX, и я поддерживаю мое заявление о том, что никакая основная однобайтовая кодировка не использует нулевые байты как что-либо кроме NUL. - person bk2204; 18.09.2020
comment
ASCII допускает NULL. Вопрос не в POSIX (никто не будет использовать ANSI для кодировки в POSIX), и речь идет не о языке C (но если использовать #define If if и т. д.). Linux использует нуль (например, в /proc в качестве разделителя полей (остальное - текст), а git касается не только текстовых файлов (в определении posix, на самом деле, он допускает разные символы конца строки). - person Giacomo Catenazzi; 18.09.2020
comment
Я полностью понимаю, как работает Git в этом случае и что он может работать с любыми файлами; Я основной участник. Общепонятное определение текстового файла (в отличие от двоичного файла) исключает байты с NUL; использование стандартного определения, такого как POSIX, для определения термина текстовый файл вполне разумно. Например, file назвал бы эти файлы «данными», потому что они содержат NUL, а не «текст»; подробности см. на странице руководства. - person bk2204; 19.09.2020