We are often in a hurry to commit our changes in Git and so we write something random in our commit messages. In fact, I have seen people putting the date and time or even something like
commit 1
,
commit 2
in their messages.
This is not a good practice, as commit messages should be helpful and make sense so that the people working on the project, reading the code, or contributing to it understand the changes from the message itself.
Now let's look at a simple way to solve this issue.
What is Commitlint?
Commitlint
is a simple tool that lints your commit messages and makes sure they follow a set of rules.
It runs as a husky pre-commit hook, that is, it runs before the code is committed and blocks the commit in case it fails the lint checks.
How to Use Commitlint with a Simple JavaScript Project
In this example, we are going to see how we can set up commitlint in a simple JavaScript project. To get started, let's create an empty project first:
mkdir commitlint_example && cd commitlint_example
npm init
yarn init
# Just accept the defaults when prompted to configure the project
Creates a directory named commitlint_example and initialises an empty JavaScript project with the respective package manager
Now, let's initialise an empty Git repository:
git init
Initialises an empty git repository locally
We must also add a
.gitignore
file to prevent some files from being committed:
node_modules/
Now we'll add a file called
index.js
and just log out something for now:
console.log("Hello, World!!!")
Logs out "Hello, World!!!" to the console
Running
node .
should print out the text on your terminal like this:
adds a husky pre-commit hook that will run before the code is committed
Now we're done setting up commitlint. So let's test to see if it works.
First, we'll stage all files to commit them:
git add -A
Stages all files to be committed
Now, let's try to commit the changes, without following the default convention:
git commit -m "set up a basic js project, added commitlint and husky for liniting commit messages"
Try to commit with a message not following the default convention
Error that should occur due to not following the default convention
You should get the above output (or something similar) which errors out. If the commit is successful, you have likely gone wrong somewhere. Make sure that you have run all the commands above and try undoing the commit, running the scripts, and committing again until it fails.
Now it is time to commit properly. Run this command:
git commit -m "ci: initialised basic js project, added commitlint and husky to lint commit messages"
Code should get committed due to following the default convention
And now it all looks good.
How the Default commitlint Convention Works
The default commitlint convention uses the
Conventional Commits Convention
where there is a type, optionally a scope, a subject, and optionally a body and footer.
For example I can fix a bug related to UI and then the commit message can be
fix(ui): Button was not showing up properly on mobile view
. Here the type is
fix
, that is, a fix for a bug, the scope is
ui
as the fix was related to the ui, and the subject provides more context about the issue.
Note that I can supply multiple scopes, for example,
feat(ui,lang): added an option to save the image as svg and added language support for Spanish
. Here we introduce 2 features – a new button to save an image as svg and language support for Spanish. This means that there are 2 scopes. The scopes can be separated by the 3 delimiters -
,
,
/
and
\
.
Just a quick note here: you should usually keep commits small and specific, and while there might be some edge cases, this is not one. We're just using it for example purposes.
Breaking changes are usually represented with an exclamation
!
mark but you can also write them in bold in the footer of the commit message. Doing both is the best practice where the footer gives more information. Here's an example:
refactor(runtime)!: Dropped support for NodeJS v12
BREAKING CHANGE: Support for NodeJS v12 has been dropped due to the latest refactor, please upgrade to the latest LTS version of NodeJS
Example for breaking change commit message
This brings us to multi-line commit messages. Sometimes we need to give more context on something. In this case, it best to include the info in the commit message to make it clear to anyone trying to understand what all has changed and why it has changed in a commit. Here's an example:
docs: Added an aria-label in the IconButton example
aria-label is a required prop by the IconButton component. If it is not present, the build will fail
Example for multi-line commit message
Advantages of using commitlint
Automatic changelogs – Due to commits following a standard convention, tools like
standard-version
can automatically generate changelogs
Better understanding of commits – A commit with a specific type and scope will help you understand what code the commit changes
Adherence to a particular convention – When you have a big project and a lot of people committing to it, people might forget to use the convention. commitlint blocks such commits so that the commits adhere to the defined convention.
Now you know the basics of commitlint. And in the next part of this article, we are going to dive a little deeper and see how to write custom commitlint rules and how to run a commitlint CI in GitHub Actions.
How to Create Custom commitlint Rules
The
Conventional Commits Convention
works for most projects. But sometimes you might want to add some more rules specific to your use case.
For our example here, we'll use an application which has a library of buttons made with TailwindCSS. You can add your creation to this application through a pull request.
Now these commits can have different types, so let's take a
button
for this example. This would require me to override the
type-enum
rule in the conventional commits convention.
To do this, I will create a
rules
object in my commitlint config and add
button
as a type. This is how our
commitlint.config.js
should look:
We need to make a new folder called
.github
and then a new folder in it called
workflows
. Then we can add a file called
commitlint.yml
and add the workflow configuration.
This workflow will run every time code is pushed to GitHub and every time a pull request is opened. To test it, let's commit and push our code.
git add -A
git commit -m "ci(commitlint,workflow): added GitHub action workflow to run commitlint on push and pr"
git push origin master
Now, we can go to the GitHub repository and then the actions tab and we can see the workflow.
I made a typo in the name of the
workflows
folder so I had to fix that and commit and push again so the commit name is different.
When you look at the details, you can see that the workflow has been successful as all the commits until now have adhered to the convention.
We can also inspect the logs:
What's next?
I hope everything has worked well for you so far. If you had any issues, feel free to reach out to me on
Twitter
and I will be happy to help 😃.
Now that you have commitlint set up, it's a good idea to add automated changelogs. So head over to the
standard version
repository and try to implement it on your own!
freeCodeCamp is a donor-supported tax-exempt 501(c)(3) charity organization (United States Federal Tax Identification Number: 82-0779546)
Our mission: to help people learn to code for free. We accomplish this by creating thousands of videos, articles, and interactive coding lessons - all freely available to the public. We also have thousands of freeCodeCamp study groups around the world.
Donations to freeCodeCamp go toward our education initiatives, and help pay for servers, services, and staff.