Count files changed since a commit
Get the number of distinct files that differ between a reference commit (a tag, SHA, or `origin/main`) and the working tree — for CI gates and PR-size summaries.
How to count files changed since a commit in each shell
git diff --name-only HEAD~5 | wc -l`HEAD~5` = 5 commits back. Other useful refs: `origin/main` (vs upstream), `v1.0..HEAD` (since tag), `--since="2 weeks ago"` (commit-range filter). `--name-only` returns ONE file per line — safe to pipe to `wc -l`.
git diff --name-only HEAD~5 | wc -lgit diff --name-only HEAD~5 | wc -l(git diff --name-only HEAD~5 | Measure-Object).Countpwsh pipeline captures git stdout as an array of strings (one per line) — `.Count` works directly without `Measure-Object`: `@(git diff --name-only HEAD~5).Count` is shorter. The `@()` array-wraps the result so single-file output still returns 1 (not the string itself).
git diff --name-only HEAD~5 | find /v "" /c`find /v "" /c` — same line-counting idiom as the standalone task. Counts every line that does NOT contain the empty string (i.e., every line). cmd has no `wc` analog.
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **`git diff` vs `git diff --cached` vs `git diff HEAD`** — three meanings, easy to confuse: `git diff` (no args) = working tree vs INDEX (uncommitted unstaged); `git diff --cached` (or `--staged`) = INDEX vs HEAD (uncommitted staged); `git diff HEAD` = working tree vs HEAD (both staged + unstaged combined). For "since a specific commit" use `git diff <ref>` which is "working tree vs <ref>". For commit-to-commit diff (no working tree involvement): `git diff <from> <to>` or `git diff <from>..<to>` — the two-dot form is equivalent for diff; the three-dot form `<from>...<to>` means "merge-base of from+to, vs to" (the GitHub PR convention).
- `--name-only` vs `--name-status` vs `--stat` — `--name-only` returns just paths (one per line, suitable for `wc -l`); `--name-status` prepends `A`/`M`/`D`/`R`/`C` for Added/Modified/Deleted/Renamed/Copied (good for "how many added vs deleted" gates: `git diff --name-status HEAD~5 | grep -c "^A"`); `--stat` produces a human-readable summary with insertions/deletions per file (NOT machine-parseable — for grepping, use `--numstat` which gives tab-separated `additions deletions filename` per line).
- Renames + binary files: by default `git diff --name-only` reports a renamed file ONCE under its new name (rename detection is on if files moved <50% changed — `git config diff.renames true` is default). To force NO rename detection (count the delete + add separately, giving 2 instead of 1): `git diff --no-renames --name-only`. Binary files appear in `--name-only` output but show "Binary files differ" in `--stat`. `git diff --shortstat HEAD~5` is the most compact "X files changed, Y insertions(+), Z deletions(-)" one-liner.
- pwsh array semantics gotcha: `git diff --name-only` returning 0 files leaves `$x` as `$null` (NOT empty array) — `($null | Measure-Object).Count` is `0` but `$null.Count` THROWS (or returns 0 silently depending on pwsh version). Always wrap: `@(git diff --name-only HEAD~5).Count`. Same trap for: any external command output, `Get-ChildItem` with no matches, `Where-Object` that filters out everything. Set `$ErrorActionPreference = "Stop"` to surface these instead of swallowing silently.
Related commands
Related tasks
- List modified files in a git repo— Print the paths of files with uncommitted changes (staged or unstaged) — for pre-commit hooks, CI lint-only-changed gates, and "what am I about to commit".
- List staged changes— Print files (or the full diff) of what's currently in the git index — what `git commit` will commit if you run it now.
- Count lines in a file— Get the line count of one or more files, recursively or in a single command.
- Show the current git branch— Print the name of the branch HEAD points at — used in shell prompts, CI build labels, and "deploy what's on the branch" scripts.