что такое git hooks

Хуки в Git

Как и многие другие системы контроля версий, Git предоставляет возможность запуска пользовательских скриптов в случае возникновения определённых событий. Такие действия называются хуками и разделяются на две группы: серверные и клиентские. Если хуки на стороне клиента запускаются такими операциями как слияние или создание коммита, то на стороне сервера они инициируются сетевыми операциями, такими как получение отправленного коммита. Хуки часто используются для широкого круга задач.

Установка хука

Для подключения собственного скрипта достаточно задать ему соответствующее имя, поместить в подкаталог hooks основного каталога Git и сделать его исполняемым. Далее, мы рассмотрим наиболее часто используемые хуки.

Клиентские Хуки

Для клиента существует множество различных хуков. В этой главе они разделены на хуки уровня коммита, уровня e-mail и прочие.

Необходимо отметить, что клиентские хуки НЕ копируются при клонировании репозитория. Если вы намерены использовать такие скрипты для обеспечения соблюдения политики, то вам следует использовать серверные хуки; например Пример принудительной политики Git.

Хуки уровня коммита

Первые четыре хука работают во время создания коммитов.

Хук prepare-commit-msg запускается до вызова редактора сообщения коммита, но после создания стандартного сообщения. Это позволяет вам изменить стандартное сообщение коммита до того, как автор коммита увидит его. Хук принимает несколько параметров: путь к файлу, содержащему сообщение коммита, тип коммита и SHA-1-хеш, если текущий коммит является исправлением существующего. Для обычных коммитов этот хук бесполезен, однако находит своё применение для коммитов, где сообщение генерируется автоматически, например, для сообщений на основе шаблонов, коммитов слияния, сжимаемых и исправляемых коммитов. Его можно использовать для программного заполнения шаблона коммита необходимой информацией.

Хук commit-msg принимает один параметр — путь к временному файлу, содержащему указанное разработчиком сообщение коммита. Если скрипт завершается с ненулевым кодом, то Git отменяет создание коммита, поэтому вы можете использовать этот хук для валидации состояния проекта или сообщения коммита до того как он будет создан. В последнем разделе этой главы мы покажем как использовать этот хук для проверки сообщения коммита на соответствие заданному шаблону.

Хуки для рабочего процесса на основе E-mail

Прочие хуки на стороне клиента

Хук pre-rebase выполняется при попытке перебазирования и может остановить процесс вернув ненулевой код. Его можно использовать для запрета перебазирования уже отправленных коммитов. Git устанавливается с примером такого скрипта, однако он делает некоторые допущения, которые могут не соответствовать вашему рабочему процессу.

После успешного выполнения git checkout запускается хук post-checkout ; его можно использовать для настройки рабочего каталога в соответствии с требованиями проекта. Например, перемещение в рабочий каталог больших бинарных файлов, которые не должны отслеживаться, автогенерация документации и тому подобное.

Хуки на сервере

В дополнение к хукам на стороне клиента, как системный администратор вы можете использовать несколько важных хуков на сервере для вашего проекта, тем самым обеспечив выполнение практически любой политики. Эти скрипты выполняются до и после отправки на сервер. Pre-хуки могут возвращать ненулевой код в любой момент, что отменит передачу и отправит сообщение об ошибке клиенту; таким образом вы можете реализовать сколь угодно сложную политику.

pre-receive

Хук pre-receive запускается первым при старте получения данных от клиента. Он получает на stdin список отправленных изменений и если завершается ненулевым кодом, то ни одно из них принято не будет. Этот хук можно использовать для того, чтобы убедиться что все изменения можно применить методом перемотки вперёд, а так же для проверки прав доступа.

update

post-receive

Если вы пишете сценарий/хук, который другие должны будут прочитать, используйте длинные версии параметров командной строки; через шесть месяцев вы будете нас благодарить.

Источник

Запуск Git-хуков при помощи pre-commit

Умение работать с системой контроля версий Git — базовый навык для выживания разработчика (на любом языке программирования) в современных реалиях. Система контроля версий — это этакая машина времени для вашего проекта: всегда можно вернуться на любое прошлое состояние проекта, понять когда, как, что и кем менялось.

