{"id":45690812,"url":"https://github.com/affromero/gitpane","last_synced_at":"2026-06-10T02:01:23.003Z","repository":{"id":340361321,"uuid":"1165764217","full_name":"affromero/gitpane","owner":"affromero","description":"Multi-repo Git workspace dashboard for the terminal","archived":false,"fork":false,"pushed_at":"2026-05-29T13:58:31.000Z","size":11206,"stargazers_count":100,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-05-29T15:04:10.352Z","etag":null,"topics":["cli","dashboard","developer-tools","git","multi-repo","ratatui","rust","terminal","tui","worktree"],"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/affromero.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-02-24T14:18:46.000Z","updated_at":"2026-05-29T13:50:26.000Z","dependencies_parsed_at":"2026-03-01T14:02:10.243Z","dependency_job_id":"0e83e5d6-30fa-475b-bef4-19554bfc398a","html_url":"https://github.com/affromero/gitpane","commit_stats":null,"previous_names":["affromero/gitpane"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/affromero/gitpane","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/affromero%2Fgitpane","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/affromero%2Fgitpane/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/affromero%2Fgitpane/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/affromero%2Fgitpane/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/affromero","download_url":"https://codeload.github.com/affromero/gitpane/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/affromero%2Fgitpane/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33715211,"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-05-30T02:00:06.278Z","response_time":92,"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","dashboard","developer-tools","git","multi-repo","ratatui","rust","terminal","tui","worktree"],"created_at":"2026-02-24T17:03:10.863Z","updated_at":"2026-06-10T02:01:22.994Z","avatar_url":"https://github.com/affromero.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003egitpane\u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\n    \u003cstrong\u003eMulti repo Git workspace dashboard for the terminal\u003c/strong\u003e\n  \u003c/p\u003e\n  \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/affromero/gitpane/ci.yml?branch=main\u0026label=CI\u0026logo=github\u0026style=flat-square\" alt=\"CI\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/actions/workflows/security.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/affromero/gitpane/security.yml?branch=main\u0026label=Security\u0026logo=github\u0026style=flat-square\" alt=\"Security\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/actions/workflows/coverage.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/affromero/gitpane/coverage.yml?branch=main\u0026label=Coverage\u0026logo=github\u0026style=flat-square\" alt=\"Coverage\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/actions/workflows/docs.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/affromero/gitpane/docs.yml?branch=main\u0026label=Docs\u0026logo=github\u0026style=flat-square\" alt=\"Docs\"\u003e\u003c/a\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://crates.io/crates/gitpane\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/gitpane.svg?logo=rust\u0026style=flat-square\u0026color=brightgreen\" alt=\"crates.io\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://docs.rs/gitpane\"\u003e\u003cimg src=\"https://img.shields.io/docsrs/gitpane?logo=rust\u0026style=flat-square\" alt=\"docs.rs\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://deps.rs/repo/github/affromero/gitpane\"\u003e\u003cimg src=\"https://deps.rs/repo/github/affromero/gitpane/status.svg?style=flat-square\" alt=\"Dependency status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/blob/main/Cargo.toml\"\u003e\u003cimg src=\"https://img.shields.io/badge/MSRV-1.88.0-brightgreen?style=flat-square\" alt=\"MSRV 1.88.0\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/gitpane\"\u003e\u003cimg src=\"https://img.shields.io/crates/d/gitpane?label=downloads\u0026style=flat-square\u0026color=brightgreen\" alt=\"Downloads\"\u003e\u003c/a\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/tag/affromero/gitpane?label=release\u0026style=flat-square\u0026color=brightgreen\" alt=\"GitHub Release\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/downloads/affromero/gitpane/total?label=release%20downloads\u0026style=flat-square\u0026color=brightgreen\" alt=\"Release downloads\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/affromero/gitpane/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-brightgreen?style=flat-square\" alt=\"Platform\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/languages/top/affromero/gitpane?style=flat-square\u0026color=brightgreen\" alt=\"Language\"\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\nMonitor **all your repos at a glance**. See branch, dirty state, ahead/behind, active worktrees, changed files, and commit history without leaving the terminal.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/demo.gif\" alt=\"gitpane demo\" width=\"800\"\u003e\n\u003c/p\u003e\n\n## Install\n\n```bash\ncargo install gitpane\n```\n\nThat's it. No cloning, no building from source. Runs on **Linux, macOS, and Windows**.\n\n\u003e **Don't have Rust?** Download a prebuilt binary from [GitHub Releases](https://github.com/affromero/gitpane/releases/latest). It is a single static binary with zero dependencies.\n\u003e\n\u003e ```bash\n\u003e # macOS (Apple Silicon)\n\u003e curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-aarch64-apple-darwin.tar.gz\n\u003e tar xzf gitpane-aarch64-apple-darwin.tar.gz \u0026\u0026 sudo mv gitpane /usr/local/bin/\n\u003e\n\u003e # macOS (Intel)\n\u003e curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-x86_64-apple-darwin.tar.gz\n\u003e tar xzf gitpane-x86_64-apple-darwin.tar.gz \u0026\u0026 sudo mv gitpane /usr/local/bin/\n\u003e\n\u003e # Linux (x86_64, statically linked)\n\u003e curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-x86_64-unknown-linux-musl.tar.gz\n\u003e tar xzf gitpane-x86_64-unknown-linux-musl.tar.gz \u0026\u0026 sudo mv gitpane /usr/local/bin/\n\u003e\n\u003e # Linux (ARM64)\n\u003e curl -LO https://github.com/affromero/gitpane/releases/latest/download/gitpane-aarch64-unknown-linux-gnu.tar.gz\n\u003e tar xzf gitpane-aarch64-unknown-linux-gnu.tar.gz \u0026\u0026 sudo mv gitpane /usr/local/bin/\n\u003e ```\n\n\u003e **On NetBSD?** gitpane is available as a community-maintained [pkgsrc](https://www.pkgsrc.org/) package in [`devel/gitpane`](https://pkgsrc.se/devel/gitpane), thanks to [@0323pin](https://github.com/0323pin):\n\u003e\n\u003e ```sh\n\u003e pkgin install gitpane                            # prebuilt binary package\n\u003e cd /usr/pkgsrc/devel/gitpane \u0026\u0026 make install     # build from pkgsrc source\n\u003e ```\n\nThen run:\n\n```bash\ngitpane                     # Scans ~/Code by default\ngitpane --root ~/projects   # Scan a specific directory\ngitpane diagnostic          # Print config, watcher, and workspace diagnostics\n```\n\n## Update\n\nIf you installed with cargo, gitpane can update itself:\n\n```bash\ngitpane update              # checks for a newer release, then runs cargo install\ncargo install gitpane       # the equivalent manual command, overwrites the old binary\n```\n\nIf you installed from a [GitHub Release](https://github.com/affromero/gitpane/releases/latest), download the latest binary for your platform using the same commands from the install section above.\n\nOn NetBSD, update through pkgsrc instead of `gitpane update`, which shells out to `cargo install` and would replace the package-managed binary with a cargo-built one.\n\n## Why gitpane?\n\nIf you work across multiple repositories, such as microservices, monorepos with submodules, or a mix of projects, you know the pain of checking status one directory at a time. Existing TUI tools focus on **one repo at a time**:\n\n| Tool | Multi repo | Auto refresh | Worktrees | Mouse | Commit graph | Split diffs | Push/Pull |\n|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n| **gitpane** | **Yes** | **Yes** | **Yes** | **Yes** | **Yes** | **Yes** | **Yes** |\n| [lazygit](https://github.com/jesseduffield/lazygit) | No | No | No | Yes | Yes | Yes | Yes |\n| [gitui](https://github.com/extrawurst/gitui) | No | No | No | Yes | Yes | Yes | Yes |\n| [tig](https://github.com/jonas/tig) | No | No | No | No | Yes | No | No |\n| [git-delta](https://github.com/dandavison/delta) | No | No | No | No | No | Yes (pager) | No |\n| [grv](https://github.com/rgburke/grv) | No | No | No | Yes | Yes | No | No |\n| [git-summary](https://github.com/MircoT/git-summary) | Yes (list only) | No | No | No | No | No | No |\n| [mgitstatus](https://github.com/fboender/multi-git-status) | Yes (list only) | No | No | No | No | No | No |\n| [gita](https://github.com/nosarthur/gita) | Yes (CLI only) | No | No | No | No | No | Yes |\n\n**lazygit** and **gitui** are excellent for deep single repo work like staging hunks, interactive rebase, and conflict resolution. gitpane is the **workspace level dashboard**. It shows every repo at once, lets you drill into anything, and keeps you in the terminal. They complement each other.\n\n## Screenshots\n\n### Three panel overview\nRepos on the left show branch, dirty state (`*`), ahead/behind arrows (`↑↓`), worktree count (`⎇`), dirty submodules (`◈`), unpushed submodule pointer (`⇡`), stash count (`$`), and file count. Changes in the middle. Commit graph on the right.\n\n\u003cimg src=\"assets/screenshot-main.png\" alt=\"Three panel overview\" width=\"800\"\u003e\n\n### Split diff view\nClick a changed file (or press Enter) to see its diff side by side. File list stays navigable on the left.\n\n\u003cimg src=\"assets/screenshot-diff.png\" alt=\"Split diff view\" width=\"800\"\u003e\n\n### Commit detail drill down\nClick a commit in the graph to see its files. Click a file to see the commit diff. Layered Esc dismissal: diff → files → graph.\n\n\u003cimg src=\"assets/screenshot-commit.png\" alt=\"Commit detail drill down\" width=\"800\"\u003e\n\n## Features\n\n- **Multi repo overview**: Scans `~/Code` (configurable) for git repos. It shows branch, dirty indicator (`*`), ahead/behind arrows (`↑↓`), worktree count (`⎇`), dirty submodule (`◈`), unpushed submodule pointer (`⇡`), stash count (`$`), and change count.\n- **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 which repos have active parallel work.\n- **Filesystem awareness**: Watches repo roots and Git metadata for commits, checkouts, and new repos. Local polling catches nested worktree file changes without overwhelming Linux inotify.\n- **Commit graph**: Lane based graph with colored box drawing characters, up to 200 commits.\n- **Split diff views**: Click a file to see its diff side by side. Click a commit to see its files and per file diffs.\n- **Full mouse support**: Click to select, right click for context menu, scroll wheel everywhere.\n- **Push / Pull / Rebase**: Right click context menu with git operations that account for ahead and behind state. Explicit `origin \u003cbranch\u003e` is used for reliability.\n- **Add and remove repos**: Press `a` to add any repo with tab completing path input. Press `d` to remove. Press `R` to rescan.\n- **Sort repos**: Cycle between alphabetical and dirty first with `s`.\n- **Copy to clipboard**: Press `y` to copy selected item from any panel (OSC 52).\n- **Configurable**: TOML config for root dirs, scan depth, pinned repos, exclusions, frame rate.\n- **Responsive layout**: Three horizontal panels on wide terminals, vertical stack on narrow ones.\n- **Cross platform**: Linux, macOS, Windows.\n\n## Keybindings\n\n### Global\n\n| Key | Action |\n|-----|--------|\n| `?` | Toggle keybindings help overlay |\n| `Tab` / `Shift+Tab` | Cycle focus between panels |\n| `r` | Refresh all repo statuses |\n| `R` | Rescan directories for repos (clears exclusions) |\n| `g` | Reload git graph for selected repo |\n| `a` | Add a repo (opens path input with tab completion) |\n| `d` | Remove selected repo (with confirmation) |\n| `s` | Cycle sort order (Alphabetical / Dirty first) |\n| `w` | Toggle worktree subtree for the selected repo |\n| `S` | Toggle stash subtree for the selected repo |\n| `t` | Open the theme picker (live preview, Enter to persist) |\n| `y` | Copy selected item to clipboard |\n| `q` | Quit (or close diff if one is open) |\n| `Esc` | Navigate back through panels, then quit |\n\n### Repos panel\n\n| Key | Action |\n|-----|--------|\n| `j` / `↓` | Next repo |\n| `k` / `↑` | Previous repo |\n\n### Changes panel\n\n| Key | Action |\n|-----|--------|\n| `j` / `↓` | Next file |\n| `k` / `↑` | Previous file |\n| `Enter` | Open split diff view |\n| `Esc` / `h` / `←` | Close diff view |\n\n### Graph panel\n\n| Key | Action |\n|-----|--------|\n| `j` / `↓` | Next commit / file |\n| `k` / `↑` | Previous commit / file |\n| `h` / `l` | Scroll graph left / right |\n| `Enter` | Open commit files / file diff |\n| `Esc` | Close diff → close files → back |\n| `/` | Search commits (message, author, short ID) |\n| `n` / `N` | Next / previous search match |\n| `f` | Toggle first parent mode |\n| `c` | Collapse / expand branch |\n| `H` | Expand all collapsed branches |\n\n### Mouse\n\n| Action | Effect |\n|--------|--------|\n| Left click | Select item, switch panel focus |\n| Click selected row | Open diff / commit detail |\n| Right click (repo list) | Context menu (push, pull, copy path) |\n| Scroll wheel | Navigate lists or scroll diffs |\n\n### Path input (`a`)\n\n| Key | Action |\n|-----|--------|\n| `Tab` | Autocomplete directory path (cycles matches) |\n| `Enter` | Add the repo |\n| `Esc` | Cancel |\n| `Ctrl+A` / `Home` | Cursor to start |\n| `Ctrl+E` / `End` | Cursor to end |\n| `Ctrl+U` | Clear line before cursor |\n\n## Configuration\n\ngitpane resolves its config file in this order (first existing file wins):\n\n1. `$GITPANE_CONFIG` (if set, treated as the full path; this overrides everything below and is also the save target)\n2. `$XDG_CONFIG_HOME/gitpane/config.toml` (if `$XDG_CONFIG_HOME` is set and absolute)\n3. `~/.config/gitpane/config.toml` (the XDG default, on every platform)\n4. The platform native location:\n\n| Platform | Path |\n|----------|------|\n| Linux    | `~/.config/gitpane/config.toml` (same as 3) |\n| macOS    | `~/Library/Application Support/gitpane/config.toml` |\n| Windows  | `%APPDATA%\\gitpane\\config\\config.toml` |\n\nIf 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.\n\ngitpane logs the resolved path at startup (`tracing` info level on stderr).\n\n```toml\n# Directories to scan for git repositories\nroot_dirs = [\"~/Code\", \"~/work\"]\n\n# Maximum directory depth for repo discovery\nscan_depth = 2\n\n# Always show these repos at the top\npinned_repos = [\"~/Code/important-project\"]\n\n# Skip repos matching these directory names\nexcluded_repos = [\"node_modules\", \".cargo\", \"target\"]\n\n[watch]\ndebounce_ms = 500             # Filesystem change debounce (ms)\nrefresh_cooldown_ms = 5000    # Min ms between watcher triggered status refreshes per repo\nwatch_worktree_dirs = false   # Opt in to nested worktree watches; polling still catches changes\npoll_local_secs = 5           # Local status poll interval (catches missed watcher events)\npoll_fetch_secs = 30          # Remote fetch poll interval (updates ahead/behind from origin)\ndiscovery_cooldown_secs = 5   # Min seconds between automatic rescans on root dir changes (new clones)\n\n[ui]\nframe_rate = 10              # Terminal refresh rate (fps)\ncheck_for_updates = true     # Check for new versions on startup\nupdate_position = \"top-right\" # Update notification position (\"top-right\" or \"top-left\")\n\n[graph]\nbranches = \"all\"         # Branch filter: \"all\", \"local\", \"remote\", or \"none\"\nlabel_max_len = 24       # Max length for branch/tag labels\nshow_stats = true        # Show +N/-M diff stats per commit\n```\n\nSee [`examples/config.toml`](examples/config.toml) for a fully annotated example.\n\n### Theming\n\ngitpane ships two built in themes:\n\n- `default`, the original palette (used when `theme` is unset).\n- `muted`, softer 256 color indices for dark terminals where the default `Light*` colors feel too bright.\n\n```toml\n# In config.toml\ntheme = \"muted\"\n```\n\nTo define a custom theme, drop a TOML file at `\u003cconfig_dir\u003e/gitpane/themes/\u003cname\u003e.toml` and set `theme = \"\u003cname\u003e\"`. Any field you don't list falls back to the corresponding `default` slot, so a custom theme can be as small as one override:\n\n```toml\n# ~/.config/gitpane/themes/mine.toml\n[repo_list]\nstash = \"Magenta\"\n\n[graph]\ntag_label = \"143\"        # 256 color index\nlane_palette = [\"Red\", \"#5fafd7\", \"Cyan\", \"67\", \"Magenta\", \"Yellow\"]\n```\n\nColor values accept ratatui's standard names (`\"Yellow\"`, `\"LightMagenta\"`, ...), 8 bit indices as bare integers (`\"67\"`), or 24 bit hex (`\"#5fafd7\"`). If `$GITPANE_CONFIG` points to a non XDG location, the `themes/` directory next to that file is searched first.\n\n**Switching themes:**\n\n- **From inside the app**: press `t` to open the picker. Up/Down (or `j`/`k`) cycles through themes with live preview, `Enter` saves the choice to `config.toml`, `Esc` cancels and restores.\n- **From the shell**: `gitpane --theme muted` overrides the active theme for one run without modifying `config.toml`.\n- **List available themes**: `gitpane themes` prints every built in and custom theme, with a marker on the currently resolved one.\n\n## Troubleshooting\n\nFor a copyable snapshot of the active config, scan roots, watcher settings, repo count, and CPU pressure warnings, run:\n\n```sh\ngitpane diagnostic\ngitpane --root ~/projects diagnostic\n```\n\n### gitpane shows no repositories\n\nRun through these in order:\n\n**1. Check that gitpane is reading your config file.**\n\ngitpane prints the resolved config path at startup. Run gitpane with stderr captured:\n\n```sh\nRUST_LOG=gitpane=info gitpane 2\u003e/tmp/gitpane.log\ncat /tmp/gitpane.log\n```\n\nYou 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](#configuration). On macOS, `~/.config/gitpane/config.toml` works as well as the native `~/Library/Application Support/gitpane/config.toml`.\n\nTo force a specific file:\n\n```sh\nGITPANE_CONFIG=/path/to/config.toml gitpane\n```\n\nYou can also bypass the config entirely to confirm repo discovery:\n\n```sh\ngitpane --root \"$HOME/src\"\n```\n\nIf `--root` finds your repos but the config does not, the file path or config contents are the likely issue.\n\n**2. Check that `scan_depth` is large enough.**\n\n`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/\u003cowner\u003e/\u003crepo\u003e/.git`, the `.git` lives at depth 4, so you need `scan_depth = 4` (or higher). Counting from the root:\n\n```\n~/src                                       depth 0\n~/src/github.com                            depth 1\n~/src/github.com/\u003cowner\u003e                    depth 2\n~/src/github.com/\u003cowner\u003e/\u003crepo\u003e             depth 3\n~/src/github.com/\u003cowner\u003e/\u003crepo\u003e/.git        depth 4\n```\n\n**3. Check for symlinks in your tree.**\n\nThe 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.\n\n```sh\nfind    \"$HOME/src\" -maxdepth 4 -name .git -type d -print\necho '... with -L (follows symlinks):'\nfind -L \"$HOME/src\" -maxdepth 4 -name .git -type d -print\n```\n\nIf 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`.\n\n**4. Check whether `.git` is a directory or a file.**\n\nLinked 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:\n\n```sh\ntest -d \"$HOME/src/github.com/affromero/gitpane/.git\" \u0026\u0026 echo dir \\\n || test -f \"$HOME/src/github.com/affromero/gitpane/.git\" \u0026\u0026 echo file\n```\n\nIf it prints `file`, add the repo via `pinned_repos` instead, or open it explicitly with `gitpane --root /path/to/parent`.\n\n**5. Verbose logging.**\n\n```sh\nRUST_LOG=gitpane=debug gitpane 2\u003e/tmp/gitpane.log\n```\n\nThen inspect `/tmp/gitpane.log` for any errors during config load or repo scanning.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────┐\n│                     tokio runtime                        │\n│  ┌──────────┐  ┌──────────┐  ┌──────────────────────┐   │\n│  │ Event    │→ │ Action   │→ │ Components            │   │\n│  │ Loop     │  │ Dispatch │  │  RepoList             │   │\n│  │ (tui.rs) │  │ (app.rs) │  │  FileList (split diff)│   │\n│  └──────────┘  └──────────┘  │  GitGraph (drill down)│   │\n│       ↑                      │  ContextMenu          │   │\n│  ┌──────────┐                │  PathInput             │   │\n│  │ notify   │                │  StatusBar             │   │\n│  │ watcher  │                └──────────────────────┘   │\n│  └──────────┘                                           │\n│       ↑              ┌───────────────────────┐           │\n│  filesystem          │ git2 (spawn_blocking) │           │\n│  changes             │  status · graph       │           │\n│                      │  commit_files · fetch │           │\n│                      └───────────────────────┘           │\n└──────────────────────────────────────────────────────────┘\n```\n\n- **[ratatui](https://github.com/ratatui/ratatui)** + **[crossterm](https://github.com/crossterm-rs/crossterm)**: TUI rendering with full mouse support.\n- **[git2](https://github.com/rust-lang/git2-rs)** (libgit2): Branch, status, ahead/behind, graph, commit diffs.\n- **[notify](https://github.com/notify-rs/notify)**: Filesystem watching with configurable debounce.\n- **[tokio](https://github.com/tokio-rs/tokio)**: Async runtime. Git queries run in `spawn_blocking` to keep the UI responsive.\n\nMessage passing architecture: terminal events → actions → component updates → render. Each component implements a `Component` trait with `draw`, `handle_key_event`, `handle_mouse_event`, and `update`.\n\n## Development\n\n```bash\njust run           # Build and run\njust test          # Run test suite\njust fmt           # Format code\njust lint          # Run clippy with warnings denied\njust audit         # Run cargo-audit security advisory checks\njust coverage      # Generate lcov.info with cargo-llvm-cov\njust docs          # Build docs with warnings denied\njust ci            # fmt + lint + docs + test\n```\n\nCI runs formatting, clippy, MSRV checks, docs, tests, and release builds across\nLinux, macOS, and Windows. Security and coverage run as separate workflows so\ntheir README badges map to real checks.\n\nInstall the local hooks before contributing:\n\n```bash\nbrew install pre-commit        # or: pipx install pre-commit\npre-commit install             # installs pre-commit and pre-push hooks\npre-commit run --all-files     # optional one-time full check\n```\n\nThe pre-commit hook handles file hygiene, TOML/YAML validation, formatting, and\nclippy. The pre-push hook runs tests, audit, docs, and coverage.\n\nOptional tooling for the full local suite:\n\n```bash\ncargo install cargo-audit cargo-llvm-cov\n```\n\n### Project structure\n\n```\nsrc/\n├── main.rs              # Entry point, CLI parsing\n├── app.rs               # Main loop, action dispatch, layout\n├── action.rs            # Action enum (message passing)\n├── event.rs             # Terminal event types\n├── tui.rs               # Terminal setup, event loop\n├── config.rs            # TOML config load/save\n├── watcher.rs           # Filesystem watcher to repo index mapping\n├── components/\n│   ├── mod.rs           # Component trait\n│   ├── repo_list.rs     # Left panel: repo list with status\n│   ├── file_list.rs     # Middle panel: changed files + split diff\n│   ├── git_graph.rs     # Right panel: commit graph and drill down\n│   ├── context_menu.rs  # Right click overlay\n│   ├── path_input.rs    # Add repo input overlay\n│   └── status_bar.rs    # Bottom bar with keybinding hints\n└── git/\n    ├── mod.rs\n    ├── scanner.rs       # Repo discovery via walkdir\n    ├── status.rs        # Branch, files, ahead/behind, fetch\n    ├── graph.rs         # Lane based commit graph builder\n    ├── graph_render.rs  # Box drawing character rendering\n    └── commit_files.rs  # Commit file list and per file diffs\n```\n\n## Related Projects\n\n| Project | Description |\n|---------|-------------|\n| [**Splattie**](https://github.com/affromero/splattie) | 3D Gaussian splat avatar pipeline, hosted editor, and portable `.splattie` bundle format |\n| [**Flight Finder**](https://github.com/affromero/flight-finder) | Flight price evolution tracker with natural language search |\n| [**PriceToken**](https://github.com/affromero/pricetoken) | Live LLM pricing API, npm/PyPI packages, and dashboard |\n| [**kin3o**](https://github.com/affromero/kin3o) | AI powered Lottie animation generator CLI |\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faffromero%2Fgitpane","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faffromero%2Fgitpane","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faffromero%2Fgitpane/lists"}