Как писать понятные сообщения коммитов в Git: регистр, длина, формат

Чистое сообщение коммита в Git состоит из короткой темы (ориентир — 50 символов) в повелительном наклонении, то есть «Add», а не «Added», и без точки в конце. Дальше идёт пустая строка, а за ней тело с переносом на 72 символах, которое объясняет, зачем сделано изменение. Что касается регистра, выберите одно соглашение и держитесь его: с префиксом вроде feat: или docs: слово после него обычно пишут строчным; без префикса классический совет — начинать с заглавной. Эти соглашения, в том числе о регистре, сложились для сообщений на английском.
Сам Git ничего из этого не проверяет, поэтому история со временем расползается. Ниже собраны правила, источник каждого из них и способы заставить инструменты проверять всё за вас, чтобы не держать это в голове.
Из чего состоит хорошее сообщение коммита
fix(editor): keep cursor position after paste
Pasting into an empty textarea moved the cursor to the start, because
the change handler replaced the value before restoring the selection.
Restore the selection after updating the value instead.
Fixes #142Здесь три части: тема, которая говорит, что делает коммит, тело, которое объясняет зачем, и необязательные футеры для ссылок и метаданных. Собственная документация Git советует начинать с «a single short (no more than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description» (одной короткой строки, не длиннее 50 символов, с кратким описанием изменения, затем пустой строки и более подробного описания). Всё до первой пустой строки считается заголовком коммита, именно его показывают git log --oneline, git shortlog и git format-patch.
Тема: коротко, в повелительном наклонении, без точки
Пятьдесят символов — мягкий лимит. Собственное руководство SubmittingPatches проекта Git называет его именно так и просит авторов «skip the full stop» (не ставить точку в конце). Если уложиться в 50 не получается, это часто признак того, что коммит делает сразу две вещи.
Тот же документ просит «describe your changes in imperative mood» (описывать изменения в повелительном наклонении), как будто вы отдаёте приказы кодовой базе: «make xyzzy do frotz», а не «makes xyzzy do frotz» и не «changed xyzzy to do frotz». Удобная проверка из семи правил сообщений коммитов от Chris Beams: хорошая тема продолжает фразу «If applied, this commit will …» (если применить, этот коммит …).
- «If applied, this commit will add CSV export» — подходит.
- «If applied, this commit will added CSV export» — не подходит.
- «If applied, this commit will bug fixes» — тоже не подходит и ничего не говорит читателю.
Повелительное наклонение совпадает и с сообщениями, которые Git пишет за вас, например Merge branch 'main' и Revert "…", поэтому ваши коммиты читаются в журнале в том же ключе.
Регистр темы: два лагеря
Здесь советы действительно расходятся, и верный ответ зависит от того, начинаются ли ваши темы с префикса.
| Соглашение | Пример | Кто рекомендует |
|---|---|---|
| Без префикса, с заглавной | Add CSV export | Заметка Tim Pope 2008 года о сообщениях коммитов; семь правил Chris Beams |
| Префикс области, после него строчная | doc: clarify sign-off rules | SubmittingPatches проекта Git |
| Префикс типа, описание строчными | feat: add CSV export | Руководство Angular по коммитам; conventional-конфигурация commitlint |
В варианте проекта Git есть деталь, которую стоит перенять. Заглавную теряет только первое слово после префикса, и только потому, что оно первое: слова, которые всегда пишутся с заглавной, остаются как есть. Его собственный пример — refs: HEAD is also treated as a ref, ведь HEAD всегда пишется заглавными. Та же логика даёт fix: handle iOS paste и docs: update README badges.
Наша позиция такая: с префиксом пишите первое слово строчным, если только это не имя или аббревиатура. Без префикса пишите его с заглавной. Единственная настоящая ошибка — мешать стили, потому что git log --oneline превращает ваши темы в список, а список, в котором регистр меняется через несколько строк, выглядит небрежно. О том же применительно к документации читайте в нашем руководстве по единообразной документации.
Что на самом деле говорит спецификация Conventional Commits
Спецификация Conventional Commits 1.0.0 задаёт такую структуру:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]Главные правила вкратце:
feat(новая функциональность) иfix(исправление ошибки) — два типа, которые определяет спецификация, и они соответствуют релизам MINOR и PATCH в Semantic Versioning, а изменения, ломающие совместимость, — MAJOR. Другие типы допускаются; спецификация упоминаетbuild,chore,ci,docs,style,refactor,perfиtest, как рекомендует conventional-конфигурация commitlint, основанная на соглашении Angular.- Изменение, ломающее совместимость (breaking change), отмечается знаком
!перед двоеточием (feat!: drop Node 16 support) или футеромBREAKING CHANGE:, причём этот футер должен быть написан заглавными буквами. - Инструменты, которые читают этот формат, не должны считать его чувствительным к регистру, кроме
BREAKING CHANGE. На вопрос, писать ли типы строчными или заглавными, FAQ спецификации отвечает, что можно использовать любой регистр, «but it's best to be consistent» (но лучше быть последовательным).
Спецификация не задаёт длину темы и не требует повелительного наклонения. Это пришло из соглашений Git.
Где commitlint строже спецификации
@commitlint/config-conventional — привычный способ проверять этот формат, и его настройки по умолчанию идут дальше спецификации. Мы прогнали примеры тем через версию 21:
| Тема | Результат |
|---|---|
feat: add CSV export | Проходит |
feat: Add CSV export | Не проходит: тема не должна быть в «Регистре предложения» (sentence case) |
Feat: add CSV export | Не проходит: тип должен быть строчными буквами |
fix: handle empty textarea. | Не проходит: точка в конце не допускается |
fix: README typo | Не проходит: тоже считается «Регистром предложения» |
fix: typo in README | Проходит |
За строкой с README стоит последить внимательнее всего. Тема, которая начинается с аббревиатуры или имени класса, например fix: README typo или feat: URLSearchParams support, отклоняется, хотя проект Git принял бы такую же. Переформулируйте так, чтобы аббревиатура не стояла первой, или ослабьте правило subject-case в конфигурации. Лимит заголовка по умолчанию — 100 символов, так что и 50 commitlint от вас не потребует.
Тело: перенос на 72 символах и объяснение «зачем»
Git никогда не переносит строки вашего сообщения сам. Объяснение Tim Pope 2008 года остаётся в силе: git log сдвигает сообщения на четыре пробела, поэтому на терминале в 80 столбцов ещё четыре для симметрии справа оставляют 72 столбца, а текст с переносами остаётся читаемым и тогда, когда коммиты отправляют по почте через git format-patch. Если ваш редактор так не умеет, наш инструмент «Перенос текста на N символов» перенесёт абзац на 72 символах по границам слов.
Используйте тело для того, чего не видно в диффе: проблема, причина именно такого исправления и всё, что вы пробовали, но не сработало. В командной строке каждый -m становится отдельным абзацем, поэтому git commit -m "fix: keep cursor after paste" -m "Pasting into…" даёт тему, пустую строку и тело.
Пусть проверяют инструменты
- VS Code: задайте
"git.inputValidation": true. Тогда поле коммита предупреждает, когда тема длиннее 50 символов (git.inputValidationSubjectLength) или любая другая строка длиннее 72 (git.inputValidationLength). - Vim: его встроенные настройки для сообщений коммитов переносят тело на 72 символах (
textwidth=72) и перестают подсвечивать тему после 50 символов. - Шаблон коммита:
git config commit.template ~/.gitmessageоткрывает каждый коммит в редакторе с вашими напоминаниями. - Хук commit-msg для проверок без зависимостей. Сохраните это как
.git/hooks/commit-msgи сделайте файл исполняемым:
#!/bin/sh
# Checks the subject line of every commit message.
subject=$(head -n 1 "$1")
length=${#subject} # counts bytes in some shells; fine for ASCII subjects
if [ "$length" -gt 72 ]; then
echo "commit-msg: subject is $length characters (limit 72, aim for 50)" >&2
exit 1
fi
if [ "$length" -gt 50 ]; then
echo "commit-msg: note, subject is $length characters (aim for 50)" >&2
fi
case "$subject" in
*.) echo "commit-msg: drop the full stop at the end of the subject" >&2
exit 1 ;;
esac
if [ -n "$(sed -n 2p "$1")" ]; then
echo "commit-msg: leave line 2 blank, between subject and body" >&2
exit 1
fiХук предупреждает, когда в теме больше 50 символов, отклоняет темы длиннее 72, точку в конце и отсутствие пустой строки, а git commit --no-verify пропускает его, когда это действительно нужно. Чтобы быстро посчитать символы перед коммитом, вставьте тему в наш счётчик символов.
Итак, памятка: тема в повелительном наклонении, около 50 символов, без точки, одно правило регистра на весь репозиторий, пустая строка, тело с переносом на 72 символах, которое объясняет «зачем», и хук или линтер, чтобы правила пережили напряжённую неделю.


