← Блог · · Команда ConvertCaseHQ · 7 мин чтения

Как писать понятные сообщения коммитов в 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 rulesSubmittingPatches проекта 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 символах, которое объясняет «зачем», и хук или линтер, чтобы правила пережили напряжённую неделю.