GIT pre-commit hook, который ищет не-UTF-8 кодировки среди измененных / добавленных файлов (и отклоняет коммит, если найдет) - PullRequest
1 голос
/ 12 апреля 2019

Я использую Git для Windows (и TortoiseGit).

Моя цель состоит в том, чтобы предотвратить коммиты, у которых есть хотя бы один файл не-UTF-8 среди измененных / добавленных.

  • Перечисление измененных / добавленных файлов: Я нашел следующий код

    { git diff --name-only ; git diff --name-only --staged ; }
    

    Является ли это лучшим (правильным и кратким)подход?

  • Поиск файлов не-UTF-8: Я нашел следующий код

    { git diff --name-only ; git diff --name-only --staged ; } | xargs -I {} bash -c "iconv -f utf-8 -t utf-16 {} &>/dev/null || echo {} - is non-UTF8!"
    

    Если я запускаю Git Bashв моей корневой папке репозитория - это работает (отображается каждый файл не-UTF-8).Поэтому я переименовал .git/hooks/pre-commit.sample в .git/hooks/pre-commit и скопировал код выше.После внесения изменений ничего особенного не отображается в окне графического интерфейса TortoiseGit.Таким образом, похоже, что ловушка предварительной фиксации работает неправильно.

  • Отклонение фиксации, если есть какой-либо файл не-UTF-8: После отображения всех не-UTP-Прием 8 файлов должен быть отклоненНо я понятия не имею, как это сделать (показать код выхода - но как?).

Так что любая помощь приветствуется.

Ответы [ 2 ]

2 голосов
/ 12 апреля 2019

Таким образом, ответ (thx на phd и большое спасибо на torek за его полезные заметки):

    git diff --name-only --staged --diff-filter d | xargs -I {} bash -c 
 "iconv -f utf-8 -t utf-16 {} &>/dev/null || { echo {} - is non-UTF8!; exit 1; }"

Этот код перебирает все файлы, которые были изменены в коммите (кроме удаленных - то есть добавленных, модифицированных, скопированных и переименованных), и проверяет, существует ли файл не-UTF8.Все найденные файлы перечислены и фиксация отменена.

1 голос
/ 12 апреля 2019

Ваше существующее решение, вероятно, достаточно.Хотя это не на 100% правильно: вот оставшиеся проблемы, все из которых являются незначительными, которые вы можете исправить позже (если когда-либо) на досуге:

  • Вам нужны только git diff ... --staged (или --cached), поскольку Git будет фиксировать все файлы в области index / staging-области, а git diff сравнивает это с тем, что находится в коммите HEAD, и сообщает вам, что там происходит.Если копия файла в индексе отличается от копии файла в HEAD, вам следует изучить копию индекса.

  • Технически было бы лучше использовать git diff-index --cachedздесь, чтобы не подчиняться какой-либо конфигурации пользователя git diff.Таким образом, git diff-index - это команда plumbing в Git, что означает, что она предназначена для использования из других компьютерных программ: она выполняется полностью предсказуемым образом, основываясь только на аргументах, а не на любых настройках git config,Но если вы делаете это для себя, и вы настраиваете git diff таким образом, что это нарушает ваше собственное использование git diff, то это ваша собственная ошибка.: -)

  • Вы также можете использовать --diff-filter для исключения удаленных файлов.В противном случае ваша программа проверки всегда удастся удалить (так как iconv не сможет прочитать удаленный файл).

  • Наиболее важно: iconv будет считывать файл из работа дерево .Как я отметил в первом пункте, Git собирается зафиксировать то, что staged , а не то, что находится в рабочем дереве.

В качестве примера, который может илиможет быть невозможным изнутри TortoiseGit - подумайте, что произойдет, если вы сделаете следующее:

$ git checkout master
$ printf '\300\300\300' > badfile    # put bad non-UTF-8 crud into file
$ git add badfile                    # copy file into index
$ echo 'good data' > badfile         # replace work-tree contents
$ git commit

Этот коммит будет фиксировать плохое содержимое - три байта \300 безсимвол новой строки - который находится в индексе, но ваш хук перед фиксацией будет запускать iconv -f utf-8 -t utf-16 над содержимым файла good , читая good data, что, конечно, хорошо.

Чтобы исправить это, ваш фильтр предварительной фиксации должен извлечь данные из индекса для каждого файла, который должен быть зафиксирован.Как вы поступите так, зависит от вас.Самый простой (но, возможно, самый медленный) метод - просто извлечь все содержимое индекса во временную рабочую область, используя git checkout-index.Лучшим методом может быть преобразование каждого имени пути в индексной области (in-staging-area) в допустимый спецификатор индекса (то есть path/to/file становится :path/to/file) и использование git cat-file -p $specifier | iconv ... для сканирования каждого.Но все это будет довольно неэффективно, особенно в Windows.Для эффективности вы можете написать скрипт на Python, который использует git cat-file --batch, чтобы извлечь их все за один проход, и выполнить проверку формата там.

...