Git pre-commit hook changed/added files
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A pre-commit hook runs before Git creates the commit, which makes it the right place to inspect exactly what is staged. The important detail is that hooks should usually work against the index, not the whole working tree. If you only want added or modified files, Git already exposes that information directly.
Read the Staged File List
Inside a pre-commit hook, the safest starting point is git diff --cached. The --cached flag tells Git to compare the staged index to the current HEAD, which means you are checking what will actually be committed.
This command means:
- '
--cachedreads staged content.' - '
--name-onlyprints only file paths.' - '
--diff-filter=AMlimits the result to added and modified files.'
That is usually the correct file set for linters, format checks, and repository policy rules.
Minimal Hook Example
Place a script in .git/hooks/pre-commit and make it executable. This example prints the staged added and modified files, then exits successfully.
Then mark it executable:
This skeleton is intentionally small. A good hook should be quick, predictable, and easy to debug.
Filter for Relevant File Types
Most teams do not want to run every tool on every file. A better pattern is to filter the staged set and run only the checks that matter for that commit.
That keeps the hook focused. If no Python files are staged, the hook exits immediately instead of wasting time.
Understand Index Versus Working Tree
One of the most common mistakes is checking on-disk files while the commit actually contains a different staged version. This happens when a developer stages part of a file and keeps more edits unstaged.
If your tool must inspect the exact staged content, read it from the index:
That command prints the version stored in the index, not whatever is currently sitting in the working tree. This distinction matters for strict validation or content-based policies.
Example Policy: Block Large Newly Added Files
Hooks are useful for more than linting. They are also a good place to stop common repository mistakes before they reach history.
This version checks only newly added files, which is often the right policy for blocking accidental binaries or large generated artifacts.
Share Hooks Deliberately
Files inside .git/hooks are local to one clone and are not versioned by default. If the team needs shared hook behavior, keep hook scripts in the repository and point Git at that directory.
That makes the setup reproducible and easier to review. It also avoids the common problem where one developer has a hook and another does not.
Common Pitfalls
- Reading all working tree changes instead of only staged commit content.
- Forgetting
--diff-filterand accidentally processing deleted files. - Running heavy checks on every commit and training developers to bypass the hook.
- Breaking on filenames with spaces because variables are not quoted properly.
- Assuming a hook in
.git/hooksis automatically shared with the rest of the team.
Summary
- Use
git diff --cached --name-only --diff-filter=AMto target staged added and modified files. - Build hook logic around the index rather than the whole working tree.
- Filter by file type to keep the hook fast and relevant.
- Read staged blobs from the index when exact commit content matters.
- Version shared hooks explicitly with
core.hooksPathif the whole team should use them.

