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:

  1. Hybrid discovery — auto-scan children, plus aliases, groups, and filters
  2. Direct passthroughgg status means git status everywhere selected
  3. Reserved builtins — overview, list, sync, update, scaffold, etc. win over passthrough
  4. Scriptable — JSON/NDJSON, stable exit aggregation, dry-run

What’s in 1.1.0

  • Semantic colors in overview / sync / stale tables (tree, age, ahead/behind)
  • [[auto_enroll]] + gg update to enroll new repos into aliases / groups / tags
  • Correct --root / selection / --dry-run behavior 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

ScopePath
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 update does not use --root / -i selection; 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

CommandAliasPurpose
overviewovDashboard: branch, dirty/clean, ahead/behind, age, in-progress (semantic colors)
listlsDiscovered / selected repos (--refresh bypasses cache)
info [PATH]Detailed status; optional path still respects --only-* / --in when those are set
commits -n NTop-N commits across selection
worktreesWorktree listing
doctorEnvironment + repo health checks
stale --days NRepos with no commits newer than N days

Default (no subcommand) runs overview.

Multi-repo actions

CommandPurpose
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
updateEnroll repos from [[auto_enroll]] into aliases / groups / tags

Catalog & config

CommandPurpose
alias / groupManage path aliases and groups
configshow / path / get / set
remotesCatalog + add-to selected repos
hooksList packs / install into selection
init / scaffoldCreate a repo from a profile

Selection flags do not apply to catalog-only subcommands (alias list, config show, hooks list, …). See Targeting & flags.

Meta

CommandPurpose
completions <shell>Shell completions
man [--output FILE]Generate man page
versionPrint version
self-updateRelease 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

FlagEffect
--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
--refreshBypass discovery cache
--include-submodulesTreat gitfile submodules as repos

Status filters (probe each repo):

FlagKeeps
--only-dirty / --only-cleanWorking tree state
--only-ahead / --only-behindUpstream divergence
--only-stashedRepos with stashes
--only-detachedDetached HEAD

Execution & output

FlagEffect
-j / --jobsParallelism for passthrough, each, overview probes, and status filters
--fail-fastStop scheduling more work after first failure (passthrough / each)
--dry-runPrint planned actions; also honored by update, hooks install, remotes mutations, init/scaffold, alias/group/config set
-q / --quietSuppress informational output; hides successful passthrough/each blocks
--timing / -vPer-repo timing on passthrough / each
--format human|json|ndjsonOutput shape
--color auto|always|neverColor
--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 path
  • gg-prompt — print dirty child count for prompts
  • completion wiring when gg is on PATH

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

flake.nix:

nix run github:chtnnh/git-gist -- version
nix profile install github:chtnnh/git-gist

Optional later: nixpkgs / NUR PRs.

Release checklist

  1. cargo test --workspace and coverage gate (≥95%)
  2. Bump version in Cargo.toml + CHANGELOG.md + flake.nix
  3. Tag vX.Y.Z and push
  4. Verify Release assets (archives + installers)
  5. Confirm Homebrew tap formula updated
  6. cargo publish
  7. 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_version migrations 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

TypeNameTargetProxy
CNAMEggchtnnh.github.ioDNS 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”

  1. Confirm dig +short gg.chtnnhfoundation.orgchtnnh.github.io. and GitHub Pages IPs.
  2. Flush local DNS cache (macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder).
  3. Remember chtnnh.github.io/git-gist redirects to the custom domain — if the custom name fails, both URLs appear broken.
  4. Try another network or a private browser window.

Operator notes: docs/README.md.