Введение. Почему важно оставлять хорошие сообщения к коммитам

Если заглянуть в логи любого репозитория Git, в сообщениях к коммитам, скорее всего, будет твориться бардак. Посмотрите, например, как я сам блистательно писал сообщения, когда только начинал работать в Spring:

$ git log --oneline -5 --author cbeams --before "Fri Mar 26 2009"
e5f4b49 Re-adding ConfigurationPostProcessorTests after its brief removal in r814. @Ignore-ing the testCglibClassesAreLoadedJustInTimeForEnhancement() method as it turns out this was one of the culprits in the recent build breakage. The classloader hacking causes subtle downstream effects, breaking unrelated tests. The test method is still useful, but should only be run on a manual basis to ensure CGLIB is not prematurely classloaded, and should not be run as part of the automated build.
2db0f12 fixed two build-breaking issues: + reverted ClassMetadataReadingVisitor to revision 794 + eliminated ConfigurationPostProcessorTests until further investigation determines why it causes downstream tests to fail (such as the seemingly unrelated ClassPathXmlApplicationContextTests)
147709f Tweaks to package-info.java files
22b25e0 Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils
7f96f57 polishing

Гадость, да? А теперь для сравнения взгляните, как выглядят мои недавние коммиты в том же репозитории.

$ git log --oneline -5 --author pwebb --before "Sat Aug 30 2014"
5ba3db6 Fix failing CompositePropertySourceTests
84564a0 Rework @PropertySource early parsing logic
e142fd1 Add tests for ImportSelector meta-data
887815f Update docbook dependency and generate epub
ac8326d Polish mockito usage

Какой вариант лучше читается?

В первом случае сообщения сильно варьируются по длине и содержанию, а во втором они краткие и последовательные. Первый вариант я написал, не задумываясь, а вот второй – это результат целенаправленных усилий.

Обычно логи репозиториев похожи на первый вариант, но бывают и исключения. Ядро Linux и сам Git – примеры отличных логов. Также всё в порядке с сообщениями в Spring Boot и любом репозитории под управлением Тима Поупа.

Контрибьюторы этих репозиториев понимают, что правильно составленное сообщение к коммиту – это лучший способ объяснить контекст изменений другим разработчикам (и себе в будущем). Diff даёт понять, что изменилось, но только сообщение коммиту разъясняет, почему. Питер Хаттерер хорошо об этом написал:

Восстанавливать контекст для каждого фрагмента кода – это трата времени. Впрочем, совсем без этого не обойтись. Но нужно стремиться к тому, чтобы другие разработчики при виде нашего кода как можно реже восклицали «Что за фигня тут творится?!». По сути, по сообщениям коммитов видно, насколько хорошо разработчик умеет работать в команде.

Если вы еще ни разу не задумывались, как выглядит хорошее сообщение коммита, то, вероятно, вы особо не пользовались git log и схожими инструментами. Это порочный круг: поскольку история коммитов не структурирована и непоследовательна, разработчик мало ей пользуется и не тратит силы на её улучшение. А раз историю никто не использует и не улучшает, она так и остается неструктурированной и непоследовательной.

Но если вести историю старательно и аккуратно, она превращается в красивую и очень полезную штуку. И тут на помощь приходят git blame, revert, rebase, log, shortlog и другие команды. Внезапно оказывается, что в изучении чужих коммитов и запросов на принятие изменений (pull requests) есть смысл, и делать это можно самостоятельно. Становится понятно, почему что-либо было сделано несколько месяцев или лет назад, и это здорово помогает в работе.

Долгосрочный успех любого проекта зависит, среди прочего, от того, насколько удобно его поддерживать. А у специалиста, который будет заниматься этой поддержкой, вряд ли найдется инструмент мощнее, чем лог проекта. Есть смысл потратить время и научиться правильно ухаживать за логом. Сначала это будет казаться лишней морокой, но потом войдет в привычку, и лог станет поводом для гордости и источником продуктивной работы всех участников проекта.

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

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

