Почему GIT изначально не поддерживает UTF-16 - PullRequest
0 голосов
/ 24 сентября 2018

Git поддерживает несколько различных схем кодирования: UTF-7, UTF-8, UTF-32, а также не-UTF.

Учитывая это, почему он не поддерживает UTF-16?

Есть много вопросов, которые задают, как заставить git поддерживать UTF-16, но я не думаю, что об этом прямо уже задавали или отвечали.

Ответы [ 5 ]

0 голосов
/ 14 февраля 2019

git недавно начал понимать такие кодировки, как utf16.См. gitattributes docs, поиск кодировки рабочего дерева

Если вы хотите, чтобы файлы .txt были utf-16 без BOM на компьютере Windows, то добавьте в файл gitattributes

*.txt text working-tree-encoding=UTF-16LE eol=CRLF

Добавлено в ответ на комментарии @jthill выше

Нет сомнений, что UTF16 - беспорядок.Однако рассмотрим

  • Java использует UTF16
  • Как и Microsoft

    Обратите внимание на строку UTF16… тот, который используется для родного кодирования Unicode в операционных системах Windows

  • Javascript использует mess между UCS-2 и UTF-16
0 голосов
/ 17 ноября 2018

Git поддерживает UTF-16 ... для переменных среды, с Git 2.20 (Q4 2018)
(и исправление ошибки в Git 2.21: см. Вторую часть ответа)

См. коммит fe21c6b , коммит 665177e (30 октября 2018 г.) от Йоханнес Шинделин (dscho) .
Помощник: ДжеффHostetler (jeffhostetler) .
(Объединено Junio ​​C Hamano - gitster - в коммит 0474cd1 , 13 ноября 2018 г.)

mingw: перекодировать переменные среды на лету (UTF-16 <-> UTF-8)

В Windows авторитетная среда кодируется в UTF-16.
в Gitдля Windows мы конвертируем это в UTF-8 (потому что UTF-16 является такой чужой идеей для Git, что его исходный код к нему не подготовлен).

Ранее из соображений производительностимы конвертировали всю среду в UTF-8 одним махом в начале, а после putenv() и run_command() преобразовали ее обратно.

Имея личную копию средыидет со своими собственными опасностями: когда библиотека, используемая исходным кодом Git, пытается изменить среду, она на самом деле не работает (в случае Git для Windows libcurl, см. git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2 для краткого обзораиз вопросов).

Следовательно, это делает нашу среду значительно более надежной, если мы переключаемся на преобразование «на лету» при вызовах getenv() / putenv().
На основе начальной версиив контексте MSVC Джеффа Хостетлера (Jeff Hostetler) этот патч делает это так.

Удивительно, но это оказывает положительное влияние на скорость: во время написания текущего кода мы тестировали производительностьи было столько getenv() вызовов, что казалось, что лучше конвертировать все за один раз.
Тем временем, тем не менее, Git, очевидно, был немного очищен относительно getenv()вызовы, так что процессы Git, порожденные набором тестов, используют в среднем только 40 getenv() / putenv() вызовов в течение времени жизни процесса.

Говоря о всем наборе тестов: общее время, потраченное в тповторное кодирование в текущем коде занимает около 32,4 секунды (из 113 минут времени выполнения), тогда как код, представленный в этом патче, занимает всего около 8,2 секунды.
Не так много, но это доказывает, что нам не следует об этом беспокоитьсяо влиянии на производительность, представленном этим патчем.


В Git 2.21 (Q1 2019) предыдущий путь привел к ошибке, которая повлияла на команду GIT_EXTERNAL_DIFF: строка, возвращаемая из getenv()быть неустойчивым, что не соответствует действительности, которое было исправлено.

См. коммит 6776a84 (11 января 2019) Ким Гайбельс (Jeff-G) .
(Объединено с Junio ​​C Hamano - gitster - in commit 6a015ce , 29 Jan 2019)

