Scans a directory tree for git repos and produces a concise report showing uncommitted changes (with +/- character counts) and ahead/behind status per remote. Supports --dryrun to preview discovered repos without running checks. Includes OpenSpec, implementation, and 26-assertion test suite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.5 KiB
git-status-report
Purpose
Recursively scan a directory tree for git repositories and produce a concise report showing uncommitted changes and remote sync status.
Usage
git-status-report [OPTIONS] [DIRECTORY]
Arguments
| Argument | Default | Description |
|---|---|---|
DIRECTORY |
. (current directory) |
Root directory to scan |
Flags
| Flag | Short | Description |
|---|---|---|
--dryrun |
-n |
List discovered repos without running status checks |
--help |
-h |
Show usage information |
Behaviour
-
Discovery phase: Walk
DIRECTORYrecursively, identifying directories that contain a.gitfolder. Stop descending into a directory once a.gitis found (don't scan nested repos inside a git worktree). -
Status phase: For each discovered repo: a. Run
git status --porcelainto detect uncommitted changes (staged, unstaged, untracked). b. For each changed file, compute the character-level diff:+Nadded,-Nremoved. For untracked files, count all characters as+N. For deleted files, count all characters as-N. c. Rungit remoteto list remotes. For each remote, rungit rev-list --left-right --count <branch>...<remote>/<remote-branch>to determine ahead/behind counts. -
Report phase: Print a grouped report:
- Clean repos are listed in a single summary line (count only) unless there are none.
- Dirty repos get a section each, showing:
- Repo path (relative to
DIRECTORY) - Each changed file with its status and
+N / -Ncharacter counts - Ahead/behind status per remote/branch
- Repo path (relative to
- Repos with remote divergence (ahead or behind) but no local changes still get a section showing the ahead/behind status.
-
Exit code:
0— all repos clean and in sync1— at least one repo has uncommitted changes or is out of sync
Dryrun Behaviour
When --dryrun is passed:
- Perform the discovery phase only
- Print each discovered repo path (relative to
DIRECTORY), one per line - Prefix output with
[dryrun] Would check N repositories: - Do NOT run any git status or remote checks
- Exit code is always
0
Edge Cases
| Scenario | Handling |
|---|---|
| No git repos found | Print "No git repositories found in " and exit 0 |
| Repo has no remotes | Skip remote sync section for that repo, show "(no remotes)" |
| Repo has detached HEAD | Show branch as (detached HEAD) and skip remote comparison |
| Remote branch doesn't exist | Show "(no upstream)" for that remote |
| Binary files changed | Show (binary) instead of character counts |
| Permission denied on subdirectory | Skip with warning to stderr, continue scanning |
| Nested git repos (submodules) | Stop at the outermost .git — don't descend further |
| Symlinked directories | Follow symlinks during discovery |
Examples
Clean repos
Scanned 5 repositories in ~/dev
✓ All 5 repositories are clean and in sync.
Mixed status
Scanned 5 repositories in ~/dev
── dirty: project-alpha ──
M src/main.py +42 / -17
M README.md +5 / -0
?? TODO.txt +120
↕ origin/main: 2 ahead, 0 behind
── dirty: infra-configs ──
D old-config.yaml -89
↕ origin/main: 0 ahead, 3 behind
✓ 3 repositories are clean and in sync.
Dryrun
[dryrun] Would check 5 repositories:
~/dev/project-alpha
~/dev/project-beta
~/dev/infra-configs
~/dev/scripts
~/dev/docs