Git Commit Message Hooks with Husky

Kiran Mohan
6 min readJun 3, 2023

--

Introduction:

Git hooks are powerful tools that allow you to automate tasks and enforce specific actions in your Git workflow. One popular tool that simplifies the setup and management of Git hooks is Husky. In this article, we will explore what Git hooks are, why Husky is widely used, and how you can leverage it to create a commit message hook.

What are Git Hooks?

Git hooks are scripts that run automatically at specific points in the Git workflow. They enable you to perform custom actions, validate commit messages, enforce coding standards, run tests, and more. Git hooks can be triggered before or after specific events, such as committing code, pushing changes, or merging branches.

What is Husky and Why is it Used?

Husky is a lightweight Node.js library that simplifies the management of Git hooks. It provides an intuitive and declarative way to configure and run hooks in your project. Husky eliminates the need for manual hook installation and ensures consistency across team members, making it an ideal choice for projects of any size.

Scenarios for Using Husky:

Husky can be beneficial in various scenarios, including:

  • Enforcing commit message guidelines to ensure a consistent and meaningful commit history.
  • Running pre-commit hooks to automatically format code, lint files, or run tests.
  • Triggering post-merge hooks to install dependencies or update configuration files.

Now that we know what Husky is and how it works, let’s set up some Git hooks using Husky!

Implementation

Step 1 — Set up a Project

You can set up a Next.js project by running

yarn create next-app my-husky-project
# or
npx create-next-app my-husky-project

This will create a new folder, create a new Next.js project, and install all the dependencies. Navigate into the newly created folder and we should be good to go.

Note: Feel free to use a custom name for the project by replacing my-husky-project with a name of your choice.

Step 2 — Install Husky

To get started with Husky, add it as a development dependency to your project using the following command:

yarn add husky --dev

Step 3 — Enable Husky

After installing Husky, run the following command to set up the necessary hooks:

yarn husky install

Step 4 — Create the Commit Message Hook

In the root directory of your project, create a .husky folder and inside it, create a file named commit-msg with the following content:

This hook utilizes commitlint to enforce commit message conventions, ensuring that commit messages follow a consistent format. Let’s break down what each of the lines does:

  1. #!/usr/bin/env sh: This line is known as the shebang line and specifies the interpreter to use for executing the script, in this case, the Bourne shell (sh).
  2. . "$(dirname -- "$0")/_/husky.sh": This line sources the husky.sh script located in the _/ directory relative to the current script's location. Sourcing the husky.sh script is a common step in Husky Hooks to set up the necessary environment and functions.
  3. npx --no -- commitlint --edit "": This line executes the commitlint command using NPX (Node Package Runner). commitlint is a tool commonly used for enforcing commit message conventions. The --no flag prevents NPX from checking and installing missing packages and --edit "" opens an interactive editor for the developer to enter the commit message.

Overall, this script sets up the necessary environment using husky.sh and then invokes commitlint to enforce commit message conventions or perform additional checks before allowing the commit. The specific conventions or checks enforced by commitlint can be configured separately based on the project's requirements.

Step 5 — Making the Commit-Msg Hook Executable

To make the pre-commit file executable, run the following command in your project's root directory:

chmod +x .husky/commit-msg

Step 6— Setting up Commitlint

To enable Commitlint, install the required dependencies by running the following command:

yarn add @commitlint/cli @commitlint/config-conventional --dev

Next, create a commitlint.config.js file in the root directory and populate it with the required configuration. This configuration enforces rules for the type, scope, and format of commit messages.

Step 7— Commitlint Config

Here is some sample code for the commitlint.config.js file. Feel free to play around with it and tweak it as per your requirements.

Here’s a breakdown of what this configuration does:

  1. extends: ["@commitlint/config-conventional"]: This line indicates that the configuration extends the rules defined in the @commitlint/config-conventional package. The config-conventional package provides a set of commonly used commit message conventions.
  2. rules: This section specifies the rules to be applied to commit messages. Each rule has a key-value pair format.
  • "body-leading-blank": [1, "always"]: Requires a blank line after the commit message subject.
  • "body-max-line-length": [2, "always", 100]: Enforces a maximum line length of 100 characters for the commit message body.
  • "footer-leading-blank": [1, "always"]: Requires a blank line before the commit message footer.
  • "footer-max-line-length": [2, "always", 100]: Enforces a maximum line length of 100 characters for the commit message footer.
  • "header-max-length": [2, "always", 100]: Enforces a maximum line length of 100 characters for the entire commit message header.
  • "scope-case": [2, "always", "lower-case"]: Enforces the scope portion of the commit message to be in lower case.
  • "subject-case": [2, "never", ["sentence-case", "start-case", "pascal-case", "upper-case"]]: Enforces the subject portion of the commit message to be in lower case and disallows other casing styles.
  • "subject-empty": [2, "never"]: Requires the commit message subject to be non-empty.
  • "subject-full-stop": [2, "never", "."]: Disallows a period at the end of the commit message subject.
  • "type-case": [2, "always", "lower-case"]: Enforces the commit message type to be in lower case.
  • "type-empty": [2, "never"]: Requires the commit message type to be non-empty.
  • "type-enum": [2, "always", ["build", "ci", "docs", "feat", "fix", "perf", "refactor", "style", "test", "chore"]]: Enforces the commit message type to be one of the specified values (build, ci, docs, feat, fix, perf, refactor, style, test, chore).

By using this commitlint configuration, developers can ensure that commit messages follow a consistent structure and adhere to the defined conventions. It helps improve readability and provides a standardized format for commit messages across a project or team.

Step 8— Configure Husky

To instruct Husky to use the .husky folder for hooks, create a .huskyrc file in the project's root directory.

Step 9— Commit Away

You can now commit all you want rest assured that Husky will take care of ensuring the guidelines that you set are met by each and every member of your team!

Based on the above config set in the commitlint.config.js , each commit message much be of the form: type: commit message here (where type can be any of build, ci, docs, feat, fix, perf, refactor, style, test), else Husky will throw an error as such and prevent the commit from going through!

Error is thrown by Husky when the commit message doesn’t meet the guidelines

Conclusion

By leveraging Husky and Git hooks, you can streamline your Git workflow and ensure consistency in your development process. With Husky’s simplicity and flexibility, you can enforce commit message standards, automate tasks, and enhance collaboration within your team. As you explore further, check out other articles in this series to supercharge your development productivity.

Remember, Husky is just one of the many tools available to optimize your Git workflow, and understanding Git hooks opens up a world of possibilities for automating and enhancing your development process.

--

--