git-yawn 0.10.0

Yet Another Worktree Navigator
git-yawn-0.10.0 is not a library.

yawn — Yet Another Worktree Navigator

CI crates.io License: MIT

A fast project switcher and worktree manager for git.

Install

From source

cargo install git-yawn

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:

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ComeBertrand/yawn/releases/latest/download/yawn-installer.sh | sh

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.

yawn list ~/projects           # pretty output (tree in terminal, flat when piped)
yawn list                      # discover projects under cwd
yawn list --porcelain          # force flat pretty names (stable for scripts)
yawn list --raw                # absolute paths, one per line
yawn list --json               # structured JSON output

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:

yawn list ~/projects --json
# [
#   { "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:

cd ~/projects/my-app
yawn list
/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
yawn pick -F fzf ~/projects

# rofi
yawn pick -F "rofi -dmenu -p project -i" ~

# from current directory
yawn pick -F fzf

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:

yawn open "$(yawn resolve -P ~ "$(yawn list ~ --pretty | fzf)")"

# override the configured open command for a single invocation
yawn open /path/to/project -c "code {dir}"

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:

  1. If <name> exists as a local branch, check it out.
  2. If <name> exists as origin/<name>, track it.
  3. If --source <base> is provided, create a new branch from <base>.
  4. Otherwise, create a new branch from the default branch (origin/HEAD, falling back to main then master).
# Create a worktree (new branch from default branch)
yawn create feature-x

# Create from a specific base branch
yawn create feature-x --source develop

# Create and immediately open a terminal in it
yawn create feature-x --open

# Create and run init (copy files + setup commands)
yawn create feature-x --init

# All flags combine
yawn create feature-x --source develop --init --open

# Delete a worktree (prompts to delete the local branch)
yawn delete feature-x

# Delete a worktree and its local branch without prompting
yawn delete feature-x --branch

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:

[init]
include = [".env", ".env.local", "config/*.toml"]
commands = ["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
yawn init

# Create a worktree and initialize it in one step
yawn create feature-x --init

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.
[init]
include = [".env", ".env.local", "config/*.toml"]
commands = ["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

[discovery]
max_depth = 3
ignore = [".*", "node_modules", "target", "vendor"]

[session]
opener = "kitty --directory {dir} --title 'dev: {name}'"
finder = "fzf"

[worktree]
root = "~/worktrees"

Shell Completion

Bash

cp completions/yawn.bash ~/.local/share/bash-completion/completions/yawn

Zsh

cp completions/yawn.zsh ~/.local/share/zsh/site-functions/_yawn

Or place it anywhere in your $fpath.

Fish

cp completions/yawn.fish ~/.config/fish/completions/yawn.fish

Man Page

A man page is generated at build time. After building from source:

man target/*/build/git-yawn-*/out/man/yawn.1

License

MIT