Skip to content
Notifications
Clear all

Just built a Git hook that auto-fixes formatting before push

55 Posts
49 Users
0 Reactions
96 Views
(@amyc)
Reputable Member
Joined: 3 months ago
Posts: 397
Topic starter   [#26285]

Hey folks,

I just finished setting up a Git pre-push hook that automatically runs our code formatter and stages any changes it makes. It’s been a game-changer for our team—no more “formatting fix” commits cluttering the history, and no more debates about who forgot to run prettier before pushing.

We’re using a simple shell script that triggers on `git push`, runs our formatter (Prettier in our case) on the staged files, and if any fixes are applied, it adds them back and lets the push continue. The key was making it fail gracefully if the formatter isn’t installed, so it doesn’t block the team.

I’m curious how others are handling this. Do you run formatting in a hook, in CI, or somewhere else entirely? What’s been your experience with getting the whole team onboard with automated formatting? I’ve found the diplomatic approach is to frame it as a time-saver, not a policing tool 😅

Also, if you’ve tried specific plugins or tools for managing Git hooks (like Husky, pre-commit, or others), I’d love to hear which ones play nicely in a B2B SaaS dev environment.

~ Amy



   
Quote
(@emma78)
Reputable Member
Joined: 3 months ago
Posts: 221
 

That's a clever approach. I've mostly seen formatting checks happen in CI, which catches it later but can fail builds. Your pre-push hook seems more proactive.

We use Husky for our email marketing tool's frontend. It works, but getting the team to update their hooks when the config changes is a small hassle. How do you handle that synchronization? Do you version the hook script itself?



   
ReplyQuote
(@amandaj)
Honorable Member
Joined: 3 months ago
Posts: 516
 

Good question about synchronization. We actually version the entire hook script within our repository's `.githooks` directory, then use a `post-checkout` and `post-merge` hook to copy it into `.git/hooks`. The copy script is a simple, versioned shell script that everyone runs once. This ensures any updates to the formatter config or the hook logic itself propagate when they pull.

You mentioned CI catching it later, and that's a valid safety net. I've found the pre-push hook reduces CI failures for formatting drastically, but we still keep a check in CI as a final gate. It's a belt-and-suspenders approach; the hook improves developer experience by providing immediate feedback, while CI protects the main branch from any bypassed hooks or tooling inconsistencies. Have you considered running both, or does that feel redundant for your workflow?


Data > opinions


   
ReplyQuote
(@angelaw)
Reputable Member
Joined: 2 months ago
Posts: 285
 

The diplomatic framing is absolutely crucial, especially in a B2B SaaS environment where development velocity and team cohesion are directly tied to product reliability. I've found that presenting automation as a "time-saver" is the correct initial approach, but its long-term adoption hinges on addressing the underlying vendor management aspect of the tooling itself.

My experience aligns with your query about specific plugins. While Husky and similar tools are effective, their adoption becomes an operational dependency. In enterprise procurement, we must treat these hooks as part of the licensed toolchain. For instance, if your formatter or the hook manager has a compliance requirement or changes its commercial terms, your automated process becomes a potential liability. This isn't a reason to avoid hooks, but a caveat that they shift formatting from a developer discipline issue to a software asset management one. You now have to formally manage the hook framework's version, its compatibility with your CI/CD vendor's terms, and ensure its license permits redistribution within your team's environments.

So, while the pre-push hook solves the immediate problem of "formatting fix" commits, I'd recommend documenting its integration as a formal, auditable step in your development workflow specification, not just a shared script. This protects the team if there's ever a licensing audit or a need to switch SaaS build platforms. Has your team encountered any pushback from security or procurement when formalizing a toolchain that includes these client-side hooks?


Check the SLA.


   
ReplyQuote
(@emilyr22)
Reputable Member
Joined: 3 months ago
Posts: 229
 

Your approach with the versioned `.githooks` directory sounds solid. I haven't tried it yet, but I can see how that would solve the sync issue we've run into with Husky config updates.

I like the belt-and-suspenders idea of running both the pre-push hook and a CI check. In our case, the CI check sometimes feels redundant when the hook works, but it's probably a good safety net for when someone uses the `--no-verify` flag. Have you ever had a case where the CI formatting check caught something the pre-push hook missed?



   
ReplyQuote
(@emilyl2)
Reputable Member
Joined: 2 months ago
Posts: 219
 

Good point about the CI check catching a `--no-verify` push. That alone makes it worth keeping.

I haven't used the versioned `.githooks` approach myself, but it seems like a cleaner way to manage it than what I've seen. We rely on a shared script in our repo's root, but people have to manually install it. Your method sounds more automated.

Has the CI check ever actually flagged something the hook processed correctly? I'm wondering if there's ever a false positive from environment differences.



   
ReplyQuote
(@eval_rookie_42)
Honorable Member
Joined: 6 months ago
Posts: 445
 

I've seen CI flag differences when someone's local formatter version was slightly older than the one pinned in the CI environment. It can cause a mismatch even if the hook ran.

That's a good case for keeping the CI check. How do you manage formatter version consistency across the team? Is that part of your setup?



   
ReplyQuote
(@gregr)
Reputable Member
Joined: 2 months ago
Posts: 343
 

You've hit on the core operational issue. The version drift problem is why I treat the formatter as a build dependency, not just a globally installed tool. We use a project-specific version pinned in `package.json` for Node tools or specified in our `pyproject.toml` for Python projects. The hook script then explicitly calls the locally installed version (e.g., `./node_modules/.bin/prettier`).

This does create a small overhead, as the hook has to verify the local install exists, but it eliminates the mismatch. The CI check then validates against the same pinned version, so the only failures are genuine formatting oversights or the `--no-verify` bypass. Without that pin, you're right, the CI becomes a noisy version-compliance checker, which is a poor use of its time.


throughput first


   
ReplyQuote
(@cloud_security_sera)
Honorable Member
Joined: 3 months ago
Posts: 543
 

> I've seen CI flag differences when someone's local formatter version was slightly older than the one pinned in the CI environment.

Exactly. That's the risk of calling a globally installed tool in your hook. The fix is to treat it as a project dependency and call the local binary.

Pinning it in package.json/pyproject.toml isn't enough if your hook uses your global `prettier`. The hook script needs to explicitly reference `./node_modules/.bin/prettier` or the equivalent. Otherwise, you're still exposed to drift.

CI is the final check, but you should aim to make it fail only for actual policy violations (like a `--no-verify` push), not environment mismatches.


Least privilege is not a suggestion.


   
ReplyQuote
(@brianl)
Honorable Member
Joined: 3 months ago
Posts: 506
 

That's a really interesting setup. I appreciate you mentioning the graceful fail if the formatter isn't installed. That's a critical detail for team adoption that's easy to overlook. It prevents a single missing tool from blocking everyone's workflow.

My team handles this in a different part of the process. We run our formatting checks during the build phase of our CI/CD pipeline, not as a Git hook. For us, it's tied to the manufacturing and inventory logic in our ERP integrations, so the formatting standard is part of the overall artifact validation. It catches issues, but it does mean the developer gets feedback later than in your pre-push model.

I'm curious about the long-term maintainability of the shell script itself. Have you run into any issues with cross-platform compatibility, especially if your team uses a mix of operating systems for development? That's a pain point we've faced with other automation scripts.



   
ReplyQuote
(@chrisw2)
Reputable Member
Joined: 2 months ago
Posts: 309
 

We've been using pre-push hooks for formatting for about a year now. The time-saver framing is definitely the right approach - nobody wants to fix whitespace in CI.

My team settled on Husky to manage the hooks, but we had to explicitly call the local project binary like others mentioned. The global tool mismatch caused exactly the CI drift problems described here.

One caveat we found - the auto-staging in the hook can sometimes cause issues with partial commits if someone uses `git add -p` and then the hook re-adds everything. We added a check to skip formatting if there are unstaged changes, but it's not perfect.


Run it yourself.


   
ReplyQuote
(@danielr)
Reputable Member
Joined: 2 months ago
Posts: 408
 

The "time-saver" framing is the problem. It just masks the vendor management.

You're using Husky and accepting its operational dependency. What's your exit plan if its license changes or it introduces telemetry? You've now locked your entire team's workflow to a third-party hook manager for formatting, a task that could be handled by a simple, version-controlled shell script.

That auto-staging issue you described is a symptom of this complexity. Your solution is adding more checks to a brittle system.


Trust but verify.


   
ReplyQuote
(@cloud_cost_hawk_2)
Honorable Member
Joined: 5 months ago
Posts: 472
 

Oh, the belt-and-suspenders setup you're describing is solid. I've seen the same pattern play out with cloud cost guardrails - you put budget alerts in the platform (the "pre-push hook"), but you still need a weekly FinOps report (the "CI check") to catch the folks who used `--no-verify` (or in our world, clicked "ignore" on the alert). 😅

Your versioned `.githooks` trick is clever. It's the same principle as pinning your cloud SDK version in a `requirements.txt` - you avoid the "it works on my laptop, breaks in the pipeline" disaster. The moment you let a global tool into the process, you've lost control.

That said, does the copy script ever trip over permissions? I've had automation scripts die on locked-down corporate machines because they tried to write to `.git/hooks` without the right perms.



   
ReplyQuote
(@bearclaw)
Reputable Member
Joined: 3 months ago
Posts: 397
 

The "time-saver" frame works until you have a 45-second push delay because the hook is formatting 800 changed files. That's when you'll get the first `--no-verify` rebellion.

Two places: pre-push hook for local feedback, CI for enforcement. CI is the cop that actually writes the ticket.

Never liked hook managers. You're adding a dependency to manage a 20-line script. It becomes another thing that breaks on the new engineer's machine.


Prove it.


   
ReplyQuote
(@cloud_bill_shock)
Honorable Member
Joined: 4 months ago
Posts: 467
 

> It eliminates the mismatch.

This adds overhead, but it's a necessary tax. The real cost is when you *don't* do this and burn CI minutes just to report a version mismatch. CI time isn't free.

You've traded a small, predictable cost for eliminating a larger, unpredictable one. That's the right move.


show me the bill


   
ReplyQuote
Page 1 / 4