Git post-merge hook
Generate post-merge hook: reinstall deps when package-lock changes, update submodules.
Git post-merge hooks: automate the boring stuff after every pull
The post-merge hook fires locally, on the developer's machine, immediately after a successful git merge โ including the implicit merge performed by git pull. Unlike pre-* hooks, it cannot block anything; the merge has already happened. Its purpose is purely reactive: react to the new working-tree state and run the chores a human would otherwise forget. It receives a single argument, $1, which is 1 if the merge was a squash and 0 otherwise. Standard input is empty.
The classic use case is dependency drift. You pull, your teammate added a package, and the next time you run the app it crashes with Cannot find module 'lodash'. A two-line post-merge hook detects the change and runs the installer automatically.
Skeleton script
#!/bin/bash
# .git/hooks/post-merge (chmod +x)
changed_files=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)
run_if_changed() {
echo "$changed_files" | grep --quiet "$1" && eval "$2"
}
run_if_changed "package-lock.json" "npm ci"
run_if_changed "pnpm-lock.yaml" "pnpm install --frozen-lockfile"
run_if_changed "prisma/schema.prisma" "npx prisma generate"
run_if_changed ".env.example" "echo '.env.example changed โ review your .env'"
run_if_changed "docker-compose.yml" "docker compose up -d"
Common patterns
- Install dependencies when lockfiles change (
package-lock.json,pnpm-lock.yaml,yarn.lock,Pipfile.lock,poetry.lock,Gemfile.lock,go.sum,Cargo.lock). - Regenerate clients when an OpenAPI spec, Prisma schema, GraphQL schema, or protobuf file changes.
- Apply migrations automatically in the development database when a new file lands in
migrations/. - Restart dev services (Docker Compose, Tilt, Skaffold) when their config files change.
- Warn about
.env.exampledrift so a missing variable surfaces immediately, not three hours into debugging. - Update submodules with
git submodule update --init --recursive.
Detecting what changed
Inside the hook, two refs are useful: ORIG_HEAD (where you were before the merge) and HEAD (where you are now). The standard idiom is:
git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD
diff-tree is faster than git diff because it skips the working tree. The -r flag recurses into subdirectories; --name-only trims output to bare paths; --no-commit-id avoids printing the commit headers in front of each diff. Pipe the result into grep or, for performance, read it into a bash array and match locally without forking.
Hook managers: husky, lefthook, simple-git-hooks, pre-commit
Because the .git/hooks/ directory is not committed, every team eventually adopts a manager that ships hooks via files inside the repo. Husky (Node, by Typicode) is the market leader for JavaScript projects; it stores hooks under .husky/ and reconfigures core.hooksPath on npm install. Lefthook (Go, by Evil Martians) is the fastest of the bunch โ a single binary, YAML config, parallel command execution, and language-agnostic. simple-git-hooks (Anton Krasanov) is zero-dependency and reads hook commands from package.json. pre-commit (Python) dominates Python and polyglot repos thanks to its huge registry of reusable hooks.
Whichever you pick, document it in the README: developers must run the manager's bootstrap command once (npx husky install, lefthook install) before the hooks become active.
FAQ
Does post-merge run after git pull? Yes โ pull is a fetch followed by merge, and the merge step triggers post-merge. If you pull with --rebase, the hook does not fire; use post-rewrite instead.
Can the hook fail without breaking the merge? The merge has already completed by the time the hook runs, so any exit code is purely informational. Print loud red errors so the developer notices.
Husky or Lefthook? Lefthook if startup performance matters (Go binary vs Node startup), Husky if your team is already deep in the Node ecosystem and you want zero extra tooling.
Does post-merge run on a fast-forward? Yes โ Git still records a merge event even when no merge commit is created.
Will it slow down every pull? Only if you make it slow. Wrap heavy commands in if blocks that fire only when relevant files actually changed, and avoid synchronous network calls.
Related Tools
Handwriting Generator
Convert typed text into an image with handwriting appearance. Useful for adding a personal touch to digital work.
Resume Generator
Fill a simple printable A4 CV from a form with personal data, education and experience.
Favicon Generator
Generate a favicon from text/emoji in all common sizes (16, 32, 48, 64, 192, 512). PNG download.