Manage git worktrees with ease

Create, list, and prune worktrees efficiently. Run commands from anywhere in your project.

terminal
# Initialize a worktree setup
$ grove init https://github.com/captainsafia/grove.git
✓ Initialized worktree setup: grove

# Add a worktree for a branch
$ grove add main
✓ Created worktree: main

# Navigate to the worktree
$ grove go main
✓ Entering worktree: main

Install

Single binary, no dependencies.

Linux (x64/arm64), macOS (x64/arm64), Windows (x64).

$ curl https://i.safia.sh/captainsafia/grove | sh

Usage

Common workflows and examples for using Grove.

Initialize a new worktree setup

Create a bare clone optimized for worktrees:

grove init https://github.com/user/repo.git

Add a new worktree

Create a worktree for a branch (creates branch if it doesn't exist):

grove add feature-branch

Or let Grove generate a default adjective-noun name:

grove add
# Example generated name: quiet-meadow
# If .groverc sets "branchPrefix": "safia", example: safia/quiet-meadow
# Directory remains: quiet-meadow
# branchPrefix only accepts alphanumeric characters

With tracking for a remote branch:

grove add feature-branch --track origin/feature-branch

Optional bootstrap commands from .groverc run in the new worktree:

{
  "branchPrefix": "safia",
  "bootstrap": {
    "commands": [
      { "program": "npm", "args": ["install"] },
      { "program": "cargo", "args": ["check"] }
    ]
  }
}

Place .groverc in the Grove project root (next to the bare clone directory).

When grove add is called without an explicit branch name, Grove generates an adjective-noun name and prepends branchPrefix to the branch name when configured. branchPrefix must be alphanumeric only (letters and numbers). The worktree directory keeps the generated base name.

Commands must be portable across Linux/macOS/Windows and use executable + args only (no shell operators like && or pipes). If one command fails, Grove continues and reports a partial bootstrap state.

Navigate to a worktree

Open a new shell session in a worktree directory:

grove go feature-branch

Navigate by partial branch name for nested branches:

grove go my-feature

Exit the shell (Ctrl+D or exit) to return to your previous directory.

List worktrees

Show all worktrees (alias: grove ls):

grove list

Show detailed information:

grove list --details

Show only dirty worktrees:

grove list --dirty

Sync with origin

Update the bare clone with the latest changes from origin:

grove sync

Sync a specific branch:

grove sync --branch develop

Prune worktrees

Preview what would be removed:

grove prune --dry-run

Remove worktrees for branches merged to main:

grove prune

Remove worktrees older than 30 days (supports human-friendly or ISO 8601 format):

grove prune --older-than 30d
# or
grove prune --older-than P30D

Use a different base branch:

grove prune --base develop

Remove a worktree

Remove a specific worktree (alias: grove rm):

grove remove feature-branch

Force removal even with uncommitted changes:

grove remove feature-branch --force

Note: Dirty worktrees require --force to remove.

Self-update

Update grove to the latest version:

grove self-update

Update to a specific version:

grove self-update v1.0.0

Update to a specific PR build (requires GitHub CLI):

grove self-update --pr 42

Commands

Command Description
grove init <git-url> Create a new worktree setup
grove add [name] Create a new worktree (name optional)
grove go <name> Navigate to a worktree
grove list (ls) [options] List all worktrees
grove sync [options] Sync the bare clone with origin
grove prune [options] Remove worktrees for merged branches
grove remove (rm) <name> Remove a specific worktree
grove self-update [version] Update grove to a specific version or PR
grove version Show version information
grove help [command] Show help information