{"id":51711007,"url":"https://github.com/npenkov/git-substatus","last_synced_at":"2026-07-16T21:43:09.037Z","repository":{"id":368481493,"uuid":"1275367908","full_name":"npenkov/git-substatus","owner":"npenkov","description":"Interactive TUI to view git status across many repos at once — parallel scan, filesystem-watch refresh, in-app diffs, custom actions","archived":false,"fork":false,"pushed_at":"2026-07-05T17:14:47.000Z","size":45,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-16T21:42:59.143Z","etag":null,"topics":["cli","git","git-status","gitoxide","ratatui","rust","terminal","tui"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/npenkov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-20T15:45:16.000Z","updated_at":"2026-07-05T17:14:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/npenkov/git-substatus","commit_stats":null,"previous_names":["npenkov/git-substatus"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/npenkov/git-substatus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npenkov%2Fgit-substatus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npenkov%2Fgit-substatus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npenkov%2Fgit-substatus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npenkov%2Fgit-substatus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/npenkov","download_url":"https://codeload.github.com/npenkov/git-substatus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npenkov%2Fgit-substatus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35559858,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","git","git-status","gitoxide","ratatui","rust","terminal","tui"],"created_at":"2026-07-16T21:43:08.404Z","updated_at":"2026-07-16T21:43:09.028Z","avatar_url":"https://github.com/npenkov.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# git-substatus\n\nAn interactive terminal UI (ratatui) that shows the git status of many repositories\nat once — designed for a directory full of worktrees/clones, and for living in a tmux\nside pane. It is the \"next level\" successor to a polling shell script: status is loaded\n**in parallel** and refreshed **only when the filesystem actually changes** (no polling).\n\n## Features\n\n- **Recursive discovery** of git repos under a root, configurable depth.\n- **Parallel scanning** (rayon) — repos fill in as they finish.\n- **Event-driven refresh** — a debounced filesystem watcher (notify) re-scans *only*\n  the repos that changed. Idle CPU is ~0%.\n- **Pure-Rust git** via [`gix`](https://github.com/GitoxideLabs/gitoxide) for status and\n  branch info (ahead/behind uses a single `git rev-list` per scan).\n- **In-app diffs** — select a file to see a colored unified diff (built with\n  [`imara-diff`](https://github.com/pascalkuthe/imara-diff), gix's own diff engine).\n- **Navigation** — expand/collapse file lists, scroll, dirty-only toggle, fuzzy filter.\n- **Custom actions** — run configurable shell commands against the selected repo\n  (open a tmux pane there, launch lazygit/nvim, etc.).\n\n## Install\n\n### Homebrew\n\n```sh\nbrew install npenkov/tap/git-substatus\n```\n\nOr tap first, then install:\n\n```sh\nbrew tap npenkov/tap\nbrew install git-substatus\n```\n\n### From source\n\n```sh\ncargo install --path .\n```\n\nThe binary `git-substatus` lands in `~/.cargo/bin`.\n\n## Usage\n\n```\ngit-substatus [ROOT] [--depth N] [--dirty-only] [--no-watch] [--config PATH]\n```\n\n- `ROOT` — parent directory to scan (default: current dir).\n- `--depth` — 1 = direct children (default), 2 = also grandchildren.\n- `--dirty-only` — start with only repos that have changes.\n- `--no-watch` — disable the filesystem watcher.\n- `--config` — path to the actions config (default `~/.config/git-substatus/config.toml`).\n\n### Keys\n\n| Key | Action |\n|-----|--------|\n| `j` / `k` / ↓ ↑ | move (or scroll the diff pane when it's focused) |\n| `l` / `Enter` / → | expand a repo; on a file, open its diff (reveals the detail panel) |\n| `h` / ← | collapse |\n| `Tab` | show/hide the detail panel (hidden by default) |\n| `d` | toggle dirty-only |\n| `/` | fuzzy filter by repo name (`Enter` keep, `Esc` clear) |\n| `a` | actions popup |\n| `r` | force rescan all |\n| `g` / `G` | top / bottom |\n| `Esc` | close the detail panel, or quit from the list |\n| `q` / `Ctrl-c` | quit |\n\nThe detail panel (diff / repo summary) is hidden by default — the repo list spans the\nfull width. Open a file or press `Tab` to reveal it.\n\n## Actions\n\nIf no config exists, built-in defaults are used (tmux split, tmux window, lazygit, nvim).\nCreate `~/.config/git-substatus/config.toml` to customize:\n\n```toml\n[[actions]]\nkey = \"t\"\nname = \"tmux split here\"\ncommand = \"tmux split-window -h -c {path}\"\nsuspend = false        # spawns in tmux; the TUI keeps running\n\n[[actions]]\nkey = \"g\"\nname = \"lazygit\"\ncommand = \"lazygit\"\ncwd = \"{repo}\"\nsuspend = true         # takes over this terminal; TUI is restored on exit\n\n[[actions]]\nkey = \"v\"\nname = \"nvim\"\ncommand = \"nvim {path}\"  # opens the selected file (or the dir if a repo is selected)\ncwd = \"{dir}\"\nsuspend = true\n```\n\nActions run in the context of the **selected item**. These placeholders are substituted\n(shell-escaped in `command`; raw in `cwd`):\n\n- `{path}` — the selected item: the file's absolute path when a file row is selected,\n  otherwise the repo root.\n- `{dir}` — a directory to run in: the file's parent directory when a file is selected,\n  otherwise the repo root. This is the default `cwd`.\n- `{repo}` — the repo root.\n\n`suspend = true` for interactive programs that need this terminal; `false` for things\nthat appear elsewhere (tmux panes/windows/popups).\n\n## tmux\n\nOpen it in a 50-column right-hand pane for the current directory:\n\n```tmux\nbind C-s split-window -h -l 50 \"cd '#{pane_current_path}' \u0026\u0026 git-substatus\"\n```\n\n## License\n\n[MIT](LICENSE) © Nick Penkov\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnpenkov%2Fgit-substatus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnpenkov%2Fgit-substatus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnpenkov%2Fgit-substatus/lists"}