Generating a separate commit for Husky lint-staged tasks (ESLint, Prettier) using the post-commit Git hook

Gifford Nowland
3 min readAug 11, 2022

--

There are a lot of tutorials out there related to running autoformatting or linting tools (such as ESLint or Prettier) on the pre-commit Git hook with Husky and lint-staged,¹ ² ³ ⁴ ⁵ but I couldn’t find any related to running these tasks on post-commit. Why would you want to run autoformatting/linting (henceforth “autoformatting”) on post-commit instead of pre-commit? To save their modifications in a separate commit, of course!

Photo by Fabian Fauth on Unsplash

Background

My team decided to add autoformatting to a mature React (create-react-app) project. Noble, right? I’m not a super-fan of opinionated autoformatters (ahem, Prettier), but I realize maintaining consistent code formatting can improve readability and maintainability, and it’s a hell of a lot easier to automate formatting than enforce it.

Egad! Suddenly, when a developer changed any part of a file that hadn’t yet been formatted by Prettier a slew of syntactical changes were committed inline with the initial code modification, attributed to the initial commit — the horror! Subsequently tracing bugs through commit diffs became a nightmare. We had to try and parse out a multitude of formatting changes from the actual code change — a veritable needle in a haystack!

The fact that these modifications happened opaquely, without input or manual review, only exacerbated the problem — especially when it resulted in unexpectedly committing code that was not initially staged for commit (from a partially-staged file).

Solution

Seeing the ongoing nightmare in front of us we decided we had two options:

  1. Run autoformatting on all files at once, after which subsequent commits would only autoformat new code, or
  2. Add the autoformatting changes to a separate, dedicated commit

We decided to proceed with option 2 since it required minimal up-front effort and would allow us to a) undo the autoformatting without affecting the initial commit, and b) provide appropriate “blame” when autoformatting broke something.

Instructions

This is targeted to projects using NPM, but would work in any environment Husky/lint-staged supports with some modifications (see package docs).

1. Install & Set Up

Since this guide is aimed at moving autoformatting from pre-committo post-commit git hooks I’ll assume you already have Husky, lint-staged, ESLint and/or Prettier already set up. If not follow their docs (or one of the five links in the footnotes) and come back when you’re done.

2. Convert pre-commit to post-commit

Rename .husky/pre-commit to .husky/post-commit (if using the Husky file-based configuration, otherwise rename pre-commit to post-commit in your package.json)

3. Adapt lint-staged to post-commit environment

Change lint-staged to lint-staged --diff="HEAD^HEAD" .
— By default lint-staged uses git diff --staged , but there aren’t any staged files in the post-commit hook!

4. Commit the changes

Add a git commit command to your lint-staged commands:

"*": [
"eslint --fix",
"prettier --ignore-unknown --write",
"git commit -m \"🤖 Autoformatted\""
]

Conclusion

And there you have it, a separate commit will be generated for all robo-changes! I hope this tutorial was helpful — please leave suggestions for improvement in the comments.

Caveats

Until lint-staged Issue #1197: Allow stash when using --diff is resolved partially committing a file will result in the unstaged/uncommitted changes being included in the auto-commit.

--

--

Gifford Nowland
Gifford Nowland

Written by Gifford Nowland

Programmer. Engineering Section Manager @ The Aerospace Corporation. If I can’t find a solution, I write one.