Интересный факт: согласно Google Trends, во всём мире Git фактически вытеснил другие системы контроля версий. Только в Китае почему-то до сих пор популярен Subversion (46% рынка). К чему бы это?

Git имеет крайне полезную фичу — возможность исполнять произвольный код на многих этапах работы через так называемые хуки. Вот примеры доступных хуков:

Полный список хуков можно посмотреть в документации.

Как это работает? Рассмотрим, например, схему работы хука pre-commit :

Фишка в том, что хуки могут прерывать операции, за которые они отвечают, если что-то идёт не так. Это идеальное место, чтобы запускать какие-нибудь проверки качества кода, например, линтеры, форматтеры или тесты (если они работают быстро, конечно), или проверять сообщение коммита на соответствие конвенциям или на грамматические ошибки.

Пишем Git-хук на bash

Давайте для понимания напишем простой скрипт, который будет использоваться в качестве хука.

В этом примере я использую Linux. Если вы пользуетесь Windows, то большинство описанный вещей будут работать без изменений через Git Bash, который устанавливается вместе с Git for Windows. Возможно, вам только придётся поменять путь до вашего bash в шебанге, как описано в конце этой статьи. Если же вы соберётесь писать в хуках что-то сложное и требующее интеграции с ОС, то, возможно, стоит вместо bash использовать PowerShell.

Создадим пустой репозиторий:

Git уже заботливо создал для нас шаблоны для написания хуков, которые лежат в специальной служебной директории:

Давайте не будем этого делать, а взамен напишем простой скрипт, который будет запускать все нужные нам линтеры и форматтеры:

Попытаемся его закоммитить:

На этот раз всё срабатывает успешно.

У такого ручного способа есть несколько недостатков:

Используем готовый инструмент — pre-commit

Как обычно, для всего уже есть решения. Представляю вашему вниманию pre-commit — умный инструмент для управления Git-хуками.

Установка

Настройка

И допилим примерно до такого состояния:

Теперь включим pre-commit в текущем репозитории.

Рекомендую добавить эту команду в документацию для разработчиков. Это всё, что нужно будет сделать вашим коллегам, чтобы хуки заработали и у них тоже.

Читайте также:  что делать если машина ест много масла

Можно убедиться, что pre-commit заменил наш старый хук на свой:

Проверим, что конфигурационный файл валиден, а заодно и что всё нынешнее содержимое репозитория удовлетворяет описанными правилам:

При первом запуске pre-commit скачает и закэширует все файлы, необходимые для выполнения проверок. Для проверок, которые написаны на Python, будут созданы изолированные виртуальные окружения, так что они никак не будут зависеть от виртуального окружения проекта. Первый раз из-за скачивания зависимостей эта команда может выполняться секунд 30 (в зависимости от скорости интернета), но перезапустите её ещё раз и она завершится за секунду.

Возможно, pre-commit найдёт проблемы или даже исправит некоторые файлы.

Если проверка отработала без ошибок, то конфиг нужно добавить в Git:

Плагины/зависимости для проверок

Использование

Теперь можно вообще забыть про существование pre-commit и просто пользоваться Git как обычно, а pre-commit будет выполнять все описанные проверки, изредка беспокоя вас прерванными операциями, если будут найдены какие-нибудь проблемы. Давайте снова попробуем закоммитить тот сломанный файл с пробелами и кавычками:

Альтернативы pre-commit

Мне проще всего использовать инструменты, написанные на знакомом мне языке, но вообще pre-commit далеко не уникальный инструмент и имеет множество альтернатив. Если вы пишете не на Python, то может быть вам будут ближе другие инструменты, хотя все они имеют примерно схожий функционал:

Возможно, вы также найдете для себя что-то полезное на странице “Awesome Git hooks”.

Заключение

Я всегда стараюсь использовать Git-хуки для запуска всех быстрых проверок. Это не дает мне забывать о проверках, и позволяет быстрее получать обратную связь.

Представьте ситуацию, когда сидишь и ждёшь результатов проверок от CI, которые могут быть достаточно долгими (на проекте, где я сейчас работаю, тесты выполняются 8-10 минут), видишь красный крестик, идёшь посмотреть, что же там сломалось, а там всё почти отлично — тесты прошли, но только flake8 нашёл лишний пробел. Чинишь лишний пробел и снова ждёшь 10 минут, чтобы получить свою зелёную галочку. Дак вот хуки спасают от таких ситуаций, потому что все тривиальные проблемы обнаруживаются за несколько секунд локально на моей машине и никогда не попадают в историю Git.

