Introduction
git-gist (gg) is a fast, cross-platform CLI that discovers git repositories under a root and runs git (or built-in insights) across them in parallel.
Current release: 1.1.0. User guide: https://gg.chtnnhfoundation.org/.
Design pillars:
- Hybrid discovery — auto-scan children, plus aliases, groups, and filters
- Direct passthrough —
gg statusmeansgit statuseverywhere selected - Reserved builtins — overview, list, sync, update, scaffold, etc. win over passthrough
- Scriptable — JSON/NDJSON, stable exit aggregation, dry-run
What’s in 1.1.0
- Semantic colors in
overview/sync/staletables (tree, age, ahead/behind) [[auto_enroll]]+gg updateto enroll new repos into aliases / groups / tags- Correct
--root/ selection /--dry-runbehavior across commands (see Targeting & flags)
Install
Cargo (crates.io / git)
cargo install git-gist --locked
# or from this repo:
cargo install --git https://github.com/chtnnh/git-gist --locked
cargo install --path . --locked
Homebrew
brew tap chtnnh/tap
brew install git-gist
# or: brew install chtnnh/tap/git-gist
Requires chtnnh/homebrew-tap. On Homebrew 6+, you may need brew trust chtnnh/tap once.
Nix
nix run github:chtnnh/git-gist -- version
nix profile install github:chtnnh/git-gist
# from a local checkout:
nix run . -- overview
deb / rpm
Download from GitHub Releases, or build locally:
cargo install cargo-deb cargo-generate-rpm
cargo build --release
cargo deb
cargo generate-rpm
Shell / PowerShell installer (cargo-dist)
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/chtnnh/git-gist/releases/latest/download/git-gist-installer.sh | sh
irm https://github.com/chtnnh/git-gist/releases/latest/download/git-gist-installer.ps1 | iex
Full operator guide: Packaging and packaging/README.md.
Quick start
cd ~/src # folder that contains many git checkouts
gg # overview (colored tree / age / sync)
gg list
gg status -sb
gg --only-dirty pull
gg -g work fetch
Scope to the current directory (honors --root; does not pull in out-of-root aliases):
gg ov --root .
Enroll new repos from watch rules in config:
gg update --dry-run
gg update
Generate completions:
gg completions zsh > ~/.zsh/completions/_gg
Man page:
gg man --output /usr/local/share/man/man1/gg.1
Configuration
Locations
| Scope | Path |
|---|---|
| Global | ~/.config/git-gist/config.toml (macOS: ~/Library/Application Support/git-gist/config.toml) |
| Local | .gg.toml or .git-gist.toml (ancestors of cwd) |
| Ignore | .ggignore at search root |
| Cache | ~/.cache/git-gist/discovery.json |
Example file: examples/config.toml.
Schema
schema_version = 1 — bump with migrations documented in CHANGELOG.
Keys: root, depth, jobs, ignore, aliases, groups, tags, remotes, profiles, hook_packs, theme, include_submodules, repo_overrides, auto_enroll.
gg config show
gg config get depth
gg config set depth 8
gg alias add api ~/src/api
gg group add work api web
CLI overrides for one invocation: --root, --depth, -j, --theme, --include-submodules.
Auto-enroll
Declare watch folders; gg update creates aliases for newly discovered git repos and adds them to the listed groups/tags:
[[auto_enroll]]
path = "/home/you/src/learning"
depth = 6
tags = ["learning"]
[[auto_enroll]]
path = "/home/you/src/oss"
depth = 3
groups = ["oss"]
gg update --dry-run # preview
gg update # write aliases / groups / tags
gg --format json update
Notes:
- Existing aliases are left in place; missing group/tag membership is repaired.
- Alias names prefer the directory basename, then a path-derived name, then numeric suffixes on collision.
- Missing watch roots are skipped with a warning.
gg updatedoes not use--root/-iselection; it only follows[[auto_enroll]]rules.
Themes
theme = "default" | "mono" | "vivid" (or --theme on the CLI). Overview / sync / stale tables use semantic cell colors for dirty trees, stale ages (≥30d / ≥90d), and ahead/behind drift.
Commands
See gg --help and per-command --help for the full flag list.
Insights
| Command | Alias | Purpose |
|---|---|---|
overview | ov | Dashboard: branch, dirty/clean, ahead/behind, age, in-progress (semantic colors) |
list | ls | Discovered / selected repos (--refresh bypasses cache) |
info [PATH] | Detailed status; optional path still respects --only-* / --in when those are set | |
commits -n N | Top-N commits across selection | |
worktrees | Worktree listing | |
doctor | Environment + repo health checks | |
stale --days N | Repos with no commits newer than N days |
Default (no subcommand) runs overview.
Multi-repo actions
| Command | Purpose |
|---|---|
each <shell…> | Run a shell command in each selected repo (--dry-run, -j, --fail-fast, -q) |
sync [--pull] | git fetch --all --prune; optional ff-only pull when clean and behind |
update | Enroll repos from [[auto_enroll]] into aliases / groups / tags |
Catalog & config
| Command | Purpose |
|---|---|
alias / group | Manage path aliases and groups |
config | show / path / get / set |
remotes | Catalog + add-to selected repos |
hooks | List packs / install into selection |
init / scaffold | Create a repo from a profile |
Selection flags do not apply to catalog-only subcommands (alias list, config show, hooks list, …). See Targeting & flags.
Meta
| Command | Purpose |
|---|---|
completions <shell> | Shell completions |
man [--output FILE] | Generate man page |
version | Print version |
self-update | Release probe / upgrade guidance |
Passthrough
Anything that is not a builtin is passed to git in each selected repo:
gg status -sb
gg pull --rebase
gg git -- status # escape hatch when a name collides with a builtin
Put global flags before the git verb: gg --dry-run status (not gg status --dry-run).
Exit code is non-zero if any selected repo fails (unless the selection is empty).
Targeting & flags
Global flags are defined once on gg and apply to selection-based commands.
Discovery & selection
| Flag | Effect |
|---|---|
--root <DIR> | Search root (overrides config root). Includes the root itself when it is a git repo. Aliases outside this root are not auto-included (use -i). |
--depth <N> | Max scan depth (0 = unlimited) |
-i / --in <TARGET> | Include alias, path, group, basename, or glob (repeatable) |
-x / --exclude <TARGET> | Exclude (repeatable) |
-g / --group <NAME> | Select a named group |
--tag <TAG> | Filter by config tag |
--refresh | Bypass discovery cache |
--include-submodules | Treat gitfile submodules as repos |
Status filters (probe each repo):
| Flag | Keeps |
|---|---|
--only-dirty / --only-clean | Working tree state |
--only-ahead / --only-behind | Upstream divergence |
--only-stashed | Repos with stashes |
--only-detached | Detached HEAD |
Execution & output
| Flag | Effect |
|---|---|
-j / --jobs | Parallelism for passthrough, each, overview probes, and status filters |
--fail-fast | Stop scheduling more work after first failure (passthrough / each) |
--dry-run | Print planned actions; also honored by update, hooks install, remotes mutations, init/scaffold, alias/group/config set |
-q / --quiet | Suppress informational output; hides successful passthrough/each blocks |
--timing / -v | Per-repo timing on passthrough / each |
--format human|json|ndjson | Output shape |
--color auto|always|never | Color |
--theme <NAME> | default, mono, vivid |
Which commands use selection?
Yes (discovery + filters): overview, list, info, commits, worktrees, doctor, stale, each, sync, hooks install, remotes add-to, git passthrough.
No (ignore --in / --root / only_*): update (uses auto_enroll paths), alias, group, config, hooks list, remotes list|add|remove, init/scaffold, self-update, completions, man, version.
Examples
# Current directory only
gg ov --root .
# One group, dirty repos only
gg -g work --only-dirty status -sb
# Preview enroll without writing config
gg update --dry-run
# Reach an alias outside --root
gg --root . --in elsewhere list
Shell integration
Source the helper for your shell from the shell/ directory:
gg.bash/gg.zsh/gg.fish/gg.sh
Provides:
gg-cd <alias>— jump to an aliased pathgg-prompt— print dirty child count for prompts- completion wiring when
ggis onPATH
Packaging & distribution
gg ships through multiple channels. End-user install commands live in the Install chapter; this page is the operator guide.
Channels
crates.io
cargo publish
# users: cargo install git-gist --locked
GitHub Releases (cargo-dist)
Push a tag vX.Y.Z. The generated Release workflow (from dist generate) uploads archives, checksums, git-gist-installer.sh / .ps1, and a Homebrew formula. Config lives in dist-workspace.toml.
Homebrew
Tap: chtnnh/homebrew-tap. cargo-dist publishes on each tag when HOMEBREW_TAP_TOKEN is set.
brew tap chtnnh/tap
brew install git-gist
Debian / RPM
Attached by .github/workflows/packages.yml after a Release is published. Locally: cargo deb / cargo generate-rpm (metadata in Cargo.toml).
Nix
nix run github:chtnnh/git-gist -- version
nix profile install github:chtnnh/git-gist
Optional later: nixpkgs / NUR PRs.
Release checklist
cargo test --workspaceand coverage gate (≥95%)- Bump version in
Cargo.toml+CHANGELOG.md+flake.nix - Tag
vX.Y.Zand push - Verify Release assets (archives + installers)
- Confirm Homebrew tap formula updated
cargo publish- Smoke-test:
brew upgrade git-gist, installer script,nix run,gg version
Cookbook
Overview for the current folder only
gg ov --root .
Update all dirty work repos
gg -g work --only-dirty pull --ff-only
Enroll new learning / OSS checkouts
# in global config
[[auto_enroll]]
path = "/Users/you/Desktop/tech/learning"
depth = 6
tags = ["learning"]
[[auto_enroll]]
path = "/Users/you/Desktop/tech/oss"
depth = 3
groups = ["oss"]
gg update --dry-run
gg update
gg --tag learning ov
Commit message hook everywhere
gg hooks install commit-msg-required
gg --dry-run hooks install noop # preview
JSON for scripting
gg --format json overview | jq '.[] | select(.dirty)'
gg --format json update | jq '.added'
Sync then show drift
gg -g work sync --pull
gg -g work --only-behind ov
Scaffold a service
gg init --profile default ./payments-api
gg --dry-run init --profile default ./scratch
Passthrough dry-run (flags before the verb)
gg --dry-run --timing status -sb
Deprecation policy
Until 1.0, flags and reserved command names may change with CHANGELOG notes.
From 1.0:
- Reserved builtin names are stable; removals require a major version
- Config
schema_versionmigrations must be backward compatible or documented - JSON field renames require a major version or dual-write period
- Announce deprecations for at least one minor release before removal when practical
Escape hatch gg git -- … remains stable for passthrough collisions.
Hosting this book
Published at https://gg.chtnnhfoundation.org/ via GitHub Pages (mdBook + Actions).
Fallback URL: https://chtnnh.github.io/git-gist/ (redirects to the custom domain once it is configured).
Preview locally
cargo install mdbook --locked
mdbook serve docs --open
Deploy
Pushes to main that change docs/ (or .github/workflows/docs.yml) deploy automatically via the Docs workflow. Manual:
gh workflow run docs.yml
Custom domain DNS
| Type | Name | Target | Proxy |
|---|---|---|---|
| CNAME | gg | chtnnh.github.io | DNS only (grey cloud) first |
- Target is
chtnnh.github.io, not…/git-gist. - After GitHub shows the domain verified and the certificate approved, enable Enforce HTTPS.
- If Cloudflare orange-cloud is enabled later, use SSL mode Full (not Flexible).
“DNS looks fine but the site won’t load”
- Confirm
dig +short gg.chtnnhfoundation.org→chtnnh.github.io.and GitHub Pages IPs. - Flush local DNS cache (macOS:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder). - Remember
chtnnh.github.io/git-gistredirects to the custom domain — if the custom name fails, both URLs appear broken. - Try another network or a private browser window.
Operator notes: docs/README.md.