Theсообщалось об ошибке в git-for-windows/git выпуске 2007 :
"Невозможно использовать difftool для более чем 8 файлов"

$ yes n | git -c difftool.prompt=yes difftool fe21c6b285df fe21c6b285df~100

Viewing (1/404): '.gitignore'
Launch 'bc3' [Y/n]?
Viewing (2/404): 'Documentation/.gitignore'
[...]
Viewing (8/404): 'Documentation/RelNotes/2.18.1.txt'
Launch 'bc3' [Y/n]?
Viewing (9/404): 'Documentation/RelNotes/2.19.0.txt'
Launch 'bc3' [Y/n]? error: cannot spawn ¦?: No such file or directory
fatal: external diff died, stopping at Documentation/RelNotes/2.19.1.txt

Следовательно:

diff: обеспечить правильное время жизни external_diff_cmd

Согласно примечаниям getenv (3):

Реализация getenv() не требуется для повторного входа.
Строка, на которую указывает возвращаемое значение getenv(), может быть статически распределена и может быть изменена путем последующего вызова getenv(), putenv(3), setenv(3),или unsetenv(3).

Поскольку строки, возвращаемые getenv(), могут меняться при последующих вызовах на getenv(), убедитесь, что дублируете при кэшировании external_diff_cmd из среды.

Эта проблема становится очевидной в Git для Windows начиная с fe21c6b (mingw: перекодировать переменные среды на лету (UTF-16 <-> UTF-8)), когда реализация getenv() представлена ​​в compat/mingw.c было изменено, чтобы сохранить определенное количество выделенных строк и освободить их при последующих вызовах.

0 голосов
/ 24 сентября 2018

Я посвящаю значительную часть полной главы моей (в настоящее время довольно умирающей) книги (см. Главу 3, которая находится в лучшей форме, чем более поздние главы) проблеме кодирования символов, потому что этоисторический беспорядок.Здесь стоит упомянуть, однако, что часть предпосылки этого вопроса - то, что Git каким-то образом поддерживает UTF-7 и UTF-32 - неверна: UTF-7 - это стандарт, который никогда даже не возник и, вероятно, никогда не должен использоваться вообще (так, естественно, более старые версии Internet Explorer делают, и это приводит к проблеме безопасности, упомянутой на связанной странице Википедии).

Тем не менее, давайте сначала отделим кодировку символов из кодовые страницы .(См. Также раздел сносок ниже.) Основная проблема здесь заключается в том, что компьютеры - ну, в любом случае, современные - работают с серией 8-битных байтов , с каждым байтомпредставляет целое число в диапазоне [0..255].В старых системах было 6, 7, 8 и даже 9-битные байты, хотя я думаю, что называть что-либо менее 8 бит «байтом» неверно.("Машины C" BBN имели 10-битные байты!) В любом случае, если один байт представляет один символ-символ, это дает нам верхний предел 256 видов символов.В те плохие старые времена ASCII этого было достаточно, поскольку в ASCII было всего 128 символов, 33 из которых были непечатными символами (управляющие коды от 0x00 до 0x1f, плюс 0x7f, представляющие DELили удаленный удар на бумажной ленте, записывая их здесь в шестнадцатеричном формате).

Когда нам нужно более 94 печатных символов плюс пробел (0x20), мы - мы Я имею в виду людей, использующих компьютеры по всему миру , а не конкретно me - сказал: Хорошо, посмотрите на это, у нас есть 128 неиспользуемых кодировок, от 0x80 до 0xff, давайтеиспользуйте некоторые из них! Таким образом, французы использовали некоторые для ç и é и так далее, и пунктуацию как «и».Чехи нуждались в одном для Z-с-Caron, ž.Русским нужно было много, для кириллицы.Грекам нужно было много и так далее.В результате верхняя половина 8-битного пространства была разбита на множество несовместимых наборов, которые люди называли кодовыми страницами .

