git diff --exit-code: Fail CI on Uncommitted Changes
git diff --exit-code makes git diff return exit status 1 whenever there are differences, so a CI job fails if a generated or formatted file was left uncommitted.
This is the canonical "did you commit the generated output?" gate. Run the generator or formatter, then let git diff --exit-code turn any residual change into a red build.
What it does
git diff normally exits 0 regardless of output. With --exit-code it exits 1 when there are differences and 0 when the tree is clean. Against the working tree it catches unstaged changes; add --cached (or --staged) to check staged changes. The related --quiet implies --exit-code and suppresses output.
Common usage
# fail if formatting was not committed
npm run format
git diff --exit-code
# fail if the lockfile is stale after install
npm ci
git diff --exit-code package-lock.json
# quiet gate (no diff printed)
go generate ./... && git diff --quiet || (git diff && exit 1)Options
| Flag | What it does |
|---|---|
| --exit-code | Exit 1 if there are differences, 0 if none |
| --quiet | Implies --exit-code and prints nothing |
| --cached / --staged | Compare the index to HEAD instead of the working tree |
| --stat | Also print a summary of what changed |
| -- <path> | Limit the check to specific files (e.g. a lockfile) |
In CI
The universal recipe: run the tool that should have already been run (formatter, codegen, dependency install), then git diff --exit-code. If the developer forgot to commit the result, the working tree is dirty and the job fails. Untracked new files escape git diff, so add git add -A first or use git status --porcelain to catch them too.
Common errors in CI
A gate that never fails is usually because the change created a new untracked file (git diff ignores those) or the generator wrote outside the repo; run git add -A before the diff. Line-ending churn ("the whole file changed") is a CRLF/autocrlf mismatch, not real drift; set core.autocrlf=false on the runner. --exit-code prints the diff by default; pair it with --stat for a compact failure log.