All your repos, one command.
soko (倉庫 — "storehouse") is a fast, lightweight CLI for managing multiple git repositories. Register your repos once, then see the status of all of them from anywhere with a single command. No more cd-ing between directories and running git status one at a time.
- Git — soko shells out to
gitfor all repository operations - Go 1.22+ — only needed if installing from source or via
go install
# Quick install (macOS / Linux)
curl -fsSL https://raw.githubusercontent.com/CelikE/soko/master/install.sh | sh
# Homebrew (macOS / Linux)
brew install CelikE/tap/soko
# Windows (winget)
winget install CelikE.soko
# Windows (Scoop)
scoop bucket add soko https://github.com/CelikE/homebrew-tap
scoop install soko
# From source (requires Go 1.22+)
go install github.com/CelikE/soko/cmd/soko@latestOr download binaries directly from GitHub Releases.
# Enable shell integration (add to .bashrc or .zshrc)
eval "$(soko shell-init)"
# Register all repos at once
soko scan ~/projects --tag work
# Or register individually
cd ~/projects/auth-service && soko init --tag backend
# See everything at a glance
soko status REPO BRANCH STATUS ↑↓ LAST COMMIT
──────────────────────────────────────────────────────────────────────────────
auth-service feat/sso ✎ 3M ↑2 2h ago feat: add OAuth
backend-api main ✓ clean ↓3 1d ago fix: rate limiter
frontend dev ✎ 1M 2U ↑1 4h ago refactor: nav bar
3 repos · 2 dirty · 1 behind · 6 changes
| Command | Description |
|---|---|
soko init |
Register the current git repo (detects worktrees) |
soko scan |
Discover and register all git repos in a directory |
soko status [repos...] |
Show status of all (or specific) repos |
soko diff [repos...] |
Show uncommitted file changes across repos |
soko stash [repos...] |
Stash/pop uncommitted changes across repos |
soko clean [repos...] |
Delete merged branches across repos |
soko list |
List all registered repos |
soko remove |
Remove a repo from the registry |
soko fetch |
Fetch all registered repos in parallel |
soko cd |
Navigate to a repo by name |
soko go |
Interactive repo picker |
soko exec |
Run a command in all registered repos |
soko open |
Open a repo in the browser |
soko report [repos...] |
Summarize commit activity across repos |
soko tag |
Manage repo tags |
soko alias |
Manage command aliases |
soko doc |
Check the health of your soko setup |
soko config |
View config path or open in editor |
soko shell-init |
Print shell integration hook |
soko version |
Print the soko version |
| Flag | Scope | Description |
|---|---|---|
--json |
Global | Output in JSON format |
--fetch |
status |
Fetch from remotes before showing status |
--dirty |
status |
Show only repos with uncommitted changes |
--clean |
status |
Show only clean repos in sync with remote |
--ahead |
status |
Show only repos ahead of remote |
--behind |
status |
Show only repos behind remote |
--tag |
init, scan, status, list, fetch, exec, go, report, clean |
Filter by tag (repeatable, combines with OR) |
--worktree |
init |
Register as a linked worktree instead of resolving to main repo |
--worktrees |
scan |
Also discover and register linked git worktrees |
--no-worktrees |
fetch, exec |
Skip worktree entries, only operate on parent repos |
--dry-run |
scan, clean |
Preview what would happen without making changes |
--depth |
scan |
Maximum directory depth to scan (default: 5) |
--group |
status, list |
Group repos by tag in a tree view |
--all |
status |
Show all repos without truncation |
--prune |
fetch, clean |
Prune stale remote tracking refs |
--force |
remove, clean |
Skip confirmation prompt |
--seq |
exec |
Run sequentially instead of in parallel |
--prs |
open |
Open pull/merge requests page |
--issues |
open |
Open issues page |
--actions |
open |
Open CI/CD page |
--branches |
open |
Open branches page |
--settings |
open |
Open settings page |
--days |
report |
Number of days to look back (default: 7) |
--author |
report |
Filter commits by author name (substring match) |
--all-authors |
report |
Show commits from all authors |
--max |
report |
Max commits per repo (default: 5, 0 for all) |
--fix |
doc |
Auto-fix issues (remove stale paths) |
--fish |
shell-init |
Output fish shell syntax |
--pwsh |
shell-init |
Output PowerShell syntax |
soko status # all repos
soko status auth # single repo (prefix match)
soko status auth frontend # multiple specific repos
soko status --fetch # fetch first, then show status
soko status --dirty # only repos with uncommitted changes
soko status --tag backend --behind # only backend repos behind remote
soko status --json # machine-readable outputsoko init --tag backend --tag go # tag during registration
soko tag backend go # tag current repo (shorthand)
soko tag add critical # add tag to current repo
soko tag add -r my-repo critical # add tag to a specific repo
soko tag remove backend # remove tag from current repo
soko tag list # show all tags with repo counts
soko status --tag backend # filter any command by tag
soko fetch --tag frontend # fetch only frontend repos
soko exec --tag go -- go mod tidy # run in tagged repos onlysoko alias set morning "sync --tag work" # create an alias
soko alias set deploy "exec --tag prod -- make deploy"
soko alias list # show all aliases
soko alias remove morning # remove an alias
soko morning # runs soko sync --tag work
soko deploy # runs soko exec --tag prod -- make deployBuilt-in commands always take priority over aliases.
soko report # your commits, last 7 days
soko report --days 1 # standup: yesterday
soko report --days 30 # monthly summary
soko report --tag backend # only backend repos
soko report auth # specific repo
soko report --all-authors # everyone's commits
soko report --author "John" # specific authorsoko exec -- git pull --rebase # pull all repos
soko exec -- git stash # stash everything
soko exec -- make test # run tests everywhere
soko exec --seq -- git log -1 # sequential, one at a time
soko exec --tag backend -- go vet # only in backend reposRequires shell integration (one-time setup):
# Bash / Zsh
eval "$(soko shell-init)"
# Fish
soko shell-init --fish | source
# PowerShell
soko shell-init --pwsh | Invoke-ExpressionThen navigate directly:
soko cd auth # jump by name (prefix match)
soko go # interactive picker
soko go --tag backend # picker filtered by tagsoko open # current repo homepage
soko open auth-service # by name
soko open --prs # pull/merge requests
soko open --issues # issues
soko open --actions # CI/CD
soko open --tag backend # open all backend repos
soko open --tag backend --prs # PRs for all backend reposSupports GitHub, GitLab, and Bitbucket — auto-detects the platform from the remote URL.
soko list # show all registered repos
soko list --group # tree view grouped by tag
soko list --tag infra # filter by tag
soko remove old-project # unregister by name
soko remove --path /old/path # unregister by path
soko remove --all --force # clear everythingsoko clean --dry-run # preview merged branches
soko clean # delete with confirmation
soko clean --force # skip confirmation
soko clean --prune # also prune stale remote refs
soko clean --tag backend # only backend repos
soko clean auth # specific reposoko doc # check paths, git, remotes, shell-init
soko doc --fix # auto-remove stale entries
soko config path # print config file location
soko config edit # open config in $EDITOR
soko config set git_path /usr/local/bin/git # use a custom git binary
soko config get git_path # check current git binarysoko supports git worktrees natively. If you use worktrees as your primary branching workflow, use --worktrees to discover and register them:
# Scan and discover repos + worktrees
soko scan ~/projects --worktrees
# Register a single worktree
cd ~/projects/api/feat-oauth
soko init --worktree
# See everything — worktrees show alongside their parent
soko list
# api ~/projects/api/main
# api/feat-oauth ~/projects/api/feat-oauth → api
# api/hotfix-123 ~/projects/api/hotfix-123 → api
# Jump to a worktree
soko cd api/feat # prefix match on parent/branch
soko go # pick interactively
# Status works per-worktree
soko status --dirty
# Remove a parent — linked worktrees are removed too
soko remove api
# Skip worktrees for bulk operations
soko fetch --no-worktrees
soko exec --no-worktrees -- git pullWithout --worktrees, soko detects when you're in a worktree and registers the main repo instead — no duplicates.
Use soko as the directory source for your tmux-sessionizer:
# Pick a repo/worktree and create a tmux session for it
TARGET=$(soko list --json | jq -r '.[].path' | fzf)
SESSION=$(basename "$TARGET")
tmux new-session -d -s "$SESSION" -c "$TARGET" 2>/dev/null
tmux switch-client -t "$SESSION"Or use soko's built-in interactive picker, which supports fuzzy search:
soko go # pick a repo or worktree, cd into itsoko stores registered repos in a single YAML file:
~/.config/soko/config.yaml
Respects $XDG_CONFIG_HOME if set. The format is minimal:
aliases:
morning: sync --tag work
deploy: exec --tag prod -- make deploy
repos:
- name: auth-service
path: /home/dev/work/auth-service
tags:
- backend
- go
- name: auth-service/feat-oauth
path: /home/dev/worktrees/feat-oauth
worktree_of: auth-service
tags:
- backend
- name: frontend
path: /home/dev/work/frontend
tags:
- frontendTags and worktree_of are optional — repos without them work the same as before.
git clone https://github.com/CelikE/soko.git
cd soko
make build
make testsoko is a single binary with minimal dependencies. No CGo, no git libraries — it shells out to the git CLI directly.
| Dependency | Purpose |
|---|---|
| spf13/cobra | CLI framework |
| gopkg.in/yaml.v3 | Config file parsing |
| fatih/color | Terminal colors (respects NO_COLOR) |
| golang.org/x/sync | Parallel execution |
| golang.org/x/term | Interactive picker (raw terminal) |