Переключение репозитория Git из кодировки ISO-8859-1 в кодировку UTF-8 для файлов исходного кода - PullRequest
0 голосов
/ 08 июня 2018

В эти выходные я собираюсь преобразовать большой проект Mercurial в Git, используя fast-export .Я проверял это несколько раз, и результаты хорошие.

Мы также хотели бы перевести нашу кодировку исходного кода (много немецких комментариев / строковых литералов с Umlauts) с ISO-8859-1 на UTF-8 (все другие не-java файлы в репозитории должны оставаться как есть), и миграция Git дает нам шанс сделать это сейчас, поскольку в любом случае все должны снова клонироваться.Однако я не нахожу для этого подходящего подхода.

  1. Я пробовал подход git filter-tree --tree-filter ... из этого комментария к SO .Однако, хотя это кажется идеальным, из-за размера репозитория (около 200000 коммитов, 18000 кодовых файлов) это заняло бы гораздо больше времени, чем просто мои выходные.Я попытался запустить его (в сильно оптимизированной версии, где список файлов разбивается на фрагменты, а подсписки преобразуются параллельно (с использованием GNU параллельно )) прямо с тома 64 ГБ tmpfs на виртуальной машине Linux с 72ядер, и все же это заняло бы несколько дней ...
  2. В качестве альтернативы я попробовал простой подход, при котором я выполняю преобразование просто в любой активной ветви по отдельности и фиксирую изменения.Тем не менее, результат неудовлетворительный, потому что тогда я почти всегда получаю конфликты при слияниях или фиксации перед преобразованием, выбирая вишню.
  3. Теперь я снова запускаю подход 1, но не пытаюсь переписать полную историю всех ветвей(--all как <rev-list>), но только все коммиты, достижимые из текущих активных ветвей и не достижимые некоторым прошлым коммитом, который (надеюсь) является предшественником всех текущих ветвей (branch-a branch-b branch-c --not old-tag-before-branch-a-b-c-forked-off как <rev-list>).Он все еще работает, но я боюсь, что не могу доверять результатам, так как это кажется очень плохой идеей.
  4. Мы могли бы просто переключить кодировку в основной ветке с помощью обычного коммита, как в подходе 2, ноопять же, это сделает исправления выбора вишни из / чтобы справиться с катастрофой.И это приведет к множеству проблем с кодированием, потому что разработчики наверняка забудут изменить свои настройки IDE при переключении между главной и неконвертированной ветвями.

Так что сейчас я почему-то считаю, что лучшим решением может бытьпросто придерживайтесь ISO-8859-1.

У кого-нибудь есть идея?Кто-то упомянул, что, возможно, reposurgeon в принципе может приблизиться к 1, используя свою transcode операцию с производительностью намного лучше, чем git filter-tree --tree-filter ..., но я понятия не имею, как это работает.

Ответы [ 3 ]

0 голосов
/ 08 июня 2018

Вы можете рассмотреть возможность использования git filter-branch --index-filter - в отличие от --tree-filter (по умолчанию).Идея состоит в том, что в --index-filter нет этапа извлечения (т. Е. Рабочее дерево не заполняется (перезаполняется) на каждой итерации).

Так что вы можете написать фильтр для git filter-branch --index-filter, который быиспользуйте git ls-files - примерно так:

  1. Вызовите git ls-files --cached --stage и выполните итерации по каждой записи.

    Рассмотрите только те из них, которые имеют режим файла 100644, - чтоесть обычные файлы.

  2. Для каждой записи выполните что-то вроде

    sha1=`git show ":0:$filename" \
        | iconv -f iso8859-1 -t utf-8 \
        | git hash-object -t blob -w --stdin`
    git update-index --cacheinfo "10644,$sha1,$filename" --info-only
    
  3. Промыть, повторить.

Альтернативный подход, который я понимаю, состоит в том, чтобы атаковать проблему под другим углом: формат потоков, генерируемых git fast-export и потребляемых git fast-import, представляет собой простой текст plain (просто перенаправьте вывод вашего экспортера в less илидругой пейджер и убедитесь сами).

Вы можете написать фильтр, используя ваш любимый PL, который будет анализировать поток, перекодировать любые фрагменты data.Поток организован таким образом, что хэши SHA-1 не используются, поэтому вы можете перекодировать по ходу дела.Единственная очевидная проблема, которую я понимаю, состоит в том, что чанки data не несут информации о том, какой файл они будут представлять в результирующем коммите (если есть), поэтому, если у вас есть нетекстовые файлы в вашей истории, вам, возможно, придется прибегнуть котгадывать на основе содержимого каждого большого двоичного объекта данных или усложнять свой процессор, запоминая двоичные объекты, которые он видел, и решая, какие из них следует перекодировать после того, как он увидел запись commit, которая присваивает имена файла (некоторым из) этих больших двоичных объектов.


¹ Документировано в git-fast-import(1) - запуск git help fast-import.

0 голосов
/ 02 августа 2019

У меня была точно такая же проблема, и решение основано на ответе @kostix об использовании в качестве основы опции --index-filter filter-branch, но с некоторыми дополнительными улучшениями.

  1. Использованиеgit diff --name-only --staged для обнаружения содержимого промежуточной области
  2. Перебрать этот список и выполнить фильтрацию для:
    1. git ls-files $filename, т. Е. Это не удаленный файл
    2. результат git show ":0:$filename" | file - --brief --mime-encoding не binary, т. е. это текстовый файл и кодировка UTF-8 уже не
  3. Использовать обнаруженную кодировку MIME для каждого файла
  4. Используйте iconv для конвертации файлов
  5. Определите режим файла с помощью git ls-files $filename --stage | cut -c 1-6

Это выглядит как моя функция bash:

changeencoding() {
    for filename in `git diff --name-only --staged`; do
        # Only if file is present, i.e., filter deletions
        if [ `git ls-files $filename` ]; then
            local encoding=`git show ":0:$filename" | file - --brief --mime-encoding`
            if [ "$encoding" != "binary" -a  "$encoding" != "utf-8" ]; then
                local sha1=`git show ":0:$filename" \
                    | iconv --from-code=$encoding --to-code=utf-8 \
                    | git hash-object -t blob -w --stdin`
                local mode=`git ls-files $filename --stage | cut -c 1-6`
                git update-index --cacheinfo "$mode,$sha1,$filename" --info-only
            fi
        fi
    done
}
0 голосов
/ 08 июня 2018

Древовидный фильтр в git filter-branch по своей сути медленный.Он работает, извлекая каждый коммит в полноценное дерево во временном каталоге, позволяя вам изменить каждый файл, а затем выясняя, что вы изменили, и делая новый коммит из каждого оставленного вами файла.

Если вы 'При повторном экспорте и импорте с помощью fast-export / fast-import, , что будет временем для преобразования данных: перед записью у вас есть расширенные данные файла в памяти, но не в виде файловой системыэто к экспортно-импортному конвейеру.Более того, git fast-import сам по себе является сценарием оболочки, поэтому вставлять туда фильтрацию тривиально, а hg-fast-export - это программа на Python, поэтому вставлять там фильтрацию тоже просто.Очевидное место будет здесь : просто перекодируйте d.

...