worktrunk 0.1.9

A Git worktree manager for trunk-based development
Documentation

Worktrunk

Crates.io License: MIT GitHub CI Status

Worktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows. Worktrees give each agent an isolated branch and directory; Worktrunk adds branch-based navigation, lifecycle hooks, and unified status. The goal is to make spinning up a new AI "developer" for a task feel as routine as git switch.

December 2025 Project Status

I've been using Worktrunk as my daily driver, and am releasing it as Open Source this week. It's built with love (no slop!). If some social proof is helpful: I also created PRQL (10k stars) and am a maintainer of Xarray (4k stars), Insta, & Numbagg.

Currently, Worktrunk:

...there's no need to use the more advanced features to get the simpler benefits, and everything is fully compatible with standard git worktrees.

To contribute:

  • Star the repo
  • Tell a friend
  • Install it, use it, post feedback
  • Post about it on socials

Demo

Worktrunk Demo

Quick Start

1. Install

$ cargo install worktrunk
$ wt config shell install  # allows commands to change directories

2. Create a worktree

$ wt switch --create fix-auth
✅ Created new worktree for fix-auth from main at ../repo.fix-auth

This creates ../repo.fix-auth on branch fix-auth.

3. Switch between worktrees

$ wt switch feature-api
✅ Switched to worktree for feature-api at ../repo.feature-api

4. List worktrees

After doing some work:

$ wt list
  Branch       Status         HEAD±    main↕  Path                Remote⇅  Commit    Age   Message
@ feature-api  +   ↑⇡      +36  -11   ↑4      ./repo.feature-api   ⇡3      b1554967  30m   Add API tests
^ main             ^⇣                         ./repo                   ⇣1  b834638e  1d    Initial commit
+ fix-auth        _                           ./repo.fix-auth              b834638e  1d    Initial commit

⚪ Showing 3 worktrees, 1 with changes, 1 ahead

--full adds CI status and conflicts. --branches includes all branches.

5. Clean up

Say we merged via CI, our changes are on main, and we're finished with the worktree:

$ wt remove
🔄 Removing feature-api worktree & branch in background

Why worktrees?

Parallel agents need isolated working directories that share one Git history.

We can get that a few ways:

  • multiple clones — slow to set up, waste disk, drift out of sync
  • one working tree with many branches — constant stashing, rebasing, and conflict risk
  • git worktrees — multiple directories backed by a single .git directory

So we use git worktrees: multiple working directories backed by a single repository.

Why Worktrunk?

Git's built-in worktree commands offers primitives, but requires the user to remember worktree's location, and compose commands together. Worktrunk bundles creation, navigation, status, and cleanup into simple commands. A few examples:

...and check out examples below for more advanced workflows.

Advanced

Most Worktrunk users just use the commands above. But I've found these features really helpful too:

Shell integration

Shell integration lets wt switch, wt merge, and wt remove change directories:

wt config shell install  # Bash, Zsh, Fish

Manual setup: wt config shell --help.

Local merging with wt merge

wt merge handles the full merge workflow: stage, commit, squash, rebase, merge, cleanup. Includes LLM commit messages, project hooks, and config/flags for skipping steps.

LLM commit messages

Worktrunk can invoke external commands to generate commit messages. llm is recommended.

~/.config/worktrunk/config.toml:

[commit-generation]
command = "llm"
args = ["-m", "claude-haiku-4-5-20251001"]

wt merge generates commit messages automatically or wt step commit runs just the commit step.

For custom prompt templates: wt config --help.

$ wt merge
🔄 Squashing 3 commits into a single commit (3 files, +33)...
🔄 Generating squash commit message...
   feat(auth): Implement JWT authentication system

   Add comprehensive JWT token handling including validation, refresh logic,
   and authentication tests. This establishes the foundation for secure
   API authentication.

   - Implement token refresh mechanism with expiry handling
   - Add JWT encoding/decoding with signature verification
   - Create test suite covering all authentication flows
✅ Squashed @ 95c3316
🔄 Running pre-merge test:
   cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.12s
     Running unittests src/lib.rs (target/debug/deps/worktrunk-abc123)

