A CLI for managing git worktrees in bare repos. One branch, one directory, no stashing.
groot new feat/my-feature # fetch, diff-check, create worktree, open editor
groot open feat/my-feature # re-open an existing worktree in the editor
groot rm feat/my-feature # run hooks, remove worktree, prune
groot list # table of active worktrees
groot purge # remove all non-protected worktrees
groot init <url> # clone bare repo and write starter config
groot assumes you're working with a bare clone — a repo with no working tree of its own, where every branch gets its own directory via git worktree. This gives you instant branch switching with no stashing, no checkout thrash, and no editor reload.
~/worktrees/
myrepo/
main/ ← worktree for main
feat-login/ ← worktree for feat/login
fix-crash/ ← worktree for fix/crash-on-nil
git clone --bare https://github.com/kirboyyy/groot ~/repos/groot.git
cd ~/repos/groot.git
make install
# macOS → installs to ~/go/bin/groot
# Linux → installs to ~/.local/bin/grootOr build manually:
go build -o groot ./cmd/grootBoth paths are on $PATH by default for their respective platforms.
Config lives at ~/.config/groot/config.json. Run groot init to generate a starter file.
{
"worktreesBase": "~/worktrees",
"editor": "kitty",
"editorArgs": [],
"autoFetch": true,
"protectedBranches": ["main", "master"]
}| Key | Default | Description |
|---|---|---|
worktreesBase |
~/worktrees |
Root directory for all worktrees |
editor |
kitty |
Command to launch when opening a worktree |
editorArgs |
[] |
Extra args passed to the editor before the path |
autoFetch |
true |
Fetch from origin before creating a worktree |
protectedBranches |
["main","master"] |
Branches purge will never touch |
{ "editor": "code", "editorArgs": [] }
{ "editor": "idea", "editorArgs": [] }
{ "editor": "zed", "editorArgs": [] }
{ "editor": "nvim", "editorArgs": [] }
{ "editor": "kitty", "editorArgs": ["--directory"] }groot runs: <editor> [editorArgs...] <worktree-path>, detached from the process.
Hooks are executable scripts placed in .groot/hooks/ inside the bare repo. Each hook receives the worktree path as $GROOT_WORKTREE_PATH and branch as $GROOT_BRANCH.
myrepo.git/
.groot/
hooks/
after-new
before-remove
after-remove
before-purge
after-purge
Example before-remove:
#!/bin/sh
# Close project in IDEA before removing the worktree
idea "$GROOT_WORKTREE_PATH" --wait --close-project| Hook | When |
|---|---|
after-new |
After worktree is created and editor is launched |
before-remove |
Before a worktree is removed (single rm) |
after-remove |
After worktree removal and prune |
before-purge |
Before purge starts removing worktrees |
after-purge |
After all worktrees have been removed and pruned |
Hooks are optional — missing scripts are silently skipped. A non-zero exit code aborts the operation.
Creates a worktree for <branch> and opens it in your configured editor.
Branch resolution order:
- Local branch exists → check it out
origin/<branch>exists → create local tracking branch- Neither → create a new branch
If autoFetch is enabled: fetches from origin first, then prompts if the branch is behind.
$ groot new feat/login
feat/login 3 commits behind origin — continue? [y/N]
🌲 ~/worktrees/myrepo/feat-login
Opens an existing worktree in the editor. With no argument, shows an interactive list to pick from.
Removes one or more worktrees. With no argument, shows a checkbox list to select which to remove. Accepts multiple arguments directly.
- Resolves the path
- Runs
before-removehook git worktree removegit worktree prune- Runs
after-removehook
Prints a table of all active worktrees, skipping the bare repo entry.
BRANCH PATH HEAD
feat/login ~/worktrees/myrepo/feat-login a3f2c1d
fix/crash-on-nil ~/worktrees/myrepo/fix-crash b91e4aa
main ~/worktrees/myrepo/main ff02381
Removes all worktrees except protected branches. Prompts for confirmation.
$ groot purge
feat/login ~/worktrees/myrepo/feat-login
fix/crash-on-nil ~/worktrees/myrepo/fix-crash
remove 2 worktrees? [y/N]
Clones <url> as a bare repo and writes a starter ~/.config/groot/config.json (non-destructively — won't overwrite existing config).
groot init https://github.com/you/myrepo# bash
groot completion bash > /etc/bash_completion.d/groot
# zsh
groot completion zsh > "${fpath[1]}/_groot"
# fish
groot completion fish > ~/.config/fish/completions/groot.fishMIT