Привет. В силу специфики, на работе используется Linux с KOI8-R, все коммиты в git репозиторий осуществлялись в локальной кодировке. Через некоторое время было принято решение перекодировать репозиторий в UTF-8. В этой статье я хочу обсудить технологию изменения кодировки существующего git репозитория, а заодно и исправления некоторых ошибок допущенных в определенных коммитах.
Предупреждение
Фактически, будет создан новый репозиторий, соответственно перед проведением процедуры необходимо приостановить текущую разработку, слить все изменения в условно центральный репозиторий, в котором мы и будем производить перекодировку. После проверки полученного репозитория, необходимо будет повторно склонировать его на все машины.
Git и кодировка
Git оперирует бинарными данными, поэтому с кодировкой файлов он никак не взаимодействует, что касается комментариев коммитов, то он также сохраняет их в том виде, котором мы ему их передали, но при этом для каждого коммита заполняется заголовок encoding
, который в дальнейшем может быть использован при запросе комментариев. Если заголовок encoding
пустой, git считает его равным UTF-8.
Для настройки существуют два параметра находящиеся в секции [i18n]:
[i18n]
commitencoding = UTF-8
logoutputencoding = KOI8-R
Первый из них как раз задает содержание заголовка encoding для команд git commit
и git commit-tree
, второй сообщает командам git log
, git show
, git blame
в какую кодировку следует перекодировать текст комментария перед выводом пользователю. Если ни один из параметров не задан, git считает, что logoutputencoding
равен UTF-8, однако, если установлен только первый параметр, git использует его значение и для второго.
Из-за этого могут возникать различные ошибки – например, если в коммитах заголовок encoding
не соответствует кодировке комментария, но равен значению параметра logoutputencoding
, git решит что перекодировка не требуется и выведет текст комментария как он есть, соответственно на машинах с локалью установленной в той же кодировке что и комментарий, содержимое будет отображено корректно, хотя на всех остальных будет мусор.
Для того, чтобы посмотреть значение заголовка encoding
комментариев, можно воспользоваться следующей командой:
git log –pretty=”%h - ‘%e’: %s”
Подробнее о возможностях команды git log
можно прочитать здесь
Git filter-branch
Итак, мы подошли к основной теме данной статьи. Для того, что бы «переписать историю» имеющегося репозитория используется команда git filter-branch
. Она позволяет последовательно повторить все произведенные коммиты предварительно обработав файлы или мета-данные различными фильтрами.
В данной статье используются три фильтра:
--msg-filter
– применяется для перезаписи текста комментария коммитов;--env-filter
– применяется, если необходимо изменить окружение, в котором был произведен коммит (имя автора, адрес электронной почты и т.д.);--tag-name-filter
– применяется для перезаписи текстов меток.
После каждого фильтра задается команда, которую git filter-branch
выполнит перед записью коммита.
Для того, что бы пройти по всему репозиторию, необходимо указать параметр --all
, отделив его дополнительным --
от фильтров, указать HEAD
как цель и перезаписать метки (tags
) согласно новым коммитам. Для этого, необходимо добавить фильтр tag-name
с командой cat
:
git filter-branch <фильтры> --tag-name-filter 'cat' -- --all HEAD
Прежде чем менять кодировку комментариев, не забываем задать правильное значение директивы i18n.commitencoding
– именно оно будет записано во всех заголовках полученного после выполнения операции репозитория.
Для конвертации кодировки комментария используем следующую команду:
'iconv -c -s -f KOI8-R -t UTF-8'
- s – silent mode;
- с – пропускать символы, которые не удается преобразовать.
Команда git filter-branch
принимает следующий вид:
git filter-branch --msg-filter 'iconv -c -s -f KOI8-R -t UTF-8'
--tag-name-filter 'cat' -- --all HEAD
Поскольку операция по «переписыванию истории” достаточно грубо вмешивается в рабочий процесс, имеет смысл (если вы все-таки решились ее произвести) попытаться исправить максимальное количество ошибок. Это могут быть неправильно заданные параметры окружения, сохраненные в репозитории файлы, которых там быть не должно, кодировка или часть данных отдельных файлов и т.д.
В частности, я обнаружил что у пары коммитов был неверно задан e-mail автора. Поскольку на тот момент все коммиты были созданы мной, проблема решилась просто перезаписью этого параметра во всех коммитах:
git filter-branch --msg-filter 'iconv -c -s -f KOI8-R -t UTF-8'
--env-filter 'export GIT_AUTHOR_EMAIL="xxx@gmail.com" export GIT_COMMITTER_EMAIL="xxx@gmail.com"'
--tag-name-filter 'cat' -- --all HEAD
Но естественно никто не мешает использовать более сложные конструкции с различными условиями и т.д.
В целом, команда git filter-branch
предоставляет очень богатый функционал для модификации/исправления git репозитория. Обо всех ее возможностях можно прочитать здесь:
Автор: Distress