Аналогично нужно действовать и с логами коммитов. Чтобы создать полезную историю изменений, команда разработчиков должна оговорить как минимум три момента:

Стиль. Синтаксис разметки, отступы при переносе, грамматика, заглавные буквы, пунктуация. Обсудите все эти вещи, устраните неясности и упростите все по максимуму. На выходе получится упорядоченный лог, который приятно читать. И его действительно будут читать регулярно.

Контент. Какую информацию нужно писать в сообщение коммита? А что писать не стоит?

Метаданные. Как следует ссылаться на задачи в трекерах, номера запросов на принятие изменений (pull request) и т.д.?

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

7 правил отличного сообщения коммита

Имейте ввиду: Всё это уже не раз обговорено.

  1. Отделяйте заголовок от тела сообщения пустой строкой
  2. Уместите заголовок в 50 знаков или меньше
  3. Пишите заголовок с заглавной буквы
  4. Не ставьте точку в конце заголовка
  5. Используйте повелительное наклонение в заголовке (Примечание: этот совет подходит для английского языка. По-русски лучше использовать глаголы третьего лица единственного числа в прошедшем времени, например, «сделала», «исправил» и т.д.)
  6. Ограничьте длину строки в теле сообщения 72 знаками
  7. В теле сообщения указывайте, что и почему было сделано, а не как.

Например:

Резюмируйте изменения, используя не более 50 знаков

Дайте более подробные объяснения, если необходимо. Ограничьтесь максимум 72 знаками. В некоторых случаях первая строчка считается заголовком, а всё остальное – телом сообщения. Пустая строка между кратким заголовком и телом сообщения чрезвычайно важна (за исключением случаев, когда вы ограничиваетесь только заголовком). Без нее инструменты log, shortlog и rebase могут работать некорректно.

Объясните, зачем сделан этот коммит. Акцентируйте внимание на том, зачем вы внесли эти изменения, а не как (это понятно из кода). Есть ли у внесенных изменений побочные эффекты или другие неожиданные последствия? Опишите их тут.

Следующие параграфы также отделяются пустой строкой.

  • Можно использовать списки
  • Обычно для маркировки списка ставят дефис или звездочку после одного пробела в начале строки, а пункты списка отделяются пустой строкой, но возможны и другие варианты.

Если вы используете таск-трекеры, поставьте ссылки на них внизу сообщения, вот таким образом:

Resolves: #123 See also: #456, #789

1. Отделяйте заголовок от тела сообщения пустой строкой

Из руководства по git commit:

Хоть это и необязательно, рекомендуется начать сообщение коммита кратким описанием внесенных изменений (не более 50 знаков). Далее следует пустая строка, а затем более подробное описание. Текст перед первой пустой строкой считается заголовком сообщения коммита, который используется во всех операциях Git. Например, Git-format-patch(1) превращает коммит в письмо, в тему которого помещается заголовок, а в тело – оставшаяся часть сообщения коммита.

Для начала важно отметить, что не у всякого сообщения коммита должен быть и заголовок, и тело. Иногда достаточно одной строчки, особенно если внесенные объяснения достаточно простые, чтобы не разъяснять их подробно. Например:

Исправил опечатку во введении к руководству пользователя

Больше и нечего добавить. Если читательнице интересно, что там была за опечатка, она может просто посмотреть изменение с помощью git show, git diff, git log -p.

Если вы коммитите что-то похожее в командной строке, к git commit легко можно добавить флаг –m:

$ git commit -m "Исправил опечатку во введении к руководству пользователя"

Однако если коммит требует более подробного объяснения и контекста, нужно писать тело сообщения. Например:

Выпилил Программу Мастер-Контроля

ПМК оказалась воплощением зла, жаждущим мирового господства. Этим коммитом мы бросаем диск Трона в ПМК (вызывая её обнуление), и она превращается обратно в шахматную программу.

Если у сообщения коммита есть тело, его не так просто написать с помощью флага -m. Большие сообщения лучше писать в текстовом редакторе. Если у вас еще нет редактора, настроенного для работы с Git в командной строке, прочтите этот раздел Pro Git.

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

