Skip to content

Latest commit

 

History

History
422 lines (269 loc) · 38.2 KB

agreement.md

File metadata and controls

422 lines (269 loc) · 38.2 KB

Стиль именования коммитов [RU]

дата:: 19.09.2023
версия:: 0.9


Оглавление


Описание

Данное соглашение, описывает стиль написания сообщений к т.н. «коммитам», содержит best-practice советы и методы написания последних для участников организации AntHiLL.

Бóльшая части соглашение пишется не запланировано, так как в росте (что важно, а не строительстве1) участвует не один человек, а множество людей. В связи с этим, это соглашение подвержено частому изменению.

Как дополнение к выше сказанному, это соглашение может помочь прояснить, что из себя представляют объекты git'a — коммиты, узнать общепринятые практики по созданию или (пере)написанию истории коммитов, также понять почему важно писать хорошие сообщения.

Что такое «коммит»?

Простыми словами, коммит — это своего рода снимок состояния файлов, записанные за какой-то период времени в локальном репозитории.

«Состояние» файлов, можно интерпретировать по-разному. Существует заблуждение о том, что коммит хранит лишь разницу между состояниями файлов, т.е. хранит изменения (что верно, ведь идейно VCS хранить т.н diff патчи для файлов, которые уходят в корни UNIX системы, но это не верно для git). Коммиты хранят состояния обо всех файлов в т.н. blob (binary objects — bobblob "бактерия", "капля") файлах. Которые представляют собой ключ-значение, где ключ это 40 символьный SHA1, а значение это зашифрованные мета данные о файле (которые могу и отсутствовать и будут ссылаться на состояния этого файла в других блобах). Вся это мишура храниться в деревьях, но это уже другая история.

Почему сообщения коммитов важны?

  • Как и в романе, серия коммитов имеет повествовательную структуру, которая контекстуализирует «сюжет» вашего изменения в коде, что помогает держать картину разработки в голове
  • Коммуникация о характере изменения между товарищами по команде, общественностью и другими заинтересованными сторонами
  • Людям проще участвовать в проекте, потому что им доступна более структурированная история коммитов
  • Ускоряют и упрощает проверку кода
  • Помогают объяснить вопросы, которые не могут быть описаны только кодом
  • Экономят время и нервы (головняка меньше), не больше и не меньше

Best practice советы

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

Маленький коммит? — отлично!

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

Каждый коммит, должен рассказать почему он появился на свет

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

  • А зачем это было сделано?
  • Почему это было необходимо? (опционально)
  • Что было сделано в итоге?

Заголовок отдельно от тела сообщения

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

# ХОРОШО
<Заголовок коммита #9832729385>

<Тело коммита, которое описывает изменения>
# ПЛОХО
<Заголовок коммита #9832729385>
<Тело коммита, которое описывает изменения>

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

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

Оговаривать проблему в коммите

Как правило коммиты несут в себе изменения кода, которые были сделаны для устранения ошибок и т.п. Но читающий историю коммитов не будет иметь представления о них, если конечно не указывать какую проблему решает данный коммит). Поэтому если изменения решают чрезвычайную багу, то это должно быть оговорено в сообщении коммита, т.е. должна быть расписана проблема, которая возникла до устранения этим* коммитом. Так же важно не расписывать изменения, которые несёт коммит, этими же изменениями: не нужно упоминать изменённый код в сообщении коммита. Содержание должно рассказывать кратко (или нет) что было сделано, зачем. А то как это было сделано, опишут изменения, которые он несёт.

Избегать подобную систему ведения истории коммитов

# ПЛОХО
* Fix this
|
* Fix stuff
|
* It should work now
|
* Change stuff
|\
| * Adjust css
|/
* Add main

Синтаксис

Типы коммитов

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

В связи с выше изложенным, предлагается использовать следующие «категории» коммитов:

  • chore: обычная поддержка кода, рутина.
  • refact/refct: рефакторинг определенного участка кодовой базы.
  • feat: новая фича, добавляемая к приложению; добавление функционала в приложение.
  • meta: всё, что связано о данных, которые рассказывают о других данных.2
  • style: обновления, имеющие отношение к стилизации, но, ВАЖНО, не меняющие работу (бизнес логику) приложения.
  • add/ad/a: добавление новых элементов в проект таких как: файлы, директории и т.п.
  • rm/r: удаление элементов проекта: файлы, директории и т.п.
  • project: всё, что касается изменения конфигурационных и т.п. файлов. 3
  • typo: изменения, касаемые исправления опечаток.
  • docs: всё, что касается документации.
  • merge: merge-коммиты.
  • rebase: rebase-коммиты.
  • fix: исправление баги.
  • test: всё, что касается тестирования.

Синтаксис шапки коммита

Первым делом коммит это заголовок. Именно заголовки читаются, как коммиты. Поэтому важно, что бы они точно отражали определённые изменения, а то есть связывали изменения с той категорией (описанных выше), к которым они относятся. Посему предлагается следующий синтаксис т.н. «шапки» коммита:

[<type>#<local'>] <Заголовок>

Где <type> это тип (категория) коммита, которая собсна и связывает «контент» коммита c его изменениями. Также прошу обратить внимание, что тип указывается в некой «корзинке», а именно в квадратных скобках — [ ]. В «корзине» также присутствует поле <local>, которое является необязательным (на это указывает символ '); это поле предназначено для конкретизации места изменения кода: будто изменения были в директории, относящийся к тестированию, или в каком-то файле. Между «корзинкой» и заголовком пробел. И последнее — заголовок. Заголовок у нас на районе принято писать с большой буквы, потому что потому4. Далее рассматривается правила письма типа и заголовка коммита.

Разновидности типов для коммитов

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

- Какие это такие ситуации?

Например, когда вы изменили опечатку в README.md файле (typo, meta) или же изменили структуру в файлах конфигурации проекта (style, project).

Для таких случаев предлагаем использовать термин «множества категорий» (МК далее), которые представляют из себя конкретизацию типов: если вы изменили документацию тестов, вы можете указать тип docs, а можете конкретизировать область изменений, добавив тип test. Но тут есть некоторые оговорки того, как это написать.

Во-первых синтаксис использования МК таков:

[<type_0>:<type_1>]

Где <type_0> есть более общая категория, а <type_1> конкретная область изменений. То есть есть вы сделали исправление опечатки (typo), то именно typo должна занять место <type_1>. А то в какой из «под категории» вы это сделали — указывается в <type_0>. Для нашего примера должен5 указывается тип [test:typo].

Во-вторых здесь важно не перепутать категории местами, потому что категории являются буквально множествами. Так как мы располагаем при себе некоторые категории, которые являются подмножествами других категорий, мы не можем указывать, что одно множество является подмножеством, когда это не так. Поэтому мы не может указать тип, который бы говорил за себя, что он отвечает за «исправления документации в рефакторинге» → [docs:refact]. Но при этом возможен случай, когда производиться «рефакторинг документации» → [refact:docs]. (далее об допустимых сочетаниях).

Также мы категорически предполагаем, что коммит не будет связан с более чем двумя категориями (как это представить?). Т.е. если указывается МК то это множество имеет кардинальное число 2: тип 0 и тип 1 — вот и всё!


Объяснение типов

[chore]

Chore (рутинная работа, обязанность) — указывается при простой поддержке кода: изменения нацелены не на исправления баги, добавления функционала, рефакторинга всего или части кода. Это скорее интуитивное понятие, что есть рутина для кода? Но описанные ранее пункты не попадают под chore тег уж точно.

[refact]

Refact (refactoring) — указывается при обычном рефакторинге кода, то есть изменения логики работы кода, или написания новой. Либо простое переписывание кода.

[feat]

Feat (feature — фича, особенность) — указывается, когда добавляется какой-то новый и самое важное — маленький по объему кода функционал, а иначе если он будет уж оч' большим, то имеет смысл вынести его в составной тип [refact:feat] (об это ниже в "Допустимые сочетания типов").

[meta]

Meta (информация о информации) — указывается при изменениях, внесённых в файлы, которые содержат какую либо информацию о другом коде: README.md, LICENSE, CONTRIBUTING.md, SECURITY.md и так далее. Т.е. если изменения касаются тех файлов, без которых проект мог бы работать, и при этом они дают какое-либо представление о проекте (т.н. проектные знания) — это мета файлы, т.е. файлы, которые рассказывают о других файлах, благодаря мета информации находящихся в них.

[style]

Style (стиль) — указывается при изменениях стиля кодовой базы, или любого другого текста. Т.е. изменяется сам облик текста, но не его функционал (если мы затрагиваем код), это важно! Если вы решили провести минификацию js, условно, то вы же не меняете его функционал, а всего лишь меняете стиль* кода. Также тег уместен, если вы решили просто чуток по другому писать открывающие скобки (например, на следующей строки, после объявления/определения функции или т.п.) или делать другие отступы, использовать одинарные кавычки вместо двойных.

[add/ad/a]

Add (добавить) — указывается при добавлении новых файлов, каталогов и т.п. в проект. Не может быть использован вне МК, т.е. должна быть указана либо конкретная область, где добавляется файл, либо способствующий тип, рассказывающий какой файл мы добавляем.

[rm/r]

Rm (remove — удалить) — указывается при удалении файлов, каталогов и т.п. из проекта. Не может быть использован вне МК, т.е. должна быть указана либо конкретная область, где удаляется файл, либо способствующий тип, рассказывающий какой файл мы удаляем.

[project]

Project (проектное) — указывается при изменениях связанные с конфигурационными файлами: .gitignore, pyproject.toml, .pre-commit-config.yaml, Dockerfile, Makefile, CMakeLists.txt, yarn.lock, requirements.txt и так далее. Т.е. это те файлы, которые прямо не влияют на работу прода, но без них ничего не заведётся/поднимется.

[typo]

Typo (опечатка) — указывается при исправлении опечаток. (обычно самые маленькие коммиты)

[docs]

Docs (documentation — документация) — указывается при работе с документацией к составляющей проекта или ко всему проекту.

[merge]

Merge (объединение) — указывается для обозначения merge-коммитов.

[rebase]

Rebase (rebase ~ перемещение) — указывается для обозначения rebase-коммитов (редкость).

[fix]

Fix (исправление) — указывается при исправлениях в различных областях проекта. В большинстве случаев в фиксе багов.

[test]

Tets (тесты) — указывается при изменениях, касающихся конкретно тестовой составляющей кодовой базы проекта.


Допустимые сочетания типов

Список допустимых сочетаний типов

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

  1. [chore:*]
    1. [chore:meta] — Обновление в мета файлах.
    2. [chore:project] — Затрагивают изменения в файлах конфигурации проекта.
    3. [chore:test] — Обновления в тестах.
    4. [chore:docs] — Изменение документации.
  2. [typo:*]
    1. [typo:meta] — Исправление опечатки в мета файлах.
    2. [typo:test] — Исправление опечатки в тестах.
    3. [typo:docs] — Исправление опечаток в документации.
    4. [typo:project] — Исправление опечаток в файлах конфигурации проекта.
  3. [docs:*]
    1. [docs:test] — Что-то связанное с документацией в тестах.
    2. [docs:project] — Что-то связанное с документацией в файлах конфигурации проекта.
  4. [refact:*]
    1. [refact:feat] — Рефакториг кодовой базы, в частности — добавление новых функций.
    2. [refact:docs] — Рефакториг документации.
    3. [refact:test] — Рефакторинг тестов.
  5. [feat:*]
    1. [feat:docs] — Добавление документации.

В целом можно сказать, что МК не являются обязательными к использованию, но если находится случай, когда можно хорошо выразить область изменений, путём добавления МК, то почему бы и не использовать их? Также хочется добавить, что лучше не усложнять себе жизнь составными типами, а лучше использовать конкретизацию места в теге: [docs#mapping] → что-то связанное с документацией в модуле (или ещё что) mapping.

Оговорка при дублировании типа и заголовка

Мы располагаем множеством типов для коммитов, но при указании некоторых типов (которые буквально могут сказать, что делает коммит), можно также допустить ошибку и дублировать смысл тега в заголовке, тем самым просто потратив пространство для другой информации в заголовке. Например, мы добавили файл с тестами модуля logging и использовали такую шапку для обозначения сие дела:

  • [add:test] Add module logging responding for handle event

    Ошибка заключается в повторном употреблении слова ADD. Лучше сделать так:

  • [add:test] Module logging responding for handle event

При этом коммит читается как "Если применить этот коммит, то будет добавлен модуль тестов 'logging', отвечающий за обработку событий "

Выделения сути в заголовке

Если в заголовке канонично фигуриет название изменяемого вами файла, или любая другая сущность, которая на прямую связана с изменениями, несущим коммитом. То для выделения этого можно использовать апострофы. Не одинарные кавычки, не двойные кавычки или любые другие обрамляющие символы. Апостроф (он же просто `) используется потому что некоторые инструменты по отображению сообщений к коммитам выделяют символы, заключенные между ними. В частности такой фичей обладает GitHub. Поэтому старайтесь при возможности использовать именно апострофы.

Связанные, но разные

Бывают случаи, когда реализуется весьма обширная фича или просто изменяется большая часть кодовой базы. И при этом все коммиты в процессе общей цели — связаны, но вместе с этим они имеют разные типы. Для того, чтобы указать связь коммита A с коммитом B (у которых разные правки и свои цели правок), обычно упоминается один коммит в другом в "подвале" коммита. Но никто не хочет полностью расписывать как и почему они связаны. Для этого существует метка WIP — Work In Progress. И чтобы выделить одну работу* (т.е. один связанный поток коммитов) от другой, предлагается использовать индексы #WIP<ID>.

В команде принялись реализовать вторую фичу N. И был выбран ID для этой долгой работы: 2. Т.е. если это вторая фича, то у них уже есть теги #WIP1, а если новые коммиты будут содержать такой же тег, то как понять, что они разные? никак. Так что, если коммиты разные, но связанные можно указывать под тег WIP с номером задачи.


Тело коммита

Заполнение тела коммита опционально. Нужно в том случае, если описание решенной проблемы достаточно сложно и не может уместиться в строку заголовка. Но при всем этом существует ограничения. Во первых ограничения ширины текста в теле коммита официально составляет 72 символа. А во вторых оглашение предлагает использовать следующие правила — у тела есть части:

  1. Повествующая об изменениях (кратко)
  2. Перечисления* (опционально)
  3. Предположения или же размышления* (опционально)

Примерный синтаксис тела коммита:

<Заголовок>

Тело коммита если и используется, то как минимум только с частью повествующих об изменениях, которые не превышают
72 символа. Если нужно выделить какое-то слово то используйте "двойные кавычки".


Если нужно указать изменеия в виде перечисления, то синтаксис такой:

	* Отступ в один таб или 4 пробела.

	+ В качестве знака пункта можно использовать числа, *, + или -.

	3. Также стоит отметить, что пункты заканчиваются точкой.

	4. Если пункт содержит подпункту, то к ним применяются теже правила,
		но если ширина слишком большая, то делается дополнительный отступ.

	- Между пунктами тами одна пустая строка. И каждый пункт с большой буквы.


В конечной части можно написать свои мысли по поводу коммита. А и часть с перечислением обрамляется
в две пустых строки. Также в теле коммита стоит придерживаться всех арфографических и т.п. правил.

Подвал коммита можно писать и без тела коммита, т.к. по сути своей в большинстве случаев он содержит мета информацию: связь с другим коммитом, упоминания issue, закрытие таска (указание номера таска) и т.д. Сюда можно писать номер закрываемого (закрываемых) тасков из Jira или тикетов с GitHub.

Синтаксис прост: #[Issue: |Task: |WIP]<ID>

  • #WIP34 — связанная работа под номером 34
  • #issue: 345 — упоминание проблемы 345
  • #Task: 72 — упоминание задачи под номером 72.

Синтаксис не жесткий, а просто представляющий пример использования. Можно писать как "fixes issue #13", так и "#Fix Issue 13". Главное придерживаться одного связующего стиля и использовать как минимум один знак решётки #.

По своему названию подвал снизу:

<Заголовок>

<возможное тело коммита>

<подвал>

Важно при этом соблюдать отступ в виде пустой строки между разделами* .


Итоговый синтаксис

В конечно варианте синтаксис будет такой:

 1. (Заголовок)   | [type1(:type2|#scope)] <Заголовок>
 2.               |
 1. (Тело)        | <Краткое описание>.
 4.               |
 5.               |
 6. (Тело:cписок) | <Заголовок перечисления>:
 7.               |
 8.               |     [DOT] Что-то из списка.
 9.               |
10.               |     [DOT] Слишком длинные пункт списка, который может
11.               |         первышать ширину тела (72 символа).
12.               |
13.               |
14. (Тело:подвал) | <Обобщение сделанного, мысли/рассуждения>
15.               | #Таски, Проблемы и т.п.

[DOT] — это выбор между использованием чисел как пунктов либо один из знаков: *, +, -.

Также важно заметить, что заголовок коммита должен содержать либо МК либо область применения изменений. Можно конечно использовать и то и то, но под сам заголовок останется не больше 30 символов.


Примеры сообщений

Заготовки

Если вы добавили такие файлы как: README.md, LICENSE, CONTRIBUTING.md, SECURITY.

То можно со спокойной душой писать так:

[add:meta] `<file-name>`

Но в случае лицензии лучше упомянуть её в заголовке (места всё равно много).

Тоже самое касается и изменений этих файлов:

[chore:meta] Update `<file-name>`

Но очень желательно заставить себя написать минимум одно сообщение к теле, что было изменено, добавлено или удалено (чтобы вообще отбить желание открыть изменения коммита).


Английский в сообщениях коммита

Если вы хотите написать коммит на английском языке, и при этом чтобы соблюдалось какое-то единое повествование от N людей. То придерживаетесь повелительного наклонения, в частности написания заголовков!

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

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

Например:

  • if applied, this commit will refactor subsystem X for readablity
  • 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.12
  • if applied, this commit will merge pull request #124 from user/branch

Источники

Footnotes

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

  2. Метаданные — это данные о данных. Т.е. в качестве «meta» файлов (которые и будут подвергаться изменению) могут выступать README, LICENSE и другие похожие файлы.

  3. К «конфигурационным» файлам можно отнести такие: project.toml, .gitignore, requirements.txt, Makefile, Dockerfile и так далее и тому подобное.

  4. С большой буквы заголовок смотрится полнее, тогда как с маленькой уместно использовать заголовки, включающее в себя не больше 3-4 слова.

  5. Под «должен» подразумевается — «опционально», но, конечно, если вы не собираетесь использовать МК.