git-yawn 0.6.0

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

yawn — Yet Another Worktree Navigator

CI crates.io License: MIT

A CLI tool for managing git worktrees and discovering projects.

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] [--pretty] [--json] [--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]   Create a git worktree
yawn delete <name>              Remove a worktree

Listing projects

Recursively discovers git projects under a directory. Takes an optional path, defaults to the current directory.

yawn list ~/projects       # discover projects under ~/projects
yawn list                  # discover projects under cwd
yawn list ~/projects -p    # tree view in terminal, flat when piped
yawn list ~/projects -p --porcelain  # force flat output (stable for scripts)
yawn list ~/projects --json # structured JSON output

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" }
# ]

When stdout is a terminal, --pretty shows a colored tree view 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 the flat format for compatibility with tools like fzf:

my-app
fix-branch @my-app
feature-x @my-app
dotfiles
notes (personal)
notes (work)

If run inside a git repo (without a path), it lists the worktrees of that repo instead:

cd ~/projects/my-app
yawn list
/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).

If a .yawninclude file exists in the main repo root, the files it lists are copied into the new worktree. This is useful for local config files like .env that aren't tracked by git. Glob patterns are supported.

# .yawninclude
.env
.env.local
config/*.toml
data_*.csv
# 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

# Delete a worktree
yawn delete feature-x

Configuration

~/.config/yawn/config.toml — 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
open_command 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.

[worktree]

Key Type Default Description
root string ~/worktrees Directory where worktrees are created. Supports ~ expansion.

Example

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

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

[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