Guess What? Your Commit Message Style Might Be All Wrong!
Discover the art of git commit messages! 🚀 Unravel a unique system to keep your code history neat. Avoid chaos, and yes, it’s not taught in school!
Introduction
Today, I’m diving into a topic that might not seem directly related to coding but is very relevant in programming: How to write the right git commit message? It’s one of those things they don’t teach you in school.
Truthfully, there’s no specific right or wrong way to write a commit message 😄. But, imagine if, in a project, everyone wrote commit messages differently. Wouldn’t the commit history look a bit messy? Not to mention the challenge of searching and reviewing a commit from a few months ago without any consistent format. You might encounter problems like:
- Reading a commit message and not understanding its purpose.
- Needing to summarize code changes when releasing after a development period.
- Choosing an appropriate version: v1.0.0, v1.0.1, v1.1.0, or v2.0.0…? It’s a headache to look back over many commits.
- Simplifying searches through regex.
Maybe some of you readers have already implemented certain rules in your projects to address the issues mentioned. Why do I call them rules? Because they pertain only to your specific project. But are there any universal guidelines we all can follow?
Conventional Commits
Conventional Commits provides a set of rules for writing commit messages, designed for both humans and machines
to understand. Here, machines refer to tools that assist with versioning, for instance.
These rules align with SemVer (Semantic Versioning) in how they describe features, bug fixes, or code refactoring. At the time of writing this article, the Conventional Commits
specification is at version 1.0.0-beta.4
, and there may be updates in the future.
Git conventional commits are widely used in repositories, especially open source projects with contributions from thousands of contributors. You can refer to the implementation of conventional commits in some open projects on github. Typically, Electron’s big repository here
Commit message structure
The general structure of a conventional commit message is:
<type>[optional scope]: <description>
[optional body]
[optional footer]
Where:
- The
type
anddescription
are mandatory elements of a commit message. Theoptional
sections are, well, optional. type
: categorizes the commit (e.g., feature, bug fix, refactor). Note the:
right aftertype
.scope
: provides further classification, answering questions like: What does this commit refactor or fix? It's placed in parentheses aftertype
, likefeat(authentication):
orfix(parser):
.description
: gives a brief overview of the changes made in the commit.body
: offers a more detailed description when the short one doesn't suffice.footer
: includes additional information, such as pull request or issue IDs, as per conventional standards.
For clarity, here are some commit message examples:
# ex1:
feat: implement multi-languages
ex2:
fix: homepage's bug
# ex3 with scope:
fix(player): uiza player can not initialize
Semantic Versioning
Conventional Commits align with SemVer through the type
in the commit message. Automated versioning tools use this to determine new versions for source code, following these conventions:
fix
: a bug-fixing commit will change the PATCH in the version, per semantic versioning.feat
: a feature-adding commit will modify the MINOR, as per semantic versioning.- Additionally, the keyword BREAKING CHANGE in a commit message indicates non-backward-compatible changes, like altering an API’s response structure. The old response-handling code won’t work, necessitating a new version and a MAJOR version change.
Common types
Recommended and commonly-used types
include:
feat
: introducing a new featurefix
: bug fixesrefactor
: code modification without adding features or fixing bugs, though sometimes bugs can get fixed during refactoring.docs
: adding or modifying documentationchore
: minor changes unrelated to the codebasestyle
: changes that don’t alter the code's meaning, like CSS/UI tweaks.perf
: improvements in code performancevendor
: updates to dependencies or packages.
Beyond feat and fix, essential for Semantic Versioning, you can still use
type
with custom nouns that you define.
Summary of the Rules
The rules are quite straightforward and easy to remember. If you write commits according to the structure mentioned above, you’re already adhering to most of these rules. To highlight:
- A commit message must have a prefix that’s a type (in noun form) such as
feat
,fix
followed immediately by a scope (if available) and a colon with a space, as discussed earlier. For example: feat:, fix:. - The
feat
type is mandatory when adding a feature. - The
fix
type is a must when fixing a bug. - If there’s a scope, it should be a noun describing the area of code changed and placed right after the
type
. E.g.,feat(authentication)
. - A
description
should provide a brief explanation of the changes in the commit and must follow a space after the type/scope. - For a lengthy commit, a body can be added after the description, offering context for the changes. There should be an empty line separating the description and the body.
- The
footer
can be placed right after the body, with a blank line in between. The footer should contain extended commit details like related pull requests, reviewers, and breaking changes. Each piece of information should be on a separate line. - Types other than
feat
andfix
can also be used in the commit message.
In essence, you don’t need to memorize much from the above as committing using the structure in the first part already gets it right. 😄 Moreover, there are a few other rules such as:
- For breaking changes, specify at the beginning of the body or footer with the capitalized term
BREAKING CHANGE
followed by a colon and a space. For instance:
feat(oauth): add scopes for oauth apps
BREAKING CHANGE: environment variables now override config files.
- Another description should immediately follow the BREAKING CHANGE, detailing the API alterations. Like:
BREAKING CHANGE: environment variables now override config files.
- An exclamation mark ! can be added before the colon in the type/scope to emphasize that the commit has a breaking change.
Conclusion
As the article explains, the rules in Conventional Commits are indeed simple and easy to implement, right? Remembering the old days, while releasing, we often used the git logs command to fetch the commit messages for CHANGE LOGS
.
git log v1.0.0...HEAD --pretty="format:%e- %s"
Now, after adopting Conventional Commits, it’s clear that we can:
- Use Semantic Release for automated versioning. Auto-create CHANGE LOGS for projects using semantic-release plugins.
- Employ Commit Lint to verify commit messages against Conventional Commits.
- Searching through the commit history also becomes simpler since you can use regex to filter commits by type/scope.
This wraps up the guide on how to craft professional and efficient commit messages. If you found this article helpful, don’t forget to give a Clap to support me. Thanks for reading my article!