running 18 tests
test auth::tests::test_jwt_decode ... ok
test auth::tests::test_jwt_encode ... ok
test auth::tests::test_token_refresh ... ok
test auth::tests::test_token_validation ... ok

test result: ok. 18 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s
🔄 Running pre-merge lint:
   cargo clippy
    Checking worktrunk v0.1.0
    Finished dev [unoptimized + debuginfo] target(s) in 1.23s
🔄 Merging 1 commit to main @ 95c3316 (no rebase needed)
   * 95c3316 feat(auth): Implement JWT authentication system
    auth.rs      |  8 ++++++++
    auth_test.rs | 17 +++++++++++++++++
    jwt.rs       |  8 ++++++++
    3 files changed, 33 insertions(+)
✅ Merged to main (1 commit, 3 files, +33)
🔄 Removing feature-auth worktree & branch in background (ancestor of main)
🔄 Running post-merge install:
   cargo install --path .
  Installing worktrunk v0.1.0
   Compiling worktrunk v0.1.0
    Finished release [optimized] target(s) in 2.34s
  Installing ~/.cargo/bin/wt
   Installed package `worktrunk v0.1.0` (executable `wt`)

Project hooks

Configure hooks in .config/wt.toml:

Hook When Example
post-create After worktree created cp -r .cache, ln -s
post-start After worktree created (background) npm install, cargo build
pre-commit Before squash commit created pre-commit run
pre-merge After squash, before push cargo test, pytest
post-merge After successful merge cargo install --path .
# Install dependencies, build setup
[post-create]
"install" = "uv sync"

# Dev servers, file watchers (runs in background)
[post-start]
"dev" = "uv run dev"

# Tests and lints before merging (blocks on failure)
[pre-merge]
"test" = "uv run pytest"
"lint" = "uv run ruff check"
$ wt switch --create feature-x
🔄 Running post-create install:
   uv sync

  Resolved 24 packages in 145ms
  Installed 24 packages in 1.2s
✅ Created new worktree for feature-x from main at ../repo.feature-x
🔄 Running post-start dev:
   uv run dev
$ wt merge
🔄 Squashing 3 commits into a single commit (2 files, +45)...
🔄 Generating squash commit message...
   feat(api): Add user authentication endpoints

   Implement login and token refresh endpoints with JWT validation.
   Includes comprehensive test coverage and input validation.
✅ Squashed @ a1b2c3d
🔄 Running pre-merge test:
   uv run pytest

============================= test session starts ==============================
collected 3 items

tests/test_auth.py::test_login_success PASSED                            [ 33%]
tests/test_auth.py::test_login_invalid_password PASSED                   [ 66%]
tests/test_auth.py::test_token_validation PASSED                         [100%]

============================== 3 passed in 0.8s ===============================

🔄 Running pre-merge lint:
   uv run ruff check

All checks passed!

🔄 Merging 1 commit to main @ a1b2c3d (no rebase needed)
   * a1b2c3d feat(api): Add user authentication endpoints
    api/auth.py        | 31 +++++++++++++++++++++++++++++++
    tests/test_auth.py | 14 ++++++++++++++
    2 files changed, 45 insertions(+)
✅ Merged to main (1 commit, 2 files, +45)
🔄 Removing feature-auth worktree & branch in background (ancestor of main)

See wt switch --help and wt merge --help for skipping hooks, template variables, security details.

Claude Code Status Tracking

The Worktrunk plugin adds Claude Code session tracking to wt list:

$ wt list
  Branch       Status         HEAD±    main↕  Path                Remote⇅  Commit    Age   Message
@ main             ^                          ./repo                       b834638e  1d    Initial commit
+ feature-api      ↑  🤖              ↑1      ./repo.feature-api           9606cd0f  1d    Add REST API endpoints
+ review-ui      ? ↑  💬              ↑1      ./repo.review-ui             afd3b353  1d    Add dashboard component
+ wip-docs       ?_                           ./repo.wip-docs              b834638e  1d    Initial commit

