oh-my-worktree
v0.15.0
Published
A TUI tool for managing Git worktrees in bare and regular repositories
Maintainers
Readme
owt (oh-my-worktree)
A fast terminal UI for developers who use Git branches as working contexts, not bookmarks.
Why owt exists
Modern development rarely happens on one branch at a time. You might be reviewing a PR, testing a hotfix, keeping a long-running feature open, and checking main before a release. Plain git switch makes that workflow expensive because every context switch drags along uncommitted files, dependencies, editor state, and mental state.
Git worktrees solve the underlying problem. owt makes them easy enough to use every day.
Open owt, pick a worktree, create another one, delete the stale ones, fetch, pull, push, or merge without remembering the exact Git incantation. It works from a normal repository and from the .bare layout if you prefer keeping all worktrees side by side.
What you get
- A keyboard-first TUI for browsing and managing worktrees
- A plain, tab-separated CLI surface for agents and scripts
- First-class support for existing regular repositories
- Optional
.bareproject layout for teams that like sibling worktrees - Fast worktree creation from local or remote branches
- Dirty-state, ahead/behind, last-commit, and GitHub PR status visibility
- Built-in fetch, pull, push, upstream merge, branch merge, editor open, terminal open, and path copy
- Shell integration so
Entercan move your shell into the selected worktree
Install
Current release with Cargo (v0.15.0):
cargo install --git https://github.com/dding-g/oh-my-worktree --tag v0.15.0 --forcePrebuilt binaries are attached to the latest GitHub Release.
This npm wrapper is also supported when the npm registry package is current:
npm install -g oh-my-worktreeFrom source:
git clone https://github.com/dding-g/oh-my-worktree.git
cd oh-my-worktree
cargo build --releaseStart from the repo you already have
You do not need to convert your repository.
cd ~/src/my-app
owtIn a regular repository, new worktrees are created under:
~/.owt/worktree/<repo-name>/Set worktree_root if you want them somewhere else.
Or start with a .bare workspace
If you like all worktrees living inside one project folder, use owt clone.
owt clone https://github.com/user/repo.git
cd repo/main
owtThat creates a layout like this:
repo/
├── .bare/
├── main/
├── feature-login/
└── hotfix-api/owt init prints a conversion guide if you want to move an existing repository into this layout manually.
Outside a Git repository, owt opens Global Home: c clones a workspace (URL and optional base path), i shows the conversion-guide entry, s runs shell setup after the TUI restores the terminal, ? shows CLI commands, and v shows the version.
Daily workflow
owtThen use the TUI:
| Key | Action |
| --- | --- |
| j / k | Move selection |
| Space | Select/unselect a worktree for batch actions |
| Enter | Enter the selected worktree |
| a | Add a worktree |
| d | Delete the selected worktree, or selected worktrees when any are checked |
| r / x | Refresh the list / preview stale-metadata and completed-PR cleanup |
| R | Query PR status (selected, all, or arbitrary branch) |
| C | Browse selected worktree commit tree |
| N | Clone a .bare workspace |
| I / S / V | Show init guide / run shell setup / show About |
| f | Fetch remotes |
| p / P | Pull selected worktree(s) / push the selected worktree |
| m / M | Merge upstream / merge selected branch |
| o / t | Open in editor / terminal |
| y | Copy path |
| / | Filter by path, name, branch, status, or PR |
| s | Cycle sort mode |
| c | View config |
| v | Toggle verbose Git command details |
| ? | Help |
| q | Quit |
What the list tells you
| Signal | Meaning |
| --- | --- |
| ✓ clean | No local changes |
| ? unknown | Git status could not be verified; destructive cleanup treats it as unsafe |
| + staged | Staged changes |
| ~ unstaged | Unstaged changes |
| ! conflict | Merge conflict |
| * mixed | Staged and unstaged changes |
| ↑N / ↓N | Ahead / behind upstream |
| PR | GitHub PR state: open, closed, merged, draft, or - |
The PR column is GitHub-only and best-effort. No PR, non-GitHub remotes, missing auth, network failures, and unknown states all show - so the worktree list stays fast and reliable.
Plain CLI for agents
Use noun command groups when you need stable, non-TUI output:
owt worktree list
owt worktree create feature/login --base main
owt worktree delete feature/login --branch --force
owt worktree prune
owt worktree prune --dry-run
owt pr status --branch feature/login
owt commit tree -n 12
owt search loginAgent bootstrap assets are versioned in the repository under .agents/: use .agents/prompts/install-owt.md, .agents/skills/owt-install/SKILL.md, and .agents/skills/owt-worktree/SKILL.md so worktree handling goes through owt.
worktree list and search print tab-separated records:
kind<TAB>path<TAB>branch<TAB>status<TAB>last_commit<TAB>ahead<TAB>behind<TAB>prworktree prune logs every worktree decision as tab-separated output. Normal mode removes non-current clean worktrees whose GitHub PR status is merged or closed, except the HEAD branch worktree itself; removal runs in parallel and never deletes branches. --dry-run previews stale metadata pruning, reviews removable candidates one at a time, and records selected candidates without deleting them.
Shell integration
Install the shell helper:
owt setupReload your shell. After that, pressing Enter in the TUI exits owt and moves the current shell into the selected worktree. When you create a worktree from the TUI, owt exits the TUI first, creates the worktree, runs copy/post-add/tmux steps in the normal terminal, then hands off to the new worktree path. Without shell integration, owt still prints the target path for wrapper scripts and manual use.
Configuration
Config file:
~/.config/owt/config.tomlExample:
editor = "code"
terminal = "Ghostty"
worktree_root = "~/.owt/worktree"
copy_files = [".env", ".envrc"]
post_add_script = ".owt/post-add.sh"
tmux_worktree_mode = false
run_post_add_script_in_tmux = falseUseful options:
| Option | Purpose |
| --- | --- |
| editor | Command used by o |
| terminal | Terminal app used by t |
| worktree_root | Root for new worktrees in regular repositories |
| copy_files | Relative file paths copied into new worktrees. Absolute paths, .., and destination symlinks are rejected; copy problems are shown as warnings after creation. |
| post_add_script | Script path for post-add setup. Relative paths use the current effective project root. |
| tmux_worktree_mode | Open a tmux pane in each new worktree and focus an existing matching pane on Enter. |
| run_post_add_script_in_tmux | Run the post-add script in detached tmux after creating a worktree. Only global config can enable this. |
Project config in .owt/config.toml can override safe values, including post_add_script, but it cannot enable automatic post-add execution. A regular linked worktree only reads its own project config; it does not inherit an ancestor .owt/config.toml.
Commands
| Command | Purpose |
| --- | --- |
| owt [PATH] | Open the TUI for a repository or worktree |
| owt clone <URL> [PATH] | Clone into the .bare layout and create the first worktree |
| owt init | Print a manual conversion guide for .bare layout |
| owt setup | Install shell integration |
| owt worktree list | List worktrees as tab-separated records |
| owt worktree create <BRANCH> | Create a worktree without opening the TUI. Use --tmux=on to open it in tmux for that run. |
| owt worktree delete <TARGET> | Delete a worktree by branch, name, or path |
| owt worktree prune | Prune stale metadata, log every worktree decision, and remove non-current clean worktrees whose PR status is merged or closed |
| owt pr status | Check GitHub PR status through gh |
| owt commit tree | Print recent commits as a git graph |
| owt search <QUERY> | Search worktrees |
| owt --version | Print version |
Requirements
- Git 2.5+
- A regular Git repository or a
.bareworktree layout - Optional: GitHub CLI
ghfor PR status - Optional: tmux for worktree pane mode and post-add setup scripts
License
MIT