По сути, компьютер хранит около восьми-байтное значение байта, такое как 235 десятичное число (0xEB hex), и это зависит от чего-то другого - другой компьютерной программы или, в конечном счете, человека, смотрящего на экран, чтобы интерпретировать эти 235 как, скажем, кириллический символ «, илигреческий λ или что-то еще.Кодовая страница, если мы ее используем, говорит нам, что означает «235» : какую семантику мы должны навязать этому.

Проблема здесь в том, что существует ограничение насколько кодов символов мы можем поддержать.Если мы хотим, чтобы кириллица L (л) сосуществовала с греческой буквой L (лямбда, λ), мы не можем одновременно использовать CP-1251 и CP-1253, поэтому нам нуженлучший способ кодировать символ .Очевидным способом является прекращение использования однобайтовых значений для кодирования символов: если мы используем двухбайтовые значения, мы можем кодировать 65536 значений, от 0x0000 до 0xffff включительно;вычтите несколько для контрольных кодов, и еще есть место для многих алфавитов.Тем не менее, мы быстро преодолели даже этот предел, поэтому мы перешли к Unicode, в котором есть место для 1114112 того, что он называет кодовые точки , каждый из которых представляет своего рода символ с некоторымсвоего рода семантическое значение.В настоящее время используется более 100 000 из них, включая Emoji, такие как ? и ?.

Кодирование Unicode в байты или слова

Здесь UTF-8, UTF-16, UTF-32,Все UCS-2 и UCS-4 входят. Это все схемы для кодирования кодовых точек Unicode - одного из этих ~ 1 миллиона значений - в байтовые потоки.Я собираюсь полностью пропустить UCS и взглянуть только на кодировки UTF-8 и UTF-16, так как они являются наиболее интересными в настоящее время.(См. Также Что такое Unicode, UTF-8, UTF-16? )

Кодировка UTF-8 является строгойghtforward: любая кодовая точка, десятичное значение которой меньше 128, кодируется как байт, содержащий это значение.Это означает, что обычные текстовые символы ASCII остаются обычными текстовыми символами ASCII.Кодовые точки от 0x0080 (128 десятичных) до 0x07ff (2047 десятичных) кодируются в два байта, оба из которых находятся в диапазоне 128-255 и, следовательно, отличаются от однобайтового кодированного значения.Кодовые точки в диапазоне от 0x0800 до 0xffff кодируются в три байта в том же диапазоне 128-255, а оставшиеся действительные значения кодируются в четыре таких байта. Ключевым моментом в том, что касается самого Git, является то, что ни одно закодированное значение не похоже на ASCII NUL (0x00) или косую черту (0x2f).

Что это за кодировка UTF-8делает, чтобы Git делал вид, что , что текстовые строки - и особенно имена файлов - являются разделенными слешем именными компонентами, концы которых, или в любом случае, отмечены байтами ASCII NUL.Это кодировка, которую Git использует в древовидных объектах, поэтому кодированные в UTF-8 древовидные объекты просто подходят, без необходимости перебирать.

Кодировка UTF-16 использует два парных байта на символ.У этого есть две проблемы для Git и путей.Во-первых, байт в паре может случайно напоминать /, и все ASCII-значимые символы обязательно кодируются как пара байтов, где один байт равен 0x00, что напоминает ASCII NUL.Поэтому Git должен знать: это имя пути было закодировано в UTF-16 и работать с байтовыми парами.В объекте дерева нет места для этой информации, поэтому Git понадобится новый тип объекта.Во-вторых, всякий раз, когда мы разбиваем 16-битное значение на два отдельных 8-битных байта, мы делаем это в некотором порядке: сначала я даю вам более старший байт, а затем менее значимый;или сначала я даю менее значимый байт, затем более значимый.Эта вторая проблема приводит к тому, что UTF-16 имеет байтовых меток .UTF-8 не нуждается в метке порядка байтов и достаточно, так почему бы не использовать это в деревьях?Git делает.

Это хорошо для деревьев, но у нас также есть коммиты, теги и BLOB-объекты