$ git log
commit 42e769bdf4894310333942ffc5a15151222a87be
Author: Kevin Flynn <kevin@flynnsarcade.com>
Date:   Fri Jan 01 00:00:00 1982 -0200

    Выпилил Программу Мастер-Контроля

    ПМК оказалась воплощением зла, жаждущим мирового господства. Этим
    коммитом мы бросаем диск Трона в ПМК (вызывая её обнуление), и она
    превращается обратно в шахматную программу.

А теперь используйте команду git log --oneline, и она покажет только заголовок:

$ git log --oneline

42e769 Выпилил Программу Мастер-Контроля

Или git shortlog, которая группирует коммиты по авторам и для краткости показывает только заголовки:

$ git shortlog
Kevin Flynn (1):
      Выпилил Программу Мастер-Контроля
Alan Bradley (1):
      Запустил программу безопасности "Трон"
Ed Dillinger (3):
      Переименовал шахматную программу в "ПМК"
      Изменил шахматную программу
      Обновил шахматную программу
Walter Gibbs (1):
      Запустил пилотную версию шахматной программы

В Git есть ряд других ситуаций, когда важна разница между заголовком и телом сообщения, но ни один из таких сценариев не будет работать нормально без пустой строки-разделителя.

2. Уместите заголовок в 50 знаков (или меньше)

50 знаков – это не жесткое ограничение, а проверенный практикой способ. Такая длина делает заголовок читаемым и заставляет автора задуматься, как наиболее кратко объяснить, в чем дело.

Подсказка: если вам сложно придумать краткий заголовок, возможно, вы коммитите слишком много изменений за раз. Старайтесь делать атомарные коммиты (это тема для отдельного поста).

Интерфейс GitHub учитывает это правило и предупреждает, если лимит в 50 знаков был превышен:

Интерфейс GitHub учитывает это правило и предупреждает, если лимит в 50 знаков был превышен:

Если заголовок длиннее 72 знаков, все, что не поместилось, GitHub уберёт под кат.

Если заголовок длиннее 72 знаков, все, что не поместилось, GitHub уберёт под кат.

Так что в идеале старайтесь уложиться в 50 знаков, но не выходите за 72.

3. Пишите заголовок с заглавной буквы

Тут все просто. Пишите заголовки с большой буквы. Например:

  • Разгоняйтесь до 140 км/ч

А не:

  • разгоняйтесь до 140 км/ч

4. Не ставьте точку в конце заголовка

В заголовках не обязательно ставить завершающие знаки препинания. Особенно если учесть, что места мало, и вы пытаетесь уместить мысль в 50 знаков.

Например:

  • Откройте двери отсеков

А не:

  • Откройте двери отсеков.

5. Используйте повелительное наклонение в заголовке

Примечание переводчика: этот совет актуален, если вы работаете в Git на английском языке. Если ваша команда работает на русском, лучше использовать глаголы третьего лица единственного числа в прошедшем времени, например, «сделала», «исправил» и т.д.

Фраза в повелительном наклонении — это «устная или письменная коммманда или инструкция». Вот несколько примеров:

  • Clean your room
  • Close the door
  • Take out the trash

Все семь правил, которые вы сейчас читаете, написаны в повелительном наклонении («Ограничьте длину строки в теле сообщения 72 знаками» и т.д.)

Повелительное наклонение идеально подходит для заголовков сообщений коммитов (на английском — прим. пер.), в частности, потому что Git сам использует повелительное наклонение, когда создает коммит от вашего имени.

Например, при команде git merge сообщение коммита выглядит так:

Merge branch ‘myfeature’

А вот так при команде git revert:

Revert “Add the thing with the stuff”

This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.

При нажатии на кнопку «Merge» (слить) в запросе на принятие изменений на GitHub:

Merge pull request #123 from someuser/somebranch

