Config-based Git hooks

Git 2.54 brings hooks into your config

May 01, 2026

Git hooks let you run scripts automatically on commit, push, or merge. But they have always had a few rough edges: the scripts live in .git/hooks/, which is never committed to the repository, each event can only have one script, and there is no built-in way to see what hooks are active or where they come from.

Git 2.54 addresses all three with config-based hooks. You can now define hooks directly in your Git configuration files.

Config-based hooks in Git 2.54

Instead of placing an executable script at .git/hooks/pre-commit, you can now write this in your .gitconfig or .git/config:

[hook "lint"]
    event = pre-commit
    command = ./hooks/pre-commit-lint

The hook.<name>.event key specifies which Git event triggers the hook, and hook.<name>.command is the command to run. The name (lint here) is just a label, it can be anything you like.

Because this is standard Git config, it can live in any of the usual config scopes:

  • - ~/.gitconfig, applies to all your repos globally
  • - .git/config, applies to a single repository only
  • - /etc/gitconfig, applies system-wide

Multiple hooks per event

Previously, each hook event could only have one script. If you wanted to run both a linter and a secret scanner before a commit, you needed a wrapper script that called both. With config-based hooks, you just define them separately:

[hook "lint"]
    event = pre-commit
    command = ./hooks/pre-commit-lint

[hook "no-leaks"]
    event = pre-commit
    command = ./hooks/pre-commit-secrets

Git runs them in the order it encounters them in the config. The traditional script at .git/hooks/pre-commit still works too and runs last, so existing setups are unaffected.

See what's active

Use git hook list to inspect all configured hooks for an event and see which config scope each one comes from:

$ git hook list pre-commit
global    lint      ./hooks/pre-commit-lint
local     no-leaks  ./hooks/pre-commit-secrets

Setting it up for a team

The actual hook scripts should be committed to the repository in a hooks/ directory. Make sure each script has the executable bit set:

mkdir hooks
chmod +x hooks/pre-commit-lint

Then each team member needs to register the hooks in their local .git/config after cloning. This is two commands per hook:

git config --local hook.lint.event pre-commit
git config --local hook.lint.command ./hooks/pre-commit-lint

Put these commands in your project's setup script — a Makefile, justfile, or a simple setup.sh — so new team members only need to run it once after cloning.

# Makefile
setup:
	git config --local hook.lint.event pre-commit
	git config --local hook.lint.command ./hooks/pre-commit-lint

This is not automatic

Unlike Husky, which wires itself up via npm's prepare script on every npm install, Git does not run any setup automatically on clone. The one-time setup command is a conscious trade-off: it works for any language stack (Python, Go, Rust) without requiring Node.js or any additional tooling.

Disabling a hook per repository

If a hook is defined globally in ~/.gitconfig but you want to opt a specific repository out, you can disable it locally without removing the global config:

git config --local hook.lint.enabled false

This is particularly useful for personal global hooks like a secrets scanner or a commit message linter that you want everywhere except in a specific repo.

Config-based hooks are one of those small quality-of-life improvements that quietly make Git more self-contained. The hooks themselves are now first-class config: composable, inspectable with git hook list, and easy to disable per repo without touching anything globally.

Share

Comments (0)