Настоятельно рекомендую пользоваться Git-хуками. Это позволит вам не тратить время на ерунду, и в итоге быть более эффективным и довольным разработчиком.

Примеры из поста можно найти здесь.

Если понравилась статья, то подпишитесь на уведомления о новых постах в блоге, чтобы ничего не пропустить!

Источник

Git: много хуков полезных и разных

Статья является вольным переводом вот этого материала. Любителям длинных и заумных первоисточников можно сразу читать оригинал.

Когда перед нами ставится задача при изменении кодбейса, например, в Github-репозитории выполнить пересборку/перезапуск какого-нибудь приложения на каком-то нашем окружении, то первое, что приходит на ум в качестве возможного триггера такой пересборки, это предоставляемый тем же гитхабом механизм веб-хуков: при наступлении какого-либо события с нашим удаленным репозиторием (т.к. появление нового коммита в какой-нибудь его отслеживаемой ветке) гитхаб задействует соответствующий веб-хук и «дернет» указанный в его настройках сервис, который и запустит процесс пересборки/перезапуска нашего приложения. Это стандартный широкоиспользуемый и простой механизм для таких случаев, все так делают, и все такое…

Но что, если наше приложение живет на хосте, доступ к которому по каким-то причинам гитхабу не положен? Например, хост находится в закрытой сети или за NAT’ом и недоступен из интернета?

В этом случае можно воспользоваться механизмом локальных хуков самого Git, о котором (механизме), как выясняется, мало кто знает даже из тех, кто использует Git уже довольно продолжительное время.

Полное описание поддерживаемых событий для хуков можно найти, например, тут.

Мы воспользуемся этим механизмом и реализуем простенькую push-to-deploy схему для нашего горемычного приложения.

Нам понадобится два локальных репозитория. Создадим их, например, по указанным путям:

1. /opt/repo-dev/example-repo/
2. /opt/repo-remote.git/

Первый репозиторий — это клон нашего удаленного репозитория example-repo на гитхабе.
Второй — это bare репозиторий, копия первого, который будет служить нам исключительно для обработки события post-update при появлении обновлений в удаленном репозитории. Итак, как же мы это реализуем?

Схема очень проста (предположим, мы отслеживаем ветку test, а приложение наше это node.js, управляемый менеджером pm2):

1. Периодически обновляем первый локальный репозиторий до состояния, полностью соотвествующего состоянию удаленного репозитория.
2. Из первого локального репозитория обновляем второй.
3. Как только HEAD у нас переместился — появился новый коммит — во втором репозитории будет задействован хук post-update, который выполняется при появлении любых изменений в репозитории, и который и выполнит необходимые действия по ребилду и рестарту приложения.

Для этого мы делаем следующее:

1. В первом локальном репозитории добавляем remote — второй локальный репозиторий:

Теперь мы можем выполнять git push из первого локального репозитория во второй.

2. В папке /opt/repo-remote.git/hooks/ создаем файл post-update и делаем его исполняемым:

Что делает скрипт? Сначала просто выгружает working tree нашего bare репозитория в папку с нашим работающим приложением, а затем пересобирает зависимости и рестартует сервисы pm2. Как видите, никакой магии.

3. Настраиваем cron, который каждые n минут будет обновлять первый репозиторий из удаленного:

Т.о. теперь наш хост будет являться регулярным инициатором проверки удаленного репозитория — а не появились ли там обновления?

4. Сразу после синхронизации первого локального репозитория с удаленным мы делаем пуш всех изменений в наш псевдо-удаленный второй локальный репозиторий:

Вот и все. Как только наш псевдо-удаленный локальный репозиторий получает ненулевые изменения, Git дергает свой хук post-update, который выполняет соответствующий скрипт. И мы получаем простенькую рабочую схему push-to-deploy, которую при желании можно и дальше усовершенствовать в соответствии с нашими потребностями.

