Files
small-scripts/specs/git-status-report.spec.md
Paul O'Reilly 5230e54236 Add git-status-report: recursive git repo scanner with status reporting
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>
2026-03-13 11:13:59 +13:00

104 lines
3.5 KiB
Markdown

# 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
1. **Discovery phase:** Walk `DIRECTORY` recursively, identifying directories that contain a `.git` folder. Stop descending into a directory once a `.git` is found (don't scan nested repos inside a git worktree).
2. **Status phase:** For each discovered repo:
a. Run `git status --porcelain` to detect uncommitted changes (staged, unstaged, untracked).
b. For each changed file, compute the character-level diff: `+N` added, `-N` removed. For untracked files, count all characters as `+N`. For deleted files, count all characters as `-N`.
c. Run `git remote` to list remotes. For each remote, run `git rev-list --left-right --count <branch>...<remote>/<remote-branch>` to determine ahead/behind counts.
3. **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 / -N` character counts
- Ahead/behind status per remote/branch
- **Repos with remote divergence** (ahead or behind) but no local changes still get a section showing the ahead/behind status.
4. **Exit code:**
- `0` — all repos clean and in sync
- `1` — 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 <dir>" 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
```