⚪ Showing 4 worktrees, 2 ahead
  • 🤖 — Claude is working (processing a prompt)
  • 💬 — Claude is idle or waiting for input

Install the plugin:

claude plugin marketplace add max-sixty/worktrunk
claude plugin install worktrunk@worktrunk

Set status markers manually for any workflow:

wt config status set "🚧"           # Current branch
wt config status set "✅" --branch feature  # Specific branch
git config worktrunk.status.feature "💬"    # Direct git config

wt select

Interactive worktree selector with fuzzy search and diff preview. Unix only.

Preview tabs (toggle with 1/2/3):

  • Tab 1: Working tree changes (uncommitted)
  • Tab 2: History (commits not on main highlighted)
  • Tab 3: Branch diff (changes ahead of main)

wt list statusline

Single-line status for shell prompts, starship, or editor integrations.

Claude Code integration (--claude-code): Reads workspace context from stdin, outputs directory, branch status, and model name.

~/w/myproject.feature-auth  !🤖  ±+42 -8  ↑3  ⇡1  ●  | Opus

Add to ~/.claude/settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "wt list statusline --claude-code"
  }
}

Tips & patterns

Alias for new worktree + agent:

alias wsc='wt switch --create --execute=claude'
wsc new-feature  # Creates worktree, runs hooks, launches Claude

Eliminate cold startspost-create hooks install deps and copy caches. See .config/wt.toml for an example using copy-on-write.

Local CI gatepre-merge hooks run before merging. Failures abort the merge.

Track agent status — Custom emoji markers show agent state in wt list. Claude Code hooks can set these automatically. See Custom Worktree Status.

Monitor CI across brancheswt list --full --branches shows PR/CI status for all branches, including those without worktrees. CI column links to PR pages in terminals with hyperlink support.

JSON APIwt list --format=json for dashboards, statuslines, scripts.

Task runners — Reference Taskfile/Justfile/Makefile in hooks:

[post-create]
"setup" = "task install"

[pre-merge]
"validate" = "just test lint"

Shortcuts^ = default branch, @ = current branch, - = previous worktree. Example: wt switch --create hotfix --base=@ branches from current HEAD.

Commands Reference

wt switch — Switch to a worktree
Usage: wt switch [OPTIONS] <BRANCH>

Arguments:
  <BRANCH>
          Branch, path, '@' (HEAD), '-' (previous), or '^' (main)

Options:
  -c, --create
          Create a new branch

  -b, --base <BASE>
          Base branch

          Defaults to default branch.

  -x, --execute <EXECUTE>
          Command to run after switch

  -f, --force
          Skip approval prompts

      --no-verify
          Skip all project hooks

  -h, --help
          Print help (see a summary with '-h')

Global Options:
  -C <path>
          Working directory for this command

      --config <path>
          User config file path

  -v, --verbose
          Show commands and debug info

Operation

Worktree resolution