«Зачем городить такую неудобоваримую монструозную схему?!» — спросите вы. Я сначала задавался этим же вопросом, но оказалось, что с существующим перечнем хуков Git’а только так мы сможем вызвать необходимую обработку при любом обновлении нашей ветки в удаленном репозитории. Нужный нам хук post-update предназначен для выполнения на remote репозитории (а часть хуков предназначена для выполнения на локальном репозитории). И мы таким вот не очень изящным способом это псевдо-удаленный репозиторий и сэмулировали. Возможно в скором времени появится еще какой-нибудь более удобный хук, выполняющийся локально, и схему можно будет упростить. Но пока так.

Читайте также:  что нельзя делать во время тренировок

В заключение хочу дать несколько советов тем, кто решит это реализовать у себя и, столкнувшись с проблемами неработающего хука или каких-то его частей, будет яростно проклинать меня, Git, автора оригинальной статьи и всех остальных по списку:

1. Помните про специфику работы cron — программы в нем по умолчанию запускаются совсем не в том окружении, которое вы, вероятно, ожидаете.
2. Проверяйте версии ваших утилит (npm, node etc) при вызове их из скриптов и по cron — они могут быть не такими, как при ручном запуске из-за различия путей к исполняемым файлам в переменных окружения, например. А запуск других их версий может приводить к непрогнозируемым результатам.
3. Потратьте 20 минут на просмотр очередной серии Simpsons и возвращайтесь к экспериментам с новыми силами и хорошим настроением

Буду рад любым замечаниям и уточнениям по существу.

Источник

Отрабатываем Git hooks на автоматизации commit message

Привет, Хабр! В этой статье я расскажу о Git hooks и о том, как они могут помочь с некоторыми насущными кейсами организации создания commit’ов и commit message. Пост основан на реальном опыте из моей практики: как я упрощал то, что всем надоело делать руками. Я уверен, что хуки могут оказаться полезны почти каждому разработчику. Ведь все мы пишем в сообщении коммита чуть больше, чем «fixed what was broken», верно?

Меня зовут Роман Горбатенко, я Java-разработчик в компании DINS, на момент написания текста тружусь в команде Contact Center. Занимаюсь разработкой больше 3-х лет и прошел путь от личинки стажера до middle девелопера. Считаю Git одним из самых полезных инструментов. Многие не используют его возможности на полную, — надеюсь, мне удастся это немного исправить.

Итак, о проблеме, для затравки

На прошлом месте работы релизный процесс включал в себя ручной сбор commit’ов, относящихся к задачам, которые входят в этот самый релиз (не правда ли, здорово?). В связи с этим, команда корабля решила облегчить себе жизнь и завести правило, по которому commit message должен обязательно (!) предваряться префиксом с названием ветки.

Известно, что любое повторяющееся действие порождает случайные ошибки. И вообще, программисты не любят в повторение, мы любим в оптимизацию. Выход был найден — работу по добавлению префикса возложили на механизм Git hooks.

Ниже я расскажу, как создать prepare-commit-msg хук, немного опишу типы хуков и принципы их работы для тех, кто не знал забыл, расскажу, как завести наконец правило написания сообщений commit’ов и даже его соблюдать. Помимо этого, приведу несколько полезных штук, которые я подметил в процессе исследования и дам ссылки на источники, которые меня вдохновляли.

На всякий случай: я не претендую на экспертность в вопросах Git и Git hooks, некоторые читатели Хабр могут и должны знать больше меня о данной теме. Я буду рад любой помощи и советам в комментариях. Участие в опросах в конце статьи поможет мне при написании следующей статьи о Git.

О самих Git hooks и основных типах

Хуки разделяются на клиентские (локальные) и серверные (удаленные). Локальные хуки не затрагивают мою команду, и их можно оптимизировать под себя как угодно.

На схеме ниже показано время действия части локальных и серверных хуков относительно основных событий — commit и push.

Локальные хуки бывают нескольких типов и срабатывают после создания commit’a:

pre-commit выполняется каждый раз при вызове git commit. На этом этапе можно выполнять различные проверки commit’ов.

prepare-commit-msg выполняется после pre-commit и позволяет работать с commit message (круто, то, что надо!)

