
Предупреждение
Фактически, будет создан новый репозиторий, соответственно перед проведением процедуры необходимо приостановить текущую разработку, слить все изменения в условно центральный репозиторий, в котором мы и будем производить перекодировку. После проверки полученного репозитория, необходимо будет повторно склонировать его на все машины.
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 репозитория. Обо всех ее возможностях можно прочитать здесь.