Git по-своему интерпретирует три из этих четырех типов объектов:

  1. Коммиты содержат хэш-идентификаторы.
  2. Деревья содержат пути, режимы файлов и хэш-идентификаторы.
  3. Теги содержат хэш-идентификаторы.

Тот, которыйздесь не указан blob , и по большей части Git не выполняет никакой интерпретации BLOB-объектов.

Чтобы упростить понимание коммитов, деревьев и тегов, ограничения Gitвсе три должны быть в UTF-8 по большей части.Однако Git делает разрешающим сообщение журнала в коммите или текст тега в теге, чтобы быть несколько (в основном) не интерпретируемыми.Они следуют после заголовка, который интерпретирует Git, так что даже если в этот момент есть что-то особенно хитрое или уродливое, это довольно безопасно.(Здесь есть некоторые незначительные риски, так как сигнатуры PGP, которые появляются под заголовками, do интерпретируются.) В частности, для коммитов, современный Git будет включать в интерпретируемую строку заголовка encoding в интерпретируемомsection, и Git может затем попытаться декодировать тело сообщения фиксации и перекодировать его в любую кодировку, используемую любой программой, интерпретирующей байты, которые Git выводит. 1

Те же правила могут работать для аннотированных теговых объектов.Я не уверен, есть ли в Git код, который делает это для тегов (код коммита в основном можно использовать повторно, но теги гораздо чаще имеют подписи PGP, и, вероятно, разумнее просто использовать здесь UTF-8).Поскольку деревья являются внутренними объектами, их кодировка в значительной степени невидима в любом случае - вам не нужно знать об этом (за исключением проблем, которые я указал в моей книге).

Это оставляеткапли, которые являются большой гориллой.


1 Это повторяющаяся тема в компьютерном мире: все постоянно кодируется и декодируется.Подумайте, как что-то приходит через WiFi или соединение по кабельной сети: оно было закодировано в какую-то радиоволну или подобное, а затем некоторое оборудование декодирует это в поток битов, который некоторое другое оборудование перекодирует в поток байтов.Аппаратное и / или программное обеспечение снимает заголовки, каким-то образом интерпретирует оставшееся кодирование, соответствующим образом изменяет данные и перекодирует биты и байты, чтобы иметь дело с другим уровнем аппаратного и программного обеспечения.Удивительно, что что-то когда-либо делается.


Кодировка BLOB-объектов

Git любит утверждать, что он полностью не зависит от фактических данных , хранящихся в ваших файлах, как Gitсгустки.Это даже в основном верно.Или, ну, наполовину правда.Или что-то.Пока Git хранит ваши данные, это полностью верно!Git просто хранит байты.Что эти байты означают зависит от вас.

Эта история разваливается, когда вы запускаете git diff или git merge, потому что алгоритмы diff и, следовательно, код слияния, линия ориентированная.Строки заканчиваются символами новой строки.(Если вы работаете в системе, в которой вместо новой строки используется CRLF, то второй символ пары CRLF равен новой строкой, поэтому здесь нет проблем - и Git в порядке с неопределенной последней строкой,хотя это вызывает некоторые незначительные биты изжоги здесь и там.) Если файл закодирован в UTF-16, многие байты, как правило, выглядят как ASCII NUL, поэтому Git просто обрабатывает его как двоичный файл.

This исправимо : Git может декодировать данные UTF-16 в UTF-8, передавать эти данные через все свои существующие алгоритмы, ориентированные на строки (которые теперь будут видеть строки, оканчивающиеся на новую строку), и затем перекодироватьданные возвращаются в UTF-16.Здесь есть куча мелких технических проблем;самое большое - это решить, что какой-то файл - это UTF-16, и если да, то какой порядковый номер (UTF-16-LE или UTF-16-BE?).Если файл имеет маркер порядка байтов, который решает проблему с порядком байтов, и UTF-16-ность может быть закодирована в .gitattributes так же, как вы можете в настоящее время объявить файлы binary или text, так что все это решаемо.Это просто грязно, и никто еще не сделал эту работу.

