Skip to content

affromero/gitpane

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

200 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gitpane

Multi-repo Git workspace dashboard for the terminal

CI crates.io GitHub Release Downloads License: MIT Platform Language Socket


Monitor all your repos at a glance — branch, dirty state, ahead/behind, active worktrees, changed files, and commit history — without leaving the terminal.

gitpane demo

Install

cargo install gitpane

That's it. No cloning, no building from source. Runs on Linux, macOS, and Windows.

Don't have Rust? Download a pre-built binary from GitHub Releases — single static binary, zero dependencies.

# macOS (Apple Silicon)
curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-aarch64-apple-darwin.tar.gz
tar xzf gitpane-aarch64-apple-darwin.tar.gz && sudo mv gitpane /usr/local/bin/

# macOS (Intel)
curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-x86_64-apple-darwin.tar.gz
tar xzf gitpane-x86_64-apple-darwin.tar.gz && sudo mv gitpane /usr/local/bin/

# Linux (x86_64, statically linked)
curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-x86_64-unknown-linux-musl.tar.gz
tar xzf gitpane-x86_64-unknown-linux-musl.tar.gz && sudo mv gitpane /usr/local/bin/

# Linux (ARM64)
curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-aarch64-unknown-linux-gnu.tar.gz
tar xzf gitpane-aarch64-unknown-linux-gnu.tar.gz && sudo mv gitpane /usr/local/bin/

Then run:

gitpane                     # Scans ~/Code by default
gitpane --root ~/projects   # Scan a specific directory

Update

cargo install gitpane       # Same command — overwrites the old binary

If you installed from a GitHub Release, re-download the latest binary for your platform using the same commands from the install section above.

Why gitpane?

If you work across multiple repositories — microservices, monorepos with submodules, a mix of projects — you know the pain of cd-ing into each one to check status. Existing TUI tools focus on one repo at a time:

Tool Multi-repo Real-time watch Worktrees Mouse Commit graph Split diffs Push/Pull
gitpane Yes Yes Yes Yes Yes Yes Yes
lazygit No No No Yes Yes Yes Yes
gitui No No No Yes Yes Yes Yes
tig No No No No Yes No No
git-delta No No No No No Yes (pager) No
grv No No No Yes Yes No No
git-summary Yes (list only) No No No No No No
mgitstatus Yes (list only) No No No No No No
gita Yes (CLI only) No No No No No Yes

lazygit and gitui are excellent for deep single-repo work — staging hunks, interactive rebase, conflict resolution. gitpane is the workspace-level dashboard — see everything across all repos, drill into anything, never leave the terminal. They complement each other.

Screenshots

Three-panel overview

Repos on the left show branch, dirty state (*), ahead/behind arrows (↑↓), worktree count (), dirty submodules (), unpushed submodule pointer (), and file count. Changes in the middle. Commit graph on the right.

Three-panel overview

Split diff view

Click a changed file (or press Enter) to see its diff side-by-side. File list stays navigable on the left.

Split diff view

Commit detail drill-down

Click a commit in the graph to see its files. Click a file to see the commit diff. Layered Esc dismissal: diff → files → graph.

Commit detail drill-down

Features

  • Multi-repo overview — Scans ~/Code (configurable) for git repos; shows branch, dirty indicator (*), ahead/behind arrows (↑↓), worktree count (), dirty submodule (), unpushed submodule pointer (), and change count
  • Worktree awareness — Shows the number of linked git worktrees per repo (⎇2). In the agentic AI era, tools like Claude Code create worktrees for parallel development — gitpane lets you see at a glance which repos have active parallel work
  • Real-time filesystem watching — Status updates within ~500ms of any file change via notify
  • Commit graph — Lane-based graph with colored box-drawing characters, up to 200 commits
  • Split diff views — Click a file to see its diff side-by-side; click a commit to see its files and per-file diffs
  • Full mouse support — Click to select, right-click for context menu, scroll wheel everywhere
  • Push / Pull / Rebase — Right-click context menu with ahead/behind-aware git operations (explicit origin <branch> for reliability)
  • Add & remove repos — Press a to add any repo with tab-completing path input; d to remove; R to rescan
  • Sort repos — Cycle between alphabetical and dirty-first with s
  • Copy to clipboard — Press y to copy selected item from any panel (OSC 52)
  • Configurable — TOML config for root dirs, scan depth, pinned repos, exclusions, frame rate
  • Responsive layout — Three horizontal panels on wide terminals, vertical stack on narrow ones
  • Cross-platform — Linux, macOS, Windows