Arguments are resolved using path-first lookup:

  1. Compute the expected path for the argument (using the configured path template)
  2. If a worktree exists at that path, switch to it (regardless of what branch it's on)
  3. Otherwise, treat the argument as a branch name

Example: If repo.foo/ exists but is on branch bar:

  • wt switch foo switches to repo.foo/ (the bar branch worktree)
  • wt switch bar also works (falls back to branch lookup)

Switching to Existing Worktree

  • If worktree exists at expected path or for branch, changes directory via shell integration
  • No hooks run
  • No branch creation

Creating New Worktree (--create)

  1. Creates new branch (defaults to current default branch as base)
  2. Creates worktree in configured location (default: ../{{ main_worktree }}.{{ branch }})
  3. Runs post-create hooks sequentially (blocking)
  4. Shows success message
  5. Spawns post-start hooks in background (non-blocking)
  6. Changes directory to new worktree via shell integration

Hooks

post-create (sequential, blocking)

  • Run after worktree creation, before success message
  • Typically: npm install, cargo build, setup tasks
  • Failures block the operation
  • Skip with --no-verify

post-start (parallel, background)

  • Spawned after success message shown
  • Typically: dev servers, file watchers, editors
  • Run in background, failures logged but don't block
  • Logs: .git/wt-logs/{branch}-post-start-{name}.log
  • Skip with --no-verify

Template variables: {{ repo }}, {{ branch }}, {{ worktree }}, {{ repo_root }}

Security: Commands from project hooks require approval on first run. Approvals are saved to user config. Use --force to bypass prompts. See wt config approvals --help.

Examples

Switch to existing worktree:

wt switch feature-branch

Create new worktree from main:

wt switch --create new-feature

Switch to previous worktree:

wt switch -

Create from specific base:

wt switch --create hotfix --base production

Create and run command:

wt switch --create docs --execute "code ."

Skip hooks during creation:

wt switch --create temp --no-verify

Shortcuts

Use @ for current HEAD, - for previous, ^ for main:

wt switch @                              # Switch to current branch's worktree
wt switch -                              # Switch to previous worktree
wt switch --create new-feature --base=^  # Branch from main (default)
wt switch --create bugfix --base=@       # Branch from current HEAD
wt remove @                              # Remove current worktree
wt merge — Merge worktree into target branch
Usage: wt merge [OPTIONS] [TARGET]

Arguments:
  [TARGET]
          Target branch

          Defaults to default branch.

Options:
      --no-squash
          Skip commit squashing

      --no-commit
          Skip commit, squash, and rebase

      --no-remove
          Keep worktree after merge

      --no-verify
          Skip all project hooks

  -f, --force
          Skip approval prompts

      --stage <STAGE>
          What to stage before committing [default: all]

          Possible values:
          - all:     Stage everything: untracked files + unstaged tracked changes
          - tracked: Stage tracked changes only (like git add -u)
          - none:    Stage nothing, commit only what's already in the index

  -h, --help
          Print help (see a summary with '-h')

Global Options:
  -C <path>
          Working directory for this command

      --config <path>
          User config file path

  -v, --verbose
          Show commands and debug info

Operation

Commit → Squash → Rebase → Pre-merge hooks → Push → Cleanup → Post-merge hooks

Commit

Uncommitted changes are staged and committed with LLM commit message. Use --stage=tracked to stage only tracked files, or --stage=none to commit only what's already staged.

Squash

Multiple commits are squashed into one (like GitHub's "Squash and merge") with LLM commit message. Skip with --no-squash. Safety backup: git reflog show refs/wt-backup/<branch>

Rebase

Branch is rebased onto target. Conflicts abort the merge immediately.

Hooks

Pre-merge commands run after rebase (failures abort). Post-merge commands run after cleanup (failures logged). Skip all with --no-verify.

Push

Fast-forward push to local target branch. Non-fast-forward pushes are rejected.

Cleanup

Worktree and branch are removed. Skip with --no-remove.

Template variables: {{ repo }}, {{ branch }}, {{ worktree }}, {{ repo_root }}, {{ target }}

Security: Commands from project hooks require approval on first run. Approvals are saved to user config. Use --force to bypass prompts. See wt config approvals --help.

Examples

Basic merge to main:

wt merge

Merge without squashing:

wt merge --no-squash

Keep worktree after merging:

wt merge --no-remove

Skip all hooks:

wt merge --no-verify
wt remove — Remove worktree and branch
Usage: wt remove [OPTIONS] [WORKTREES]...

Arguments:
  [WORKTREES]...
          Worktree or branch (@ for current)

Options:
      --no-delete-branch
          Keep branch after removal

  -D, --force-delete
          Delete unmerged branches

      --no-background
          Run removal in foreground

  -h, --help
          Print help (see a summary with '-h')

Global Options:
  -C <path>
          Working directory for this command

      --config <path>
          User config file path

  -v, --verbose
          Show commands and debug info

Operation

Removes worktree directory, git metadata, and branch. Requires clean working tree.

No arguments (remove current)

  • Removes current worktree and switches to main worktree
  • In main worktree: switches to default branch

By name (remove specific)

  • Removes specified worktree(s) and branches
  • Current worktree removed last (switches to main first)

Worktree resolution

Arguments are resolved to worktrees using path-first lookup:

  1. Compute the expected path for the argument (using the configured path template)
  2. If a worktree exists at that path, use it (regardless of what branch it's on)
  3. Otherwise, treat the argument as a branch name

Example: If repo.foo/ exists but is on branch bar:

  • wt remove foo removes repo.foo/ and the bar branch
  • wt remove bar also works (falls back to branch lookup)

Conflict detection: If path repo.foo/ has a worktree on branch bar, but branch foo has a different worktree at repo.bar/, an error is raised.

Special arguments:

  • @ - current worktree (by path, works in detached HEAD)
  • - - previous worktree (from switch history)
  • ^ - main/default branch worktree

Branch deletion

By default, branches are deleted only when their content has been integrated:

  • Traditional merge: branch is an ancestor of the target (git's -d behavior)
  • Squash merge/rebase: branch's tree SHA matches target's tree SHA

This handles workflows where PRs are squash-merged or rebased, which don't preserve commit ancestry but do integrate the content. Use -D to delete unintegrated branches, or --no-delete-branch to always keep branches.

Background removal (default)

  • Returns immediately for continued work
  • Logs: .git/wt-logs/{branch}-remove.log
  • Use --no-background for foreground (blocking)

Cleanup

Stops any git fsmonitor daemon for the worktree before removal. This prevents orphaned processes when using builtin fsmonitor (core.fsmonitor=true). No effect on Watchman users.

Examples

Remove current worktree and branch:

wt remove

Remove specific worktree and branch:

wt remove feature-branch

Remove worktree but keep branch:

wt remove --no-delete-branch feature-branch

Remove multiple worktrees:

wt remove old-feature another-branch

Remove in foreground (blocking):

wt remove --no-background feature-branch

Switch to default in main:

wt remove  # (when already in main worktree)
wt list — List worktrees and optionally branches
Usage: wt list [OPTIONS]
       wt list <COMMAND>

Commands:
  statusline  Single-line status for shell prompts

Options:
      --format <FORMAT>
          Output format (table, json)

          [default: table]

      --branches
          Include branches without worktrees

      --remotes
          Include remote branches

      --full
          Show CI, conflicts, diffs

      --progressive
          Show fast info immediately, update with slow info

          Displays local data (branches, paths, status) first, then updates with remote data (CI, upstream) as it arrives. Auto-enabled for TTY.

  -h, --help
          Print help (see a summary with '-h')

Global Options:
  -C <path>
          Working directory for this command

      --config <path>
          User config file path

  -v, --verbose
          Show commands and debug info

Columns

  • Branch: Branch name
  • Status: Quick status symbols (see Status Symbols below)
  • HEAD±: Uncommitted changes vs HEAD (+added -deleted lines, staged + unstaged)
  • main↕: Commit count ahead↑/behind↓ relative to main (commits in HEAD vs main)
  • main…± (--full): Line diffs in commits ahead of main (+added -deleted)
  • Path: Worktree directory location
  • Remote⇅: Commits ahead⇡/behind⇣ relative to tracking branch (e.g. origin/branch)
  • CI (--full): CI pipeline status (tries PR/MR checks first, falls back to branch workflows)
    • passed (green) - All checks passed
    • running (blue) - Checks in progress
    • failed (red) - Checks failed
    • conflicts (yellow) - Merge conflicts with base
    • no-ci (gray) - PR/MR or workflow found but no checks configured
    • (blank) - No PR/MR or workflow found, or gh/glab CLI unavailable
    • (dimmed) - Stale: unpushed local changes differ from PR/MR head
  • Commit: Short commit hash (8 chars)
  • Age: Time since last commit (relative)
  • Message: Last commit message (truncated)

Status Symbols

Order: +!? ✖⚠≡_ ↻⋈ ↑↓↕ ⇡⇣⇅ ⎇⌫⊠

  • + Staged files (ready to commit)
  • ! Modified files (unstaged changes)
  • ? Untracked files present
  • Merge conflicts - unresolved conflicts in working tree (fix before continuing)
  • Would conflict - merging into main would fail
  • Working tree matches main (identical contents, regardless of commit history)
  • _ No commits (no commits ahead AND no uncommitted changes)
  • Rebase in progress
  • Merge in progress
  • Ahead of main branch
  • Behind main branch
  • Diverged (both ahead and behind main)
  • Ahead of remote tracking branch
  • Behind remote tracking branch
  • Diverged (both ahead and behind remote)
  • Branch indicator (shown for branches without worktrees)
  • Prunable worktree (directory missing, can be pruned)
  • Locked worktree (protected from auto-removal)

Rows are dimmed when there's no marginal contribution ( matches main OR _ no commits).

JSON Output

Use --format=json for structured data. Each object contains two status maps with the same fields in the same order as Status Symbols above:

status - variant names for querying:

  • working_tree: {untracked, modified, staged, renamed, deleted} booleans
  • branch_state: "" | "Conflicts" | "MergeTreeConflicts" | "MatchesMain" | "NoCommits"
  • git_operation: "" | "Rebase" | "Merge"
  • main_divergence: "" | "Ahead" | "Behind" | "Diverged"
  • upstream_divergence: "" | "Ahead" | "Behind" | "Diverged"
  • user_status: string (optional)

status_symbols - Unicode symbols for display (same fields, plus worktree_attrs: ⎇/⌫/⊠)

Note: locked and prunable are top-level fields on worktree objects, not in status.

Worktree position fields (for identifying special worktrees):

  • is_main: boolean - is the main/default worktree
  • is_current: boolean - is the current working directory (present when true)
  • is_previous: boolean - is the previous worktree from wt switch (present when true)

Query examples:

# Find worktrees with conflicts
jq '.[] | select(.status.branch_state == "Conflicts")'

# Find worktrees with untracked files
jq '.[] | select(.status.working_tree.untracked)'

# Find worktrees in rebase or merge
jq '.[] | select(.status.git_operation != "")'

# Get branches ahead of main
jq '.[] | select(.status.main_divergence == "Ahead")'

# Find locked worktrees
jq '.[] | select(.locked != null)'

# Get current worktree info (useful for statusline tools)
jq '.[] | select(.is_current == true)'
wt config — Manage configuration and shell integration
Usage: wt config [OPTIONS] <COMMAND>

Commands:
  shell      Shell integration setup
  create     Create global configuration file
  show       Show configuration files & locations
  cache      Manage caches (CI status, default branch)
  status     Manage branch status markers
  approvals  Manage command approvals

Options:
  -h, --help
          Print help (see a summary with '-h')

Global Options:
  -C <path>
          Working directory for this command

      --config <path>
          User config file path

  -v, --verbose
          Show commands and debug info

Setup Guide

  1. Set up shell integration

    wt config shell install
    

    Or manually add to the shell config:

    eval "$(wt config shell init bash)"
    
  2. (Optional) Create config file

    wt config create
    

    This creates ~/.config/worktrunk/config.toml with examples.

  3. (Optional) Enable LLM commit messages

    Install: uv tool install -U llm Configure: llm keys set anthropic Add to config.toml:

    [commit-generation]
    command = "llm"
    

LLM Setup Details

For Claude:

llm install llm-anthropic
llm keys set anthropic
llm models default claude-haiku-4-5-20251001

For OpenAI:

llm keys set openai

Use wt config show to view the current configuration. Docs: https://llm.datasette.io/ | https://github.com/sigoden/aichat

Configuration Files

Global config (user settings):

  • Location: ~/.config/worktrunk/config.toml (or WORKTRUNK_CONFIG_PATH)
  • Run wt config create --help to view documented examples

Project config (repository hooks):

  • Location: .config/wt.toml in repository root
  • Contains: post-create, post-start, pre-commit, pre-merge, post-merge hooks
wt step — Workflow building blocks
Usage: wt step [OPTIONS] <COMMAND>

Commands:
  commit       Commit changes with LLM commit message
  squash       Squash commits with LLM commit message
  push         Push changes to local target branch
  rebase       Rebase onto target
  post-create  Run post-create hook
  post-start   Run post-start hook
  pre-commit   Run pre-commit hook
  pre-merge    Run pre-merge hook
  post-merge   Run post-merge hook

Options:
  -h, --help  Print help

Global Options:
  -C <path>            Working directory for this command
      --config <path>  User config file path
  -v, --verbose        Show commands and debug info

FAQ

Worktrunk executes commands in three contexts:

  1. Project hooks (.config/wt.toml) - Automation for worktree lifecycle
  2. LLM commands (~/.config/worktrunk/config.toml) - Commit message generation
  3. --execute flag - Commands provided explicitly

Commands from project hooks and LLM configuration require approval on first run. Approved commands are saved to ~/.config/worktrunk/config.toml under the project's configuration. If a command changes, Worktrunk requires new approval.

Example approval prompt:

🟡 repo needs approval to execute 3 commands:

⚪ post-create install:
   echo 'Installing dependencies...'

⚪ post-create build:
   echo 'Building project...'

⚪ post-create test:
   echo 'Running tests...'

💡 Allow and remember? [y/N]

Use --force to bypass prompts (useful for CI/automation).

vs. Branch Switching

Branch switching uses one directory, so only one agent can work at a time. Worktrees give each agent its own directory.

vs. Plain git worktree

Git's built-in worktree commands work but require manual lifecycle management:

# Plain git worktree workflow
git worktree add -b feature-branch ../myapp-feature main
cd ../myapp-feature
# ...work, commit, push...
cd ../myapp
git merge feature-branch
git worktree remove ../myapp-feature
git branch -d feature-branch

Worktrunk automates the full lifecycle:

wt switch --create feature-branch  # Creates worktree, runs setup hooks
# ...work...
wt merge                            # Squashes, merges, removes worktree

What git worktree doesn't provide:

  • Consistent directory naming and cleanup validation
  • Project-specific automation (install dependencies, start services)
  • Unified status across all worktrees (commits, CI, conflicts, changes)

Worktrunk adds path management, lifecycle hooks, and wt list --full for viewing all worktrees—branches, uncommitted changes, commits ahead/behind, CI status, and conflicts—in a single view.

vs. git-machete / git-town

Different scopes:

  • git-machete: Branch stack management in a single directory
  • git-town: Git workflow automation in a single directory
  • worktrunk: Multi-worktree management with hooks and status aggregation

These tools can be used together—run git-machete or git-town inside individual worktrees.

vs. Git TUIs (lazygit, gh-dash, etc.)

Git TUIs operate on a single repository. Worktrunk manages multiple worktrees, runs automation hooks, and aggregates status across branches. TUIs work inside each worktree directory.

Errors related to tree-sitter or C compilation (C99 mode, le16toh undefined) can be avoided by installing without syntax highlighting:

cargo install worktrunk --no-default-features

This disables bash syntax highlighting in command output but keeps all core functionality. The syntax highlighting feature requires C99 compiler support and can fail on older systems or minimal Docker images.

Running Tests

Quick tests (no external dependencies):

cargo test --lib --bins           # Unit tests (~200 tests)
cargo test --test integration     # Integration tests without shell tests (~300 tests)

Full integration tests (requires bash, zsh, fish):

cargo test --test integration --features shell-integration-tests

Dependencies for shell integration tests:

  • bash, zsh, fish shells
  • Quick setup: ./dev/setup-claude-code-web.sh (installs shells on Linux)

Releases

Use cargo-release to publish new versions:

cargo install cargo-release

# Bump version, update Cargo.lock, commit, tag, and push
cargo release patch --execute   # 0.1.0 -> 0.1.1
cargo release minor --execute   # 0.1.0 -> 0.2.0
cargo release major --execute   # 0.1.0 -> 1.0.0

This updates Cargo.toml and Cargo.lock, creates a commit and tag, then pushes to GitHub. The tag push triggers GitHub Actions to build binaries, create the release, and publish to crates.io.

Run without --execute to preview changes first.