commit-msg похож на prepare-commit-msg, но вызывается после того, как вы ввели commit message. Это, например, хорошая возможность высветить предупреждение разработчикам о том, что сообщение commit’a не соответствует принятым стандартам.

post-commit вызывается сразу после commit-msg хука. К примеру, так можно триггерить отправку письма начальнику после каждого commit’a.

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

В статье я последовательно рассмотрю prepare-commit-msg и commit-msg.

Думаю, теории хватит. Наша цель практическая, больше информации вы сможете найти самостоятельно или по ссылкам на источники в конце статьи.

Про Git hooks на Windows

В конце статьи будет дана ремарка на счет работы хуков на Windows, поскольку запуск скриптов написанных на Python немного отличается. За исключением этого момента, содержание статьи верно и для хуков на Windows

Если в процессе прочтения вам не захочется разбираться, как устроены хуки, или удобнее смотреть в код целиком, переходите в GitHub репозиторий с кодом из этой статьи.

Перейдем к имплементации

prepare-commit-msg hook

Для начала нужно определиться, на каком языке можно/нужно писать скрипты, которые будут исполняться как хуки. Строго говоря, писать можно на любом скриптовом языке, будь то Bash, Python, Ruby, Perl, Rust, Swift или Go.

Я пробовал Bash и Python, последний зашел больше, т.к. Bash крайне тяжело поддается с наскока, без опыта чтения/писания на нем. В статье приведены скрипты именно на Python, поскольку я могу ручаться за то, что в них написано.

