A TUI dashboard for monitoring git repository status. Inspired by kando-tui, Gitocular borrows the visual language of a kanban board. Repos are sorted into columns by their sync state, so you can see at a glance which ones need attention and which are clean.
Managing dozens of repos means constantly asking "did I push that?", "is this one behind?", "do I have uncommitted work somewhere?". Gitocular answers all of those questions in a single view. Repos that need work land in the leftmost columns; repos that are clean sit on the right. The further left a card is, the more urgently it needs your attention.
↓ Behind 1 * Uncommitted 1 ✓ In Sync 1 ↑ Ahead 1
────────────────────────── ─────────────────────────── ────────────────────────── ──────────────────────────
┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ╭─────────────────────────╮ ╭────────────────────────╮ ╭────────────────────────╮
┃ ▸ api-server ┃ │ dotfiles │ │ cli-tools │ │ blog │
┃ main ┃ │ main │ │ main │ │ main │
┃ ↓3 1pr 3i ┃ │ *5 │ │ ✓ 2i │ │ ↑2 │
┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ╰─────────────────────────╯ ╰────────────────────────╯ ╰────────────────────────╯
Gitocular uses your terminal's default foreground and background and the eight standard ANSI color slots. No background palette, no RGB, no 256-color or bright-color overrides, so it inherits whatever theme you already use.
Color carries the numbers, not the names: ↓ red, ↑ blue, * yellow, open
PRs green, open issues red. A repo with no remote is flagged !. The selected
card gets a thick border and a ▸, so selection stays visible on terminals that
render bold or dim weakly.
Card height depends only on locally-readable facts, so the board does not reflow
when forge results arrive. Press c for compact view, one repo per row, or start
there with gitocular -c. Compact sizes each column to its content and gives
every count type its own column, so *N reads straight down the list; ✗ marks
a failed fetch and ? a forge that did not answer. Narrow terminals show one or
two columns at a time.
Point Gitocular at a directory and it finds all git repos one level deep, classifies each into one of four columns:
| Column | Meaning |
|---|---|
| Behind | Remote has commits you haven't pulled |
| Uncommitted | You have local changes (dirty working tree) |
| In Sync | Clean and up to date, in every checkout |
| Ahead | You have commits that haven't been pushed |
Column priority is Behind > Uncommitted > Ahead > In Sync, applied across the
main checkout and every worktree. Cards show raw counts; Enter names the
states.
On startup Gitocular fetches every repo with a remote in the background: 8
threads, 15-second timeout, with GIT_TERMINAL_PROMPT=0 and
ssh -o BatchMode=yes so a stuck credential helper cannot block the dashboard.
Pull and push also run in background threads.
Gitocular shows open PRs (green), open issues (red) and fork status on each card. Set a token to enable it:
| Forge | Token |
|---|---|
| GitHub | GITHUB_TOKEN, or falls back to gh auth token |
| GitLab | GITLAB_TOKEN |
| Codeberg | CODEBERG_TOKEN or GITEA_TOKEN |
| Gitea | GITEA_TOKEN |
export GITHUB_TOKEN="ghp_..."
export GITLAB_TOKEN="glpat-..."
export GITEA_TOKEN="..."API calls run on a separate thread pool (4 workers) and won't block navigation.
Gitocular recognizes GitHub, GitLab, and Codeberg by their hostnames automatically. For self-hosted Gitea or GitLab instances, register the host via environment variables:
| Variable | Purpose |
|---|---|
GITEA_HOSTS |
Comma-separated list of Gitea/Forgejo hostnames (e.g. localhost:3030,gitea.lan) |
GITLAB_HOSTS |
Comma-separated list of self-hosted GitLab hostnames (e.g. gitlab.corp.com) |
export GITEA_HOSTS="localhost:3030"
export GITLAB_HOSTS="gitlab.corp.com"The URL scheme (http or https) is inferred from the git remote URL, so
http:// remotes will use plain HTTP for API calls.
GitHub Enterprise is not currently supported. Only github.com is recognized.
Each forge type uses a single token (GITEA_TOKEN, GITLAB_TOKEN), so if you
have multiple self-hosted instances of the same type they will share the same
credentials.
A worktree is a checkout of a different branch sharing one repository. They all have a branch out at once, so Gitocular treats each as work the board owns:
- Standard worktrees group under their main repo. One whose parent lives outside
the scanned directory becomes its own card, titled
parentrepo [branch] - Bare containers (
.git→.bare) are scanned one level deeper, and gitflow namespace directories (feat/,release/) one deeper again - A repo's column reflects the worst state across every checkout, so a clean main tree does not hide a worktree with unsaved work. Only local facts feed this, so a repo never changes column when the network replies
- Counts stay per-checkout. A card reports its main tree, names the worktree most
in need of attention, and summarizes the rest as
↳ +N more p,Pandsact on a checkout, so a repo with worktrees asks which one, pre-selected to whichever explains its column
Press v to cycle the sort order within each column:
- Name (alphabetical, default)
- PRs (most open pull requests first)
- Issues (most open issues first)
| Key | Action |
|---|---|
h / ← |
Move to previous column |
l / → |
Move to next column |
j / ↓ |
Move down within column |
k / ↑ |
Move up within column |
Tab |
Cycle through all repos (wraps) |
Shift+Tab |
Reverse cycle through all repos |
| Key | Action |
|---|---|
Enter |
Open detail overlay |
p |
Pull (asks which checkout if the repo has worktrees) |
P |
Push (asks which checkout if the repo has worktrees) |
s |
Open a shell (asks which checkout if the repo has worktrees) |
r |
Refresh (re-scan and fetch) |
c |
Toggle compact list view |
v |
Cycle sort mode |
? |
Toggle help screen |
Esc / q |
Close overlay or quit |
Ctrl+C |
Force quit |
In the detail and help overlays, j/k or ↑/↓ scroll by one line,
PageUp/PageDown scroll by ten lines, and Home/End jump to the top or
bottom. Scrolling is clamped to the content, so End lands on the last line and
k steps straight back up from it. In repository details, Tab and Shift+Tab
switch repositories and reset the scroll position; closing help resets its own.
# Requires Rust 1.86+
cargo install --path .# Launch the TUI (scans current directory)
gitocular
# Launch the TUI for a specific directory
gitocular -p ~/projects
# Start in compact list view; `c` toggles it at any time
gitocular -cGitocular can also be used non-interactively. All commands accept
-p/--path to set the scan directory (defaults to .) and --json
for machine-readable output. -c/--compact is dashboard-only and is
rejected next to a subcommand rather than silently ignored.
# One-line summary of repo states
gitocular status
# 0 behind, 3 uncommitted, 10 in sync, 1 ahead
# List repos grouped by sync state
gitocular list
# Any command supports --json for scripting
gitocular list --json
gitocular status --json
# Fetch all repos with remotes (8 concurrent threads)
gitocular fetch
# Pull all repos that are behind their upstream
gitocular pullstatus exits with code 1 if any repo is not in sync, making it useful
in scripts and CI checks. fetch and pull exit with code 1 if any
operation fails.
When a worktree rather than the repo itself puts it under a heading, list names
the checkout responsible:
Behind (5)
wt-behind-promote main
↳ wt-behind-promote-tracking tracking ↓3
In JSON, sync_state is the column and own_sync_state the main checkout alone,
with a worktrees array when there are any.
- ratatui + crossterm for the terminal UI
- clap for CLI argument parsing
- ureq as the HTTP client for forge APIs
- color-eyre for error reporting
See CHANGELOG.md.
Apache-2.0