То есть, когда вы пишите заголовки в повелительном наклонении, вы следуете нормам, принятым в самом Git. Например:

  • Refactor subsystem X for readability
  • Update getting started documentation
  • Remove deprecated methods
  • Release version 1.0.0

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

  • Fixed bug with Y
  • Changing behavior of X

Иногда в заголовках коммитов описывается содержание сообщения:

  • More fixes for broken stuff
  • Sweet new API methods

Чтобы избежать путаницы, следуйте простому правилу: правильно составленным заголовком сообщения коммита всегда можно продолжить фразу if applied, this commit will, то есть:

  • If applied, this commit will ваш заголовок

Например:

  • If applied, this commit will refactor subsystem X for readability
  • If applied, this commit will update getting started documentation
  • If applied, this commit will remove deprecated methods
  • If applied, this commit will release version 1.0.0
  • If applied, this commit will merge pull request #123 from user/branch

Обратите внимание — с фразами не в повелительном наклонении это не работает:

  • If applied, this commit will fixed bug with Y
  • If applied, this commit will changing behavior of X
  • If applied, this commit will more fixes for broken stuff
  • If applied, this commit will sweet new API methods

Запомните: повелительное наклонение нужно только в заголовке. Дальше можно расслабиться и писать, как удобно.

6. Ограничьте длину строки сообщения 72 знаками

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

Тут может пригодиться хороший текстовый редактор. Например, Vim позволяет установить настройки так, чтобы каждые 72 знака начиналась новая строка. А вот IDE обычно плохо справляются с переносом текста в сообщениях коммитов (впрочем, в последних версиях IntelliJ IDEA с этим стало получше).

7. В теле сообщения указывайте, что и почему было сделано, а не как

В этом коммите из ядра Bitcoin разработчик отлично объясняет, что он поменял и почему.

Author: Pieter Wuille pieter.wuille@gmail.com Date: Fri Aug 1 22:57:55 2014 +0200

Упростил обработку исключений serialize.h

Убрал ‘state’ и ‘exceptmask’ и связанные методы из реализаций потока serialize.h' В exceptmask всегда содержался флаг ‘failbit’, а setstate всегда вызывался с помощью bits = failbit, и только и делал, что немедленно создавал исключение. Избавился от этих переменных и заменил setstate прямым выбрасыванием исключением (это также частично убирает мертвый код).

В результате после ошибки к good() больше не обращаемся (вызвали два раза, причем один - в тесте), можно заменить на !eof().

fail(), clear(n) и exceptions() никогда не вызываются.

Удалите их.

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

В большинстве случаев можно не уточнять, как именно было сделано изменение. Обычно код говорит сам за себя (а если код такой сложный, что его нужно объяснять словами, оставляйте комментарии в коде). В сообщении коммита постарайтесь в первую очередь объяснить, почему вы сделали изменения: как всё работало до изменений, что было не так, как все работает теперь, и почему вы решили проблему именно таким способом.

Будущий разработчик скажет вам спасибо. Быть может, это будете вы сами!

Советы

Полюбите командную строку. IDE оставьте в прошлом.

Подружиться с командной строкой – мудрое решение, и причин тому так же много, как и подкоманд Git. Git – очень мощная система, IDE тоже, но по-другому. Я пользуюсь IDE каждый день (IntelliJ IDEA) и раньше использовал другие (Eclipse), но я никогда не встречал такой успешной интеграции IDE и Git, которая могла бы сравниться с командной строкой по простоте и мощности (стоит только научиться с ней работать).

Некоторые связанные с Git функции IDE бесценны, например git rm при удалении файла или необходимые действия с Git при переименовании. Но как только вы начнете коммитить, использовать команды merge и rebase или делать сложный анализ истории изменений через IDE, вся работа начнёт трещать по швам.

Для полноценного использования всей мощи Git нужна командная строка. А для тех, кто пользуется Bash или Z shell, есть скрипты автодополнения, так что не придется запоминать все команды и флаги.

Читайте Pro Git

Книга Pro Git выложена онлайн бесплатно. Прочитайте ее!

Перевод оригинальной статьи: «How to Write a Git Commit Message» by Chris Beams