yawn — Yet Another Worktree Navigator
A fast project switcher and worktree manager for git.
Install
From source
Nix flake
inputs.yawn.url = "github:ComeBertrand/yawn";
# then in your packages:
inputs.yawn.packages.${system}.default
GitHub Releases
Download a binary from the releases page, or use the shell installer:
|
Usage
yawn list [path] [--json] [--raw] [--porcelain] Discover git projects
yawn resolve <pretty-name> [-P <path>] Map a pretty name back to an absolute path
yawn pick [-F <finder>] [path] Interactively pick a project and open it
yawn open <path> [-c <command>] Open a terminal in the given directory
yawn create <name> [--source <base>] [--open] [--init] Create a git worktree
yawn delete <name> [--branch] Remove a worktree
yawn init Initialize the current directory
Listing projects
Recursively discovers git projects under a directory. Takes an optional path, defaults to the current directory.
By default, the output is human-friendly. In a terminal, projects are shown as a colored tree with worktrees grouped under their parent:
my-app
├─ fix-branch
└─ feature-x
dotfiles
notes (personal)
notes (work)
When piped (or with --porcelain), it falls back to flat pretty names for compatibility with tools like fzf:
my-app
fix-branch @my-app
feature-x @my-app
dotfiles
notes (personal)
notes (work)
The --raw flag outputs absolute paths, one per line:
/home/user/projects/my-app
/home/user/worktrees/my-app--fix-branch
/home/user/worktrees/my-app--feature-x
/home/user/projects/dotfiles
The --json flag outputs an array of objects with path, name, is_worktree, and worktree_of fields:
# [
# { "path": "/home/user/projects/myapp", "name": "myapp", "is_worktree": false, "worktree_of": null },
# { "path": "/home/user/projects/myapp--feature", "name": "feature @myapp", "is_worktree": true, "worktree_of": "myapp" }
# ]
If run inside a git repo (without a path), it lists the repo itself and its worktrees:
/home/user/projects/my-app
/home/user/worktrees/my-app--fix-branch
/home/user/worktrees/my-app--feature-x
Interactive project switcher
Use yawn pick with any fuzzy finder:
# fzf
# rofi
# from current directory
This discovers projects, pipes pretty names into the finder, resolves the selection, and opens a terminal — all in one command. Easy to bind in i3/sway/hyprland.
The equivalent manual pipeline still works:
# override the configured open command for a single invocation
Worktrees
Worktrees are created under a configurable root directory (default: ~/worktrees) using the naming convention <project>--<name>. For example, running yawn create feature-x from inside a repo called my-app creates:
~/worktrees/my-app--feature-x
When listing with --pretty, the <project>-- prefix is stripped and the worktree is annotated:
feature-x @my-app
Branch resolution when creating a worktree follows this order:
- If
<name>exists as a local branch, check it out. - If
<name>exists asorigin/<name>, track it. - If
--source <base>is provided, create a new branch from<base>. - Otherwise, create a new branch from the default branch (
origin/HEAD, falling back tomainthenmaster).
# Create a worktree (new branch from default branch)
# Create from a specific base branch
# Create and immediately open a terminal in it
# Create and run init (copy files + setup commands)
# All flags combine
# Delete a worktree (prompts to delete the local branch)
# Delete a worktree and its local branch without prompting
Initializing a project
yawn init sets up the current directory by copying files from the main repo and running setup commands. Configuration lives in .yawn.toml at the repo root:
[]
= [".env", ".env.local", "config/*.toml"]
= ["npm install", "cargo build"]
include— files, directories, and glob patterns to copy from the main repo into worktrees. Directories are copied recursively. Useful for local config files (.env, etc.) that aren't tracked by git.commands— shell commands to run sequentially in the target directory. Stops on first failure.
When run in a worktree, yawn init copies include files from the main repo and then runs commands. When run in the main repo itself (e.g. after a fresh clone), it skips the copy step and only runs commands.
# Initialize the current directory
# Create a worktree and initialize it in one step
Configuration
There are two config files:
~/.config/yawn/config.toml— global user config (discovery, session, worktree settings).yawn.toml— per-project config, lives in the repo root and should be committed to git (init settings)
.yawn.toml (per-project)
| Key | Type | Description |
|---|---|---|
init.include |
list of strings | Files, directories, or glob patterns to copy from the main repo into worktrees. Directories are copied recursively. |
init.commands |
list of strings | Shell commands to run sequentially during init. |
[]
= [".env", ".env.local", "config/*.toml"]
= ["npm install"]
~/.config/yawn/config.toml (global)
All fields are optional.
[discovery]
| Key | Type | Default | Description |
|---|---|---|---|
max_depth |
integer | 5 |
Maximum recursion depth when searching for git projects. |
ignore |
list of strings | [".*", "node_modules"] |
Glob patterns matched against directory names. Matching directories are skipped during discovery. Hidden directories (except .git itself) are ignored by default. |
[session]
| Key | Type | Default | Description |
|---|---|---|---|
opener |
string | unset | Command template to open a terminal session. Placeholders: {dir} (absolute path), {name} (directory basename). When unset, uses $TERMINAL, or falls back to Terminal.app on macOS and xterm on Linux. |
finder |
string | unset | Default finder command for yawn pick (e.g. fzf, rofi -dmenu -p project -i). Can be overridden with -F. |
[worktree]
| Key | Type | Default | Description |
|---|---|---|---|
root |
string | ~/worktrees |
Directory where worktrees are created. Supports ~ expansion. |
auto_init |
boolean | false |
Automatically run init after creating a worktree (equivalent to always passing --init). |
Example
[]
= 3
= [".*", "node_modules", "target", "vendor"]
[]
= "kitty --directory {dir} --title 'dev: {name}'"
= "fzf"
[]
= "~/worktrees"
Shell Completion
Bash
Zsh
Or place it anywhere in your $fpath.
Fish
Man Page
A man page is generated at build time. After building from source:
License
MIT