Обозначать, на каком языке написан скрипт, я буду специальным символом шебанг (#!), за которым следует путь до интерпретатора конкретного языка. В случае с Mac или Linux стандартный путь к интерпретатору для Python представлен /usr/bin.

Для Bash путь чуть другой:

Уже почти время написать наш хук.

Сформулирую задачу: название ветки содержит буквенно-численный номер Jira тикета. Регистр не важен, помимо этого номера название ветки может содержать и другую информацию.

Ниже я реализую скрипт, который из ветки вида «ABC-123-bugfix» вытащит префикс вида «ABC-123», затем из commit message вида «initial commit message» сделает итоговое сообщение «[ABC-123] initial commit message».

Читайте также:  что делать если не выключается fastboot

Механизм работы моего prepare-commit-msg hook

Для лучшего понимания идеи ниже приведена схема, которая повторяет формулировку задачи:

Ниже находится листинг самого скрипта prepare-commit-msg hook:

Разберу построчно, что же тут происходит.

Lines 3-5 — блок импортов.

Line 7 — получение файла, в который сохраняется изначальное commit message.

Lines 8-10 — получаем имя ветки.

Line 13 — регулярное выражение, по которому я сверяю, что имя ветки содержит название Jira-тикета вида ABC-123.

Lines 15 — по регулярке получаем сам префикс (ветка может помимо тикета содержать что-то еще, отсекаем лишнее).

Lines 17-18 — читаем текст commit message.

Line 21 — проверяю, что сообщение коммита уже не содержит префикс, который я собираюсь добавлять.

Line 22 — устанавливаем «каретку» в начало текстового файла, у нас ведь префикс.

Line 23 — заключаем наш префикс в [ ] перед исходным сообщением.

Дополнительно отмечу лишь один момент — проверка на наличие префикса необходима, поскольку редактирование commit message средствами той же Intellij Idea приводит к повторному появлению префикса. Я совсем этого не хочу.

Результат выполнения prepare-commit-msg hook

Создам commit с сообщением и увижу в результате префикс, который добавлен хуком:

Результат команды git log для последнего commit:

commit-msg hook

На примере данного хука я продемонстрирую еще один способ автоматизации commit message.

Представим, что тимлид устал читать одинаково бессмысленные сообщения коммитов других разработчиков и решил группировать их по ключевым словам. Для этого он предложил ввести правило — писать в сообщении, о чем коммит — Fix, Update, Rework и так далее.

С этой задачей отлично справится Git hook — он будет сообщать разработчикам об ошибке в случае, если они забыли добавить в сообщение ключевое слово.

Механизм работы: проверяем наличие ключевого слова в тексте сообщения коммита. Если совпадение найдено, хвалим программиста, если нет, напоминаем ему о забывчивости и отменяем создание коммита.

Результат выполнения commit-msg hook

В случае отсутствия одного из «тэгов» хук сообщает, что нам нужно его добавить и НЕ создает коммит. Также он сообщает, что этот механизм можно обойти.

В случае, если тэг присутствует, хук хвалит разработчика за заботу о нервах тимлида.

Включаем хуки

Как я уже отмечал выше, работа Git hooks на Windows немного отличается, потому рассмотрим в отдельности системы.

Для начала текст скриптов описанных выше нужно скопировать в файл с соответствующим именем, без расширения.

Mac OS и Linux

1. Сделать файл хука исполняемым. Это очень просто:

2. Поместить файл хука в папку проекта или общую папку.

После создания хука нужно сделать так, чтобы Git для вашего проекта мог им воспользоваться. Существует 2 способа добавить хуки на исполнение:

Можно представить, как это неудобно — каждый раз для нового проекта добавлять хуки заново. Я вроде бы хотел избавиться от повторяющихся действий, разве нет?

B. Существует и второй способ, более элегантный, если подразумевается, что ваши хуки применимы для всех проектов. Нужно добавить в глобальный конфиг Git параметр core.hooksPath, значение которого содержит путь до глобальной папки с хуками, откуда Git будет все тянуть в первую очередь.

Команда довольно простая и многим знакомая:

Проверим, что конфиг сохранился:

И увидим в списке глобальных переменных что-то вроде:

Таким образом, все старые и новые проекты будут пользоваться преимуществами добавленных в общую директорию хуков.

3. Git hooks добавлены и готовы к работе. Вы восхитительны!

Windows

Все приведенное выше так же верно для хуков на Windows за исключением того, что скрипт написанный на Python не может так просто запускаться в качестве Git hook, поскольку Windows не может идентифицировать интерпретатор Python с помощью шебанга.

Выход есть — можно сделать shell скрипт, который будет запускать Python скрипт. Нужно всего-то несколько действий:

1. Сделать собственно сам shell скрипт:

Аргумент COMMIT_MSG_FILE содержит путь до временного текстового файла, который содержит commit message, нам нужно передать этот путь дальше Python скрипту, который выполнит основную работу.

2. Убедиться, что shell скрипт исполняемый, его имя соответствует названию хука и не содержит расширения.

3. Нужно удалить шебанг для интерпретатора Python из скрипта (например, у меня это prepare-commit-msg.py) и поместить сам Python файл рядом с самим Git hook.

Полезные замечания

Смысл решения в том, чтобы держать описанные выше хуки локально. Не думаю, что добавлять хуки в репозиторий и версионировать их — хорошая идея. На это есть причины:

Может случиться так, что программист не будет знать, что у него под капотом орудует хук и что-то правит.

Пути до интерпретаторов разные на разных системах (Python на Windows запросто может не быть), значит нельзя гарантировать, что хуки будут работать у всех разработчиков в команде из коробки.

Добавлять хуки во много проектов = превратить их поддержку в итеративный ад (я все еще хочу избежать повторяющихся действий).

Думаю, читатель уже догадался, что для работы Git hooks на Windows (и некоторых сборок Linux) понадобится установить Python 3, на Mac OS он имеется по умолчанию. Проверял работоспособность на версии 3.9, но и с более ранними проблем не возникнет.

Для того, чтобы адаптировать префикс под свои нужды, нужно в Python скрипте изменить итоговое сообщение, которое пишется в файл commit message.

Полезные ссылки

Обязательно зацените GitHub репозиторий с хуками из этой статьи.

Довольно подробная документация Atlassian, где можно подробнее узнать о всех типах хуков, посмотреть идеи для использования и примеры реализации на Python.

Неплохая статья с примерами на Bash, отсюда я взял пару идей, это помогло на начальном этапе.

Для всех начинающих, забывчивых или юзающих Git только через любимую IDE — вот отличный тренажер для развития понимания Git.

Вместо заключения

Это моя первая статья и мне не терпится поделиться знаниями с аудиторией Хабра. Если статья зайдет, я пойму, что можно продолжить разговор и придумать еще интересные кейсы использования хуков. Возможно, вы сможете подкинуть идеи в комментариях.

Источник

Строительный портал