Keybindings

Global

Key Action
? Toggle keybindings help overlay
Tab / Shift+Tab Cycle focus between panels
r Refresh all repo statuses
R Rescan directories for repos (clears exclusions)
g Reload git graph for selected repo
a Add a repo (opens path input with tab completion)
d Remove selected repo (with confirmation)
s Cycle sort order (Alphabetical / Dirty-first)
y Copy selected item to clipboard
q Quit (or close diff if one is open)
Esc Navigate back through panels, then quit

Repos panel

Key Action
j / Next repo
k / Previous repo

Changes panel

Key Action
j / Next file
k / Previous file
Enter Open split diff view
Esc / h / Close diff view

Graph panel

Key Action
j / Next commit / file
k / Previous commit / file
h / l Scroll graph left / right
Enter Open commit files / file diff
Esc Close diff → close files → back
/ Search commits (message, author, short ID)
n / N Next / previous search match
f Toggle first-parent mode
c Collapse / expand branch
H Expand all collapsed branches

Mouse

Action Effect
Left click Select item, switch panel focus
Click selected row Open diff / commit detail
Right click (repo list) Context menu (push, pull, copy path)
Scroll wheel Navigate lists or scroll diffs

Path input (a)

Key Action
Tab Autocomplete directory path (cycles matches)
Enter Add the repo
Esc Cancel
Ctrl+A / Home Cursor to start
Ctrl+E / End Cursor to end
Ctrl+U Clear line before cursor

Configuration

gitpane resolves its config file in this order (first existing file wins):

  1. $GITPANE_CONFIG (if set, treated as the full path; this overrides everything below and is also the save target)
  2. $XDG_CONFIG_HOME/gitpane/config.toml (if $XDG_CONFIG_HOME is set and absolute)
  3. ~/.config/gitpane/config.toml (the XDG default, on every platform)
  4. The platform-native location:
Platform Path
Linux ~/.config/gitpane/config.toml (same as 3)
macOS ~/Library/Application Support/gitpane/config.toml
Windows %APPDATA%\gitpane\config\config.toml

If no file is found at any candidate path, gitpane uses the built-in defaults (root_dirs = ["~/Code"], scan_depth = 2). When saving after loading a file, gitpane writes back to the loaded path. When saving from defaults, it writes to $GITPANE_CONFIG, $XDG_CONFIG_HOME/gitpane/config.toml, ~/.config/gitpane/config.toml, or the platform-native location, in that order.

gitpane logs the resolved path at startup (tracing info level on stderr).

# Directories to scan for git repositories
root_dirs = ["~/Code", "~/work"]

# Maximum directory depth for repo discovery
scan_depth = 2

# Always show these repos at the top
pinned_repos = ["~/Code/important-project"]

# Skip repos matching these directory names
excluded_repos = ["node_modules", ".cargo", "target"]

[watch]
debounce_ms = 500        # Filesystem change debounce (ms)
poll_local_secs = 5      # Local status poll interval (catches missed watcher events)
poll_fetch_secs = 30     # Remote fetch poll interval (updates ahead/behind from origin)

[ui]
frame_rate = 10              # Terminal refresh rate (fps)
check_for_updates = true     # Check for new versions on startup
update_position = "top-right" # Update notification position ("top-right" or "top-left")

[graph]
branches = "all"         # Branch filter: "all", "local", "remote", or "none"
label_max_len = 24       # Max length for branch/tag labels
show_stats = true        # Show +N/-M diff stats per commit

See examples/config.toml for a fully annotated example.

Troubleshooting

gitpane shows no repositories

Run through these in order:

1. Check that gitpane is reading your config file.

gitpane prints the resolved config path at startup. Run gitpane with stderr captured:

RUST_LOG=gitpane=info gitpane 2>/tmp/gitpane.log
cat /tmp/gitpane.log

You should see loaded config path=... or no config file found, using defaults. If gitpane is not loading the file you expected, check the candidate paths in Configuration. On macOS, ~/.config/gitpane/config.toml works as well as the native ~/Library/Application Support/gitpane/config.toml.

To force a specific file:

GITPANE_CONFIG=/path/to/config.toml gitpane

You can also bypass the config entirely to confirm repo discovery:

gitpane --root "$HOME/src"

If --root finds your repos but the config does not, the file path or config contents are the likely issue.

2. Check that scan_depth is large enough.

scan_depth is the maximum directory depth from each entry in root_dirs at which gitpane will look for a .git directory. For a layout like ~/src/github.com/<owner>/<repo>/.git, the .git lives at depth 4, so you need scan_depth = 4 (or higher). Counting from the root:

~/src                                       depth 0
~/src/github.com                            depth 1
~/src/github.com/<owner>                    depth 2
~/src/github.com/<owner>/<repo>             depth 3
~/src/github.com/<owner>/<repo>/.git        depth 4

3. Check for symlinks in your tree.

The scanner does not follow symlinks. If any directory along the path to a repo is a symlink (for example ~/src/github.com pointing at /mnt/code/github.com), repos under it will be skipped.

find    "$HOME/src" -maxdepth 4 -name .git -type d -print
echo '... with -L (follows symlinks):'
find -L "$HOME/src" -maxdepth 4 -name .git -type d -print

If the second command finds repos and the first doesn't, that's the cause. Workarounds: point root_dirs at the symlink target directly, or list specific repos in pinned_repos.

4. Check whether .git is a directory or a file.

Linked git worktrees and some submodule layouts store .git as a file containing a gitdir: pointer, not a directory. The scanner only matches .git directories. To check one repo:

test -d "$HOME/src/github.com/affromero/gitpane/.git" && echo dir \
 || test -f "$HOME/src/github.com/affromero/gitpane/.git" && echo file

If it prints file, add the repo via pinned_repos instead, or open it explicitly with gitpane --root /path/to/parent.

5. Verbose logging.

RUST_LOG=gitpane=debug gitpane 2>/tmp/gitpane.log

Then inspect /tmp/gitpane.log for any errors during config load or repo scanning.

Architecture

┌──────────────────────────────────────────────────────────┐
│                     tokio runtime                        │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────────┐   │
│  │ Event    │→ │ Action   │→ │ Components            │   │
│  │ Loop     │  │ Dispatch │  │  RepoList             │   │
│  │ (tui.rs) │  │ (app.rs) │  │  FileList (split diff)│   │
│  └──────────┘  └──────────┘  │  GitGraph (drill-down)│   │
│       ↑                      │  ContextMenu          │   │
│  ┌──────────┐                │  PathInput             │   │
│  │ notify   │                │  StatusBar             │   │
│  │ watcher  │                └──────────────────────┘   │
│  └──────────┘                                           │
│       ↑              ┌───────────────────────┐           │
│  filesystem          │ git2 (spawn_blocking) │           │
│  changes             │  status · graph       │           │
│                      │  commit_files · fetch │           │
│                      └───────────────────────┘           │
└──────────────────────────────────────────────────────────┘
  • ratatui + crossterm — TUI rendering with full mouse support
  • git2 (libgit2) — Branch, status, ahead/behind, graph, commit diffs
  • notify — Filesystem watching with configurable debounce
  • tokio — Async runtime; git queries run in spawn_blocking to keep the UI responsive

Message-passing architecture: terminal events → actions → component updates → render. Each component implements a Component trait with draw, handle_key_event, handle_mouse_event, and update.

Development

just run           # Build and run
just test          # Run test suite (90 tests)
just fmt           # Format code
just lint          # Run clippy
just ci            # fmt + lint + test (mirrors CI pipeline)

Project structure

src/
├── main.rs              # Entry point, CLI parsing
├── app.rs               # Main loop, action dispatch, layout
├── action.rs            # Action enum (message passing)
├── event.rs             # Terminal event types
├── tui.rs               # Terminal setup, event loop
├── config.rs            # TOML config load/save
├── watcher.rs           # Filesystem watcher → repo index mapping
├── components/
│   ├── mod.rs           # Component trait
│   ├── repo_list.rs     # Left panel: repo list with status
│   ├── file_list.rs     # Middle panel: changed files + split diff
│   ├── git_graph.rs     # Right panel: commit graph + drill-down
│   ├── context_menu.rs  # Right-click overlay
│   ├── path_input.rs    # Add-repo input overlay
│   └── status_bar.rs    # Bottom bar with keybinding hints
└── git/
    ├── mod.rs
    ├── scanner.rs       # Repo discovery via walkdir
    ├── status.rs        # Branch, files, ahead/behind, fetch
    ├── graph.rs         # Lane-based commit graph builder
    ├── graph_render.rs  # Box-drawing character rendering
    └── commit_files.rs  # Commit file list and per-file diffs

Related Projects

Project Description
Fairtrail Flight price evolution tracker with natural language search
PriceToken Real-time LLM pricing API, npm/PyPI packages, and live dashboard
kin3o AI-powered Lottie animation generator CLI

License

MIT

About

Multi-repo Git workspace dashboard for the terminal

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors