By browsing the log of many Git repositories, you will probably find messy commit messages - while well-crafted messages are the best way to communicate about a change to other contributors. Generally, the git diff will tell you what changed, but only the commit message can properly tell you why it was changed. Understanding why something happened is important not just months or years later but every day: reviewing others' commits could be done independently and efficient way with the help of meaningful and well-crafted messages.
There are many different commit message conventions where one could be a better convention to one team than the other. However, selecting one and stick to it is far better than does not apply any of them. There are four important things in commit message conventions:
Structure: helps to find information easier if you know where to look for
Content: determines which information is expected and which is not
Style: writing in plain English helps to read more quickly and easily
Metadata: provides references to issue tracking systems
In this post, I am addressing some well-tried conventions that I found useful in many projects. Our goal is to provide both human- and machine-readable meaning to commit messages, therefore, we are applying the Conventional Commits proposal, which complements and strongly correlates with SemVer specification.
Note: See Git commit message template page for the template. The Git repository template project is created to help composing (with using preformatted templates, syntax highlights within nano and Sublime, and commit message convention checking with Git::Hooks). Detailed installation instruction could be found in the project README.md.
Our guideline aims at providing an easy set of rules for creating an explicit commit history which makes it easier to write automated tools on top of it. As an overview, each commit message follows the next structure (please note, there are blank lines between the elements):
The expected structure of the commit messages are detailed in the next table:
For detailed specification read Header structure section below.
For detailed specification read Body structure section below.
For detailed specification read Footer structure section below.
The header is the title of a commit. It has a strict structure. The header has a special format that includes a type (MANDATORY), a scope (OPTIONAL), a breaking change marker (i.e. *!*) (OPTIONAL), an issue-key (MANDATORY) and a description (MANDATORY). The whole header should be written in lower case letters (except names such as CHANGELOG.md).
The type parameter describes the nature of the change, that could be one of the the following list (this list could be extended with short nouns):
the commit changes that affect the build system or external dependencies (example scopes: gulp, maven, npm)
the commit changes to the continuous integration configuration files and scripts
the commit changes the documentation only
the commit introduces a new feature to the codebase
this correlates with MINOR in semantic versioning (i.e. SemVer)
the commit patches a bug in your codebase
this correlates with PATCH in semantic versioning (i.e. SemVer)
the commit includes code change that improves performance
the commit contains only refactoring, so the code changes are neither fixes a bug nor adds a feature
the commit changes those parts that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc.)
the commit includes missing tests or correcting existing tests
The scope parameters should be one of the Git project components (see page Git repo README.md section "Component list") list if applicable. The scope should be put into parenthesis.
The subject contains the concise description of the change using:
imperative, present tense: "change" not "changed" nor "changes"
don't capitalize the first letter
don't do dot (.) at the end of the line
Commit message without body
docs(model-server): NET-1234 correct spelling of CHANGELOG.md
The body should include the motivation for the change and contrast this with previous behaviour: what changes we have made and why we made them. The major goal is to give additional contextual information about the code changes.
The body follows the same style as in the section subject parameter. A commit body is free-form and MAY consist of any number of newline-separated paragraphs. The body MUST begin one blank line after the header.
Commit message: with a body and a footer
feat(model-server): NET-133 add onUrlChange event (popstate/hashchange/polling)
Added new event to Editor:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available
A commit body is controlled-form and MAY consist of any number of newline-separated paragraphs of the following types (e.g. BREAKING CHANGES: <description>):
Describing the breaking changes (OPTIONAL item) in detail (see Breaking changes section):
BREAKING CHANGES: <details>
Giving the person who reviewed the code:
Reviewed-by: <git username>
Listing which documents should be amended due to the changes:
A breaking change is a type of change when the commit leads to incompatibility comparing with the previous version. In SemVer terminology, it is called MAJOR change. A BREAKING CHANGE can be part of commits of many type parameter (cf. above).
Every breaking change MUST be denoted with the following:
In the header, after the type structural element, an exclamation mark ('!') must be appended.
In the footer, in (several) paragraphs the changes should be detailed and this description should start with the 'BREAKING CHANGE:' expression (capitalized!). The BREAKING CHANGE section should be the last section in the footer, because it could contain many paragraphs.
Commit message: breaking change without body
feat(build)!: NET-1234 allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
Commit message: breaking change with multiple bodies - the most broader footer with detailed breaking change
feat($compile)!: NET-123 simplify isolate scope bindings
Changed the isolate scope binding options to:
- @attr - attribute binding (including interpolation)
- =model - by-directional model binding
- &expr - expression execution binding
This change simplifies the terminology as well as
number of choices available to the developer. It
also supports local name aliasing from the parent.
CC: kapasd, zalkag
Doc-impact: Scope binding document
BREAKING CHANGE: isolate scope bindings definition has changed and
the inject option for the directive controller injection was removed.
To migrate the code follow the example below:
// myEval - usually not useful, but in cases where the expression is assignable, you can use '='
myAccessor: '=' // in directive's template change myAccessor() to myAccessor
The removed `inject` wasn't generally useful for directives so there should be no code using it.
What do I do if the commit conforms to more than one of the commit types?
Go back and make multiple commits whenever possible. Part of the benefit of the convention is its ability to drive us to make more organized commits.
What do I do if I accidentally use the wrong commit type?
Please fix it as possible since the commit will be missed by tools that are based on the spec.
What language should use to write commit messages?
We follow the general advice to write code, log messages and commit messages as well in English. IT-related expressions are full of easily-not-translateable words so we stick to the English language. Although, we do not necessarily translate all business-related Hungarian expressions to English.
What is the proposed commit message length?
Any line of the commit message (the header, the body and the footer) cannot be longer than 72 characters. This allows the message to be easier to read in various git tools.
Software engineering leader with 15+ years of experience with co-located and distributed multinational development teams. Successful projects include both research-intensive product developments and highly scalable and reliable distributed applications for customers (> 10M users). Professional interests include agile methodologies, specifically product strategy & planning, development workflows and metrics.