Footnote-ish: кодовые страницы можно считать (дрянной) формой кодирования

Я упоминал выше, что мы делаем с этимЮникод предназначен для кодирования 21-битного значения кодовой точки в некотором количестве восьмибитных байтов (от 1 до 4 байтов в UTF-8, 2 байта в UTF-16 - есть ужасный маленький трюк с тем, что UTF-16 называет суррогаты , чтобы сжать 21 бит значения в 16 бит контейнера, иногда используя пары 16-битных значений, здесь).Этот прием кодирования означает, что мы можем представить все допустимые значения 21-битной кодовой точки, хотя для этого может потребоваться несколько 8-битных байтов.

Когда мы используем кодовую страницу (CP- число * 1170)*) то, что мы делаем, или, по крайней мере, может рассматриваться как отображение 256 значений - тех, которые вписываются в один 8-битный байт - в этот 21-битный кодточка пространства.Мы выбираем некоторое подмножество не более 256 таких кодовых точек и говорим: Это допустимые кодовые точки. Мы кодируем первый как, скажем, 0xa0, второй как 0xa1, и так далее.Мы всегда оставляем место по крайней мере для нескольких управляющих кодов - обычно всех 32 в диапазоне от 0x00 до 0x1f - и обычно оставляем все 7-битное подмножество ASCII, как это делает сам Юникод (см. https://en.wikipedia.org/wiki/List_of_Unicode_characters),именно поэтому мы обычно начинаем с 0xa0.

Когда кто-то пишет правильные библиотеки поддержки Unicode, кодовые страницы просто становятся таблицами перевода, используя только эту форму индексации.создание точных таблиц для всех кодовых страниц, которых очень много.

The niВ кодовых страницах главное то, что символы снова по одному байту.Плохо то, что вы выбираете свой набор символов один раз, когда говорите: Я использую эту кодовую страницу. С этого момента вы заблокированы в этом небольшом подмножестве Unicode.Если вы переключаетесь на другую кодовую страницу, некоторые или все ваши восьмибитные байтовые значения представляют различных символа.

0 голосов
/ 24 сентября 2018

В краткой форме добавлена ​​поддержка широких символов, все усложняется.Все, что имеет дело с любой из 8-битных кодовых страниц ISO или UTF-8 или любым другим MBCS , может сканировать / охватывать / копировать строки без особых усилий.Попробуйте добавить поддержку строк, чья кодировка передачи содержит встроенные значения NULL, и сложности даже для тривиальных операций начинают увеличивать весь ваш код.

Я не знаю каких-либо даже заявленных преимуществ для UTF-16, которые не более чем отменены недостатками, которые проявляются, когда вы фактически начинаете использовать его.Вы можете идентифицировать границу строки в любом из ASCII, UTF-8, во всех 16 наборах ISO / IEC-8859, во всех EBCDIC и, возможно, еще в дюжине, с тем же простым кодом.Только с небольшими ограничениями (на основе ascii, с несколькими строками, добавленными для соглашений об ограничении нескольких строк) вы получаете базовую токенизацию, и транслитерация на общую внутреннюю кодовую страницу в основном бесплатна.

Добавьте поддержку UTF-16, и вытолько что купил себе огромное количество дополнительных усилий и сложности, но вся эта работа ничего не дает - после того, как сказал «о, но теперь он может обрабатывать UTF-16!», то, что еще теперь возможно со всем этимдобавил раздувание и усилия?Ничего такого.Все, что UTF-16 может сделать, UTF-8 может делать то же самое и обычно намного лучше.

0 голосов
/ 24 сентября 2018

Первое упоминание UTF-8 в кодовой базе Git относится к d4a9ce7 (август 2005 г., v0.99.6) , что касалось исправлений почтового ящика:

Опционально,с флагом '-u' выходные данные в .info и .msg транслитерируются из исходного чейза в utf-8.Это сделано для того, чтобы побудить людей использовать utf8 в своих сообщениях о коммитах для обеспечения взаимодействия.

Подписано Junio ​​C Hamano / 濱 野 純 <junkio@cox.net>.

Кодировка символов была уточнена в commit 3a59e59 (июль 2017 г., Git v2.6.0-rc0

То, что «git кодирует агностик», действительно верно только для объектов BLOB-объектов.
Например, «не-NUL»Байтовое требование к объектам дерева и фиксации исключает UTF-16/32, а специальное значение '/' в файле индекса, а также пробел и перевод строки в объектах фиксации устраняет EBCDIC и другиеКодировки ASCII.

Git ожидает, что байты <0x80 будут чистыми <a href="https://en.wikipedia.org/wiki/ASCII" rel="nofollow noreferrer"> ASCII , таким образом, кодировки CJK , которые частично перекрываются с диапазоном ASCII, также проблематичны.
Напримерfmt_ident() удаляет завершающий 0x5C из имен пользователей при условии, что это ASCII '\'.
Однако существует более 200 GBK двухбайтовых кодов, которые заканчиваются на0x5C.

UTF-8 как код по умолчаниюg в Linux и соответствующие переводы путей в версиях Mac и Windows установили UTF-8 NFC как де-факто стандарт для имен путей.

См. " git, msysgit, accents, utf-8, окончательные ответы"для более подробной информации о последнем патче.

Самая последняя версия Documentation/i18n.txt включает в себя:

Git is toв некоторой степени не зависит от кодировки символов.

  • Содержимое объектов BLOB-объектов представляет собой неинтерпретированные последовательности байтов.Преобразование кодирования на уровне ядра отсутствует.

  • Имена путей кодируются в форме нормализации UTF-8 C .
    Это относится к:

    • древовидные объекты,
    • индексный файл,
    • имен ссылок, а также имена путей в
    • аргументах командной строки,
    • переменные окружения и
    • файлы конфигурации (.git/config, gitignore, gitattributes и gitmodules)

Вы можете увидетьпример преобразования пути в UTF-8 в commit 0217569 (январь 2012 г., Git v2.1.0-rc0 , в котором добавлена ​​поддержка имени файла Win32 Unicode.

Changes opendir /readdir для использования API-интерфейсов Windows Unicode и преобразования между UTF-8 / UTF-16.

Относительно аргументов командной строки, см. commit 3f04614 (январь 2011 г., Git v2.1.0-rc0) , который при запуске преобразует аргументы командной строки из UTF-16 в UTF-8.


Примечание: перед Git 2.21 (февраль 2019) код и тесты предполагают, что системапоставляется iconv() всегда будет использовать BOM в своем выводе при запросе кодирования в UTF-16 (или UTF-32), но, по-видимому, некоторые реализации выводят big-endian без BOM.
Добавлена ​​ручка времени компиляции, чтобы помочь таким системам (например, NonStop), чтобы добавить спецификацию к выходу для увеличения переносимости.

utf8: обрабатывать системы, которые не пишут спецификацию для UTF-16

при сериализации UTF-16 (и UTF-32), есть три возможных способа записи потока.Можно записать данные с помощью спецификации в формате с прямым порядком байтов или с прямым порядком байтов, или можно записать данные без спецификации в формате с прямым порядком байтов.

В большинстве систем iconv реализации выбирают записьэто с спецификацией с некоторым порядком байтов, поскольку это является наиболее надежной и устойчивой к неправильной интерпретации в Windows, где очень распространены UTF-16 и сериализация с прямым порядком байтов.
Для совместимости с Windows и предотвращения случайного неправильного использованиятам Git всегда хочет написать UTF-16 с BOM и откажется читать UTF-16 без него.

Однако реализация iconv в musl записывает UTF-16 без BOM, полагайтесьПользователь может интерпретировать его как big-endian.Это приводит к сбою t0028 и связанных с ним функций, так как Git не будет читать файл без спецификации.

...