Генерация commit message из diff нейросетью
Хук prepare-commit-msg и скрипт на Python, которые предлагают сообщение коммита в стиле Conventional Commits по staged diff. Вы правите его в редакторе.
5 мин · · разработка · автоматизация
Генерация commit message из diff — одна из самых простых и полезных автоматизаций для разработчика: модель читает staged-изменения и предлагает сообщение в формате Conventional Commits, а вы правите его в привычном редакторе. Ниже — скрипт на Python, git-хук, обработка больших diff и честный расчёт стоимости.
Что получится
git commitбез-mоткрывает редактор, где первой строкой уже стоит черновик вродеfix(auth): handle expired refresh token.git commit -m "...", merge,--amend— хук не вмешивается.- Если API недоступен или ответ не похож на Conventional Commits, коммит идёт как обычно, с пустым шаблоном.
Генератор подсказывает, а не решает: последнее слово остаётся за автором коммита.
Скрипт генерации
Положите файл в репозиторий как scripts/generate_commit_msg.py. Нужен pip install openai и ключ fetch_... в переменной OPENAI_API_KEY.
#!/usr/bin/env python3
import os
import re
import subprocess
import sys
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["OPENAI_API_KEY"],
)
MODEL = os.environ.get("COMMIT_MSG_MODEL", "glm-5.3-flash")
MAX_DIFF_TOKENS = 3000Почему так:
- diff идёт через stdin, а не аргументом: большой diff в аргументе упирается в лимит длины командной строки.
--statв промпте даёт модели полный список файлов, даже если сам diff обрезан.- Проверка первой строки регулярным выражением. Если модель ответила рассуждением вместо сообщения, скрипт завершится с ошибкой, а хук просто ничего не подставит.
- Модель задаётся переменной
COMMIT_MSG_MODEL, чтобы сравнить варианты без правки кода.
Хук prepare-commit-msg
По документации git, prepare-commit-msg вызывается после подготовки стандартного сообщения и до запуска редактора. Второй аргумент — источник сообщения: message (при -m или -F), template, merge, squash или commit (при -c, -C, --amend). При обычном git commit он пустой — только тогда и генерируем.
Важно: файл сообщения в этот момент уже не пустой — в нём закомментированный шаблон git. Поэтому проверка «файл пустой» не подходит: черновик нужно дописать в начало файла.
Файл .githooks/prepare-commit-msg:
#!/bin/sh
COMMIT_MSG_FILE="$1"
COMMIT_SOURCE="$2"
[ -n "$COMMIT_SOURCE" ] && exit 0
[ -n "$SKIP_AI_MSG" ] && exit 0
MESSAGE=$(git diff --cached | python3 scripts/generate_commit_msg.py) || exit 0
[ -z "$MESSAGE" ] && exit 0
{ printf '%s\n' "$MESSAGE"; cat "$COMMIT_MSG_FILE"; } > "$COMMIT_MSG_FILE.tmp"
mv "$COMMIT_MSG_FILE.tmp" "$COMMIT_MSG_FILE"Подключение для всей команды (папка .git/hooks не попадает в репозиторий, а .githooks — попадает):
chmod +x .githooks/prepare-commit-msg
git config core.hooksPath .githooksGit запускает хуки из корня рабочей копии, поэтому относительный путь scripts/generate_commit_msg.py работает из любой подпапки. Хук всегда завершается с кодом 0: ошибка генерации не должна блокировать коммит.
Ручной запуск без хука
Если хук не нужен, сгенерируйте сообщение и откройте его в редакторе перед коммитом:
git diff --cached | python3 scripts/generate_commit_msg.py > /tmp/ai_msg && git commit -e -F /tmp/ai_msgФлаг -e открывает редактор, -F берёт текст из файла. Поскольку источник здесь message, хук второй раз генерацию не запустит.
Большие diff
Обновлённый lock-файл или сгенерированный код легко дают diff на десятки тысяч строк. Отправлять всё целиком бессмысленно: выходит дорого, а сообщение коммита всё равно описывает суть, а не каждую строку. smart_truncate оставляет заголовки файлов и ханков (diff --git, @@) и сколько помещается содержимого в бюджет около 3000 токенов, остальное отрезает с пометкой.
Ещё два приёма:
- исключайте шумные файлы прямо в git:
git diff --cached -- . ':(exclude)package-lock.json'; - для огромных коммитов честнее разбить их на несколько логических — модель тут не поможет.
Сколько это стоит
Допущения: промпт с правилами и --stat — около 300 токенов, средний diff после обрезки — около 1700, итого 2000 токенов входа; ответ — около 60 токенов. На 1000 коммитов это 2 000 000 входных и 60 000 выходных токенов.
| Модель | Формула на 1000 коммитов | Итого |
|---|---|---|
glm-5.3-flash ($0.04 / $0.04) | (2 000 000 × 0.04 + 60 000 × 0.04) / 1 000 000 | ≈ $0.08 |
claude-sonnet-5 ($0.3 / $0.3) | (2 000 000 × 0.3 + 60 000 × 0.3) / 1 000 000 | ≈ $0.62 |
Худший случай — diff упёрся в лимит 3000 токенов: около 3300 входа и 150 выхода, то есть (3300 + 150) × 0.04 / 1 000 000 ≈ $0.00014 за коммит на glm-5.3-flash.
Команда из 10 человек по 5 коммитов в день — 50 коммитов в день, около 1100 в месяц (22 рабочих дня): примерно $0.09 в месяц на glm-5.3-flash или $0.68 на claude-sonnet-5. Какая модель пишет сообщения, которые вы реже правите, проверьте сами: прогоните скрипт с COMMIT_MSG_MODEL на 20–30 последних коммитах и сравните с тем, что написали люди. Актуальный список моделей — GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете.
Для хука удобно завести отдельный ключ: в кабинете можно ограничить его списком разрешённых моделей и дневным лимитом в долларах — подробнее в разделе о лимитах ключей. Создайте ключ, а общий порядок подключения смотрите в быстром старте.
Частые вопросы
Хук не срабатывает. Что проверить?
git config core.hooksPathуказывает на папку с хуком, а файл исполняемый (chmod +x).- Вы коммитите без
-m: с-mисточник сообщенияmessage, и хук намеренно выходит. OPENAI_API_KEYвиден в том окружении, где запускается git. GUI-клиенты и IDE часто не читают ваш.zshrcили.bashrc.- Запустите скрипт вручную:
git diff --cached | python3 scripts/generate_commit_msg.py— ошибка будет видна в stderr.
Как временно отключить генерацию?
SKIP_AI_MSG=1 git commit — хук проверяет эту переменную. Флаг --no-verify тут не поможет: по документации git он не отключает prepare-commit-msg, только pre-commit и commit-msg.
Модель путает feat и fix. Как поправить?
Добавьте в PROMPT правила вашей команды: «feat — новое поведение для пользователя; fix — исправление ошибки; refactor — без изменения поведения; только тесты — test; только документация — docs». Два-три примера из истории вашего репозитория работают лучше длинных объяснений.
Как подключить это через Husky?
В Husky v9 хук — обычный shell-скрипт в папке .husky, без строки подключения husky.sh из старых версий. Создайте .husky/prepare-commit-msg с тем же содержимым, что и в примере выше; core.hooksPath Husky настраивает сам.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Перевод субтитров нейросетью: SRT без сбитых таймкодовГлавный риск при переводе SRT нейросетью — сломанная структура файла. Показываем схему, где таймкоды не попадают к модели, и скрипт на Python с проверкой.
- Редактирование текстов нейросетью: корректура, стиль и сокращениеРазные виды редактуры требуют разных промптов. Показываем формулировки для корректуры, стиля и сокращения, как получать правки с объяснением и не терять авторский голос.
- Нейросеть для написания скриптов: Bash, PowerShell и PythonМодель быстро пишет скрипты для рутины, но запускать их вслепую нельзя. Показываем, как ставить задачу, требовать безопасный режим dry-run и проверять результат до запуска.
- Dify в Docker: установка, первичная настройка и подключение моделейСтавим Dify через Docker Compose, создаём администратора и подключаем модели Claude, GPT и DeepSeek по одному ключу. Отдельно — что делать, если контейнеры не стартуют.