Explore the power of Git commit conventions; Learn a structured convention, enforce it with Commitlint, and empower your codebase git history with clarity and automation.
A well-structured commit message convention is crucial for an organized git history. In our continuous pursuit of code quality, we've explored the power of Husky git hooks in the "How Husky Git Hooks enhance Code Quality and Fault Prevention?" blog. In this blog post, we'll explore the benefits and importance of implementing a commit message convention, its alignment with Semantic Versioning (SemVer), how to enforce them for a more structured and automated development process.
A commit message following the convention is structured as follows:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types like feat
, fix
, and others provide quick insights into the nature of the changes. Here is a lost of types provided by default using @commitlint/config-conventional:
A scope, contained within parentheses, may be provided to a commit's type for additional contextual information. It can be empty if the change is global or challenging to assign to a single component. Since scopes are specific to the context if your project they will not be provided for you using conventions and you should define them according to your needs. They can be "components", "utils", "helpers" for example.
Note: Avoid using issue identifiers as scopes, e.g., feat(shopping cart): add the fabulous button
.
The description is a mandatory part of the format, providing a short summary of the code changes. Use the imperative, present tense, and avoid capitalizing the first letter or adding a period(.
) to the end. For example: feat: remove ticket list endpoint
.
The body, an optional part of the format, offers additional contextual information about the code changes. We highly recommend using issue refs if possible. e.g.:
fix: prevent racing of requests
resolves #DEBT-100
By doing this, you can utilize the power of automatic issue closing in GitHub and GitLab.
But otherwise, the body must begin with one blank line after the description and should use the imperative, present tense. For example:
fix: prevent racing of requests
Introduce a request id and a reference to the latest request. Dismiss
incoming responses other than from the latest request.
Remove timeouts that were used to mitigate the racing issue but are
obsolete now.
One or more footers, an optional part of the format, may be provided with one blank line after the body. Each footer must consist of a word token, followed by either a :space
or space#
separator, followed by a string value.
An exception is made for BREAKING CHANGE, which may also be used as a token. Breaking changes should start with the word BREAKING CHANGES:
followed by a space or two newlines. The rest of the commit message is then used for this.
To ensure cohecion to the commit message convention, we can leverage Commitlint. Create a commitlint.config.js
file in your project with the following content:
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"scope-enum": [
2,
"always",
[
// Custom scopes specific to your project context
// for example "components", "utils", "helpers", ...
],
],
},
helpUrl: "https://milad-afkhami.com/blog/commit-message-convention",
};
This configuration extends @commitlint/config-conventional
, aligning with widely accepted commit message conventions that we explored.
Adopting a commit message convention not only improves collaboration and code readability but also sets the stage for automated tooling and integrates with versioning practices. Enforce the convention with Commitlint to ensure consistently high-quality commit messages across your projects. This commitment to clear and structured commit histories contributes to an efficient and reliable development process.