Worktrees
Creating, managing, and understanding worktrees in Canopy.
Worktrees
Canopy uses Git worktrees to run multiple agents on the same repo in parallel. Each worktree gets its own branch and working directory, so agents can make changes without stepping on each other.
Creating a Worktree
Open the New Worktree dialog from the + button in the sidebar, or right-click any worktree card and select New Worktree. The dialog supports three creation modes: creating a new branch, using an existing local branch, or checking out a PR branch from GitHub.
New Branch (Default)
This is the default mode. A New Branch / Existing Branch toggle appears at the top of the dialog (hidden when opening from a PR). In New Branch mode, you fill in the following fields:
- Link Issue (optional) — a searchable issue selector. Picking an issue auto-generates the branch name in
{type}/issue-{number}-{slug}format, with the branch type prefix detected from the issue's labels or title keywords. - Base Branch — a searchable dropdown with fuzzy matching. Branches are grouped into "Recent" and others. Branches already checked out in another worktree show an in use badge; clicking one navigates to that worktree instead of selecting it. Defaults to the current branch on dialog open, or
main/masterif none is current. - New Branch Name — a freeform input field. As you type, an autocomplete popover suggests known branch type prefixes:
feature/,bugfix/,chore/,docs/,refactor/,test/,release/,ci/,deps/,perf/,style/, andwip/. Common aliases are normalised automatically (featbecomesfeature,fixorhotfixbecomesbugfix). If the branch name already exists, Canopy auto-increments it with a numeric suffix (e.g.feature/login-1) and shows a green hint. - Worktree Path — auto-generated from the branch name using your configured path pattern. You can edit it manually or use the folder picker button. If the path already exists on disk, it is auto-incremented in the same way.
- Create from remote branch — a checkbox that tells Canopy to base the new worktree off the remote tracking branch. Auto-selected when the chosen base branch is remote.
- Assign to me — appears when a GitHub issue is linked and a GitHub account is configured. Assigns the issue to you on creation. This preference is remembered across sessions.
- Run Recipe (optional) — a dropdown for selecting a recipe to run after the worktree is created. Shows your project's default recipe if one is configured.
. or -, cannot end with a space or ., and cannot contain \, :, or ... The prefix component is validated separately from the rest of the name.Existing Branch
Click the Existing Branch tab in the mode toggle to switch to this mode. Use it when you want to create a worktree for a branch that already exists locally, such as resuming interrupted work or picking up a branch created outside Canopy.
The dialog simplifies: the base branch picker and branch name input are replaced by a single Select Branch dropdown. This dropdown lists local branches that are not already checked out in another worktree. Remote-only branches and branches already in use are excluded. A search box at the top of the dropdown lets you filter by name.
Once you select a branch, the worktree path is generated automatically (with the same auto-increment logic if the path already exists). The Create button stays disabled until you make a selection. On submit, Canopy uses the existing branch as-is rather than creating a new one.
Switching between New Branch and Existing Branch mode resets the branch selection, search query, and any validation errors.
PR Checkout
You can create a worktree directly from a pull request using the Create Worktree button in the GitHub Integration panel's PR list. This opens the dialog in PR Checkout mode: the title changes to Checkout PR Branch, the mode toggle is hidden, and a banner at the top shows the PR number and title.
On open, Canopy automatically resolves the PR's branch through the following sequence:
- Checks whether the branch exists as a remote tracking branch (
origin/{headRefName}). If found, selects it. - Checks whether the branch exists locally. If found, selects it.
- If neither exists, runs
git fetch origin pull/{number}/head:{headRefName}to fetch the branch from GitHub. - After the fetch completes, re-checks for the local branch and selects it if available.
If the branch still cannot be resolved after fetching, the Create button is disabled and an amber warning banner appears:
{headRefName} from the remote. You can try running git fetch origin manually and reopening this dialog.You will need to fetch the branch manually and reopen the dialog before you can create the worktree.
Branch Prefix
Canopy can automatically prepend a namespace prefix to new branch names. Configure this in Project Settings > Worktree Setup > Branch Prefix. Three options are available:
- None (default) — no prefix is added.
- Username — reads your
git config user.name, converts it to a lowercase slug (e.g.alice), and prepends it asalice/. - Custom — lets you enter any string you like (e.g.
myteam/orfeature/).
A live preview shows what a branch name will look like with the prefix applied. The configured prefix is pre-populated into the branch name input when the dialog opens. If an issue is linked, the prefix is prepended to the auto-generated branch name from the issue.
This namespace prefix is separate from the branch type prefix (feature/, bugfix/, etc.) that comes from issue labels or the autocomplete popover. The two stack together, so with a username prefix of alice/ and a type prefix of feature/, the resulting branch name would be alice/feature/issue-42-add-login.
This is a per-project setting. There is no global branch prefix option.
Error States
If something goes wrong during worktree creation, Canopy shows a banner in the dialog with a friendly message. Common errors include:
| Condition | Message |
|---|---|
| Branch already checked out elsewhere | "This branch is already open in another worktree." An Open Worktree button appears so you can navigate directly to it. |
| Directory creation failed | "Cannot create directory — check permissions or available disk space." |
| Invalid branch name | "The branch name contains invalid characters." |
| Worktree path conflict | "A worktree already exists at this path." |
For any other git error, the raw output is shown directly. A collapsible "Show details" section reveals the full git output when the friendly message differs from the raw error.
Bulk Worktree Creation
When you want to spin up worktrees for several GitHub issues or pull requests at once, bulk creation handles the entire batch in a single operation. Select the items you need, and Canopy creates a worktree for each one with per-item progress tracking, automatic retry for transient errors, and step-level recovery if something goes wrong partway through.
To start, open the Issues or Pull Requests panel from the toolbar. Hover over any row to reveal its checkbox, then check the items you want. Once the first item is checked, all rows show their checkboxes for the rest of the session. A floating action bar slides up from the bottom showing the selection count and a Create Worktrees button. See GitHub Integration for more on the GitHub panel.
Selection shortcuts
Shift-click extends the selection as a range from the last checked item to the clicked one. A Select all button appears in the header when a search is active, and for issues there is also a Select unassigned option. Press Escape to clear the selection. Note that the selection resets when the dropdown closes.
#42 for a single issue, #42,#43,#44 for multiple, or #40-45 for a range. Select what you need from the results, then click Create Worktrees.The plan view
Clicking Create Worktrees opens a dialog showing every selected item with its proposed branch name. Before execution begins, you can configure two optional settings:
- Assign to me (issues only) — assigns each issue to your GitHub account during creation. This toggle is remembered across sessions.
- Run Recipe — picks a recipe to run on each new worktree after creation. Defaults to your project's configured default recipe, or the last recipe you selected for bulk creation if you've changed it. See Recipes for more on recipes.
Items that cannot be created are shown at reduced opacity with a skip badge explaining why:
- Closed or Merged — the issue or PR is no longer open
- Has worktree — a worktree already exists for this item
- No branch info — the PR lacks branch metadata (typically fork PRs)
If every selected item is skipped, Canopy shows a toast and the execution view never appears.
Branch naming
For issues, Canopy generates a branch name from the issue's labels and title. Labels like bug or hotfix produce a bugfix/ prefix, feature or enhancement produce feature/, and docs produces docs/. If no label matches, the title is scanned for keywords, falling back to feature/. The full pattern is {prefix}/issue-{number}-{slug}, for example feature/issue-42-add-dark-mode. If the branch name already exists, Canopy appends a numeric suffix automatically.
For pull requests, Canopy uses the PR's existing source branch directly.
Execution and progress
Once you click the confirm button, each item moves through a series of stages: creating the worktree, spawning terminals (if a recipe is selected), assigning the issue (if enabled), and verifying terminal health. The dialog shows a spinner, checkmark, or warning icon for each item as it progresses, along with a progress bar tracking the overall batch.
Canopy processes up to two items concurrently. This limit is deliberate: running more in parallel risks git lock conflicts when multiple worktree operations target the same repository simultaneously.
Automatic retry
Transient errors are retried automatically up to two times per item with exponential backoff (starting at 3 seconds, capped at 30 seconds). A small retry badge appears on the item row when this happens. Errors that trigger automatic retry include git lock file conflicts, rate limits, and network timeouts. Non-transient errors like validation failures or missing resources fail immediately without retry.
Post-batch verification
When a recipe is selected, Canopy waits briefly after all items complete and then checks whether any spawned terminals crashed immediately after launch. Items with crashed terminals are downgraded from succeeded to failed, so you can use Retry Failed to re-spawn those terminals without recreating the worktree.
Completion
When the batch finishes, a toast notification shows the result: either all worktrees created successfully, or a count of successes and failures. The dialog footer shows a Done button that closes the dialog and switches to the last successfully created worktree. If any items failed, a Retry Failed button appears alongside it.
Moving an Agent to a New Worktree
If you're working with an agent in the main worktree and decide the work should be isolated to its own branch, you can create a new worktree and move the agent there in one step. The agent restarts fresh in the new worktree with your recent terminal output injected as context, giving it enough background to continue without you having to re-explain the task.
This option is available on agent panels only (Claude Code, Gemini CLI, Codex CLI, OpenCode). Cursor agent panels, plain terminals, browser panels, notes, and dev-preview panels don't have it.
How to access
- Right-click the agent panel header and select Move to New Worktree… from the context menu
- Open the three-dot menu (•••) in the panel header and select Move to New Worktree…
What happens
- Canopy captures the terminal's visible output buffer (up to 20,000 characters, with escape sequences stripped).
- The standard New Worktree dialog opens. Choose a base branch and enter a new branch name, the same as creating any worktree.
- On confirm, the panel moves to the new worktree: its working directory and worktree association update, and the agent restarts fresh in the new directory.
- Once the agent reaches an idle state, the captured history is injected as an opening prompt so the agent has context on what was done previously.
The transferred history is what was visible in the terminal output, not structured session data. It's a best-effort handoff: the agent gets enough context to continue, but may not have full recall of earlier reasoning. If the terminal buffer is empty when you trigger the action (for example, on a panel that was just launched or restarted from scratch), no history is injected and the agent starts completely fresh.
The new worktree also starts without node_modules and other untracked files. Re-run your setup steps manually, or use lifecycle scripts to automate this.
Switching Worktrees
Click a worktree card in the sidebar to switch to it, or use Cmd+Alt+1 through 9 to switch by position. When you switch worktrees:
- The panel grid shows terminals belonging to that worktree
- Active worktree terminals get higher refresh rates
- Background worktree terminals drop to 1 FPS to save resources
Deleting a Worktree
Delete a worktree from its context menu in the sidebar. You can optionally delete the associated branch. The main worktree (your repository root) is protected and cannot be deleted. If a worktree has running processes or a stuck teardown, the context menu also offers Force Delete to remove it immediately with a shorter timeout.
Comparing Worktrees
When multiple agents are working in parallel across different worktrees, you'll want to see how their changes diverge. The cross-worktree diff opens a side-by-side comparison between any two worktrees, showing every file that differs between their branch tips (based on each branch's latest commit; uncommitted changes are not included).
There are a few ways to open it:
- Right-click a worktree card in the sidebar and select Compare Worktrees... — the worktree you clicked is pre-selected as the left (base) side
- Three-dot menu on any worktree card header — same behavior, same pre-selection
- Action palette — search for "Compare Worktrees" to open the modal without any pre-selection
Selecting worktrees
The modal has two dropdowns at the top: Left (base) and Right (compare), separated by a "vs" label. The main worktree appears first in both lists, followed by the rest alphabetically. Each dropdown disables whichever worktree is already selected on the other side, so you can't compare a worktree against itself. The comparison runs automatically as soon as both sides are selected.
File list
A sidebar on the left shows all changed files with a count header (e.g. "12 files changed"). Each file has a single-letter status badge:
| Badge | Color | Meaning |
|---|---|---|
| A | Green | Added — file exists only in the right worktree |
| D | Red | Deleted — file exists only in the left worktree |
| M | Amber | Modified — file changed between the two branches |
| R | Blue | Renamed — file was moved or renamed |
| C | Purple | Copied — file was duplicated from another path |
Click any file in the list to load its diff in the main panel.
Diff panel
The right side of the modal shows a split diff view with syntax highlighting. Additions on the right (compare) branch appear in green; deletions appear in red. Binary files and files larger than 1 MB display a placeholder message instead of the diff content.
The left side is the base for the comparison. If you want to see what another agent added, put your worktree (or main) on the left and the agent's branch on the right. Additions that exist only on the right side show up as green lines.
The comparison uses a direct branch-tip diff rather than a merge-base comparison, so it shows the complete difference between the two snapshots regardless of shared history. For staging and committing changes within a single worktree, see Review Hub.
Worktree Cards
Each worktree appears as a card in the sidebar showing:
- Branch name with a badge for the main worktree (moves to the secondary row when an issue is linked)
- Mood indicator (stable / active / stale / error)
- GitHub issue title as the card headline when linked, plus PR badges showing PR state
- Git status — modified file count, insertions, and deletions
- Last commit message summary
- Terminal list — all panels in this worktree with their type and state
- Quick actions — launch agents, run recipes, open in editor
Local git state on worktree cards (commits, branch switches, and file changes) stays current through file-system event watching rather than polling. Canopy monitors git-internal files and the working directory for changes, so the sidebar reflects new commits and branch switches within roughly 300 milliseconds. During sustained file writes from an agent, updates arrive within one to two seconds. A 30-second polling fallback kicks in automatically if the OS-level watcher is unavailable, which primarily occurs on Linux when the inotify watch limit is exceeded.
On Linux, if you notice sidebar updates lagging by around 30 seconds, the inotify watch limit may be exhausted. Check Canopy's log file (Settings > Troubleshooting > Open Log File) for a watcher initialization warning. To fix this, raise the limit with sudo sysctl -w fs.inotify.max_user_watches=524288. To make the change permanent, add fs.inotify.max_user_watches=524288 to /etc/sysctl.d/99-inotify.conf and run sudo sysctl --system.
Cards can be collapsed by double-clicking anywhere on the card header. Collapsed cards take up less vertical space in the sidebar while still showing key status information. Double-click again to expand.
Lifecycle Stage Chip
A small triangular chip in the top-left corner of each worktree card tells you the next action needed for that worktree. The chip is color-coded and appears only when there is something actionable.
| Color | State | Meaning |
|---|---|---|
| Amber | Waiting | An agent in this worktree is waiting for your input. |
| Green | Complete | The worktree has a linked issue, an open PR, no uncommitted changes, and no active agents. It is ready for review. |
| Purple | Cleanup | The PR has been merged. This worktree has served its purpose and the branch can be deleted. |
When multiple conditions apply, the chip shows the highest-priority state. Priority order is: cleanup, then complete, then waiting. A merged PR always takes precedence over other signals because the lifecycle action (delete the worktree) is the most definitive.
The chip only appears on secondary worktrees. The main/primary worktree never shows a lifecycle chip. The chip is also absent while git data is still loading for a worktree.
When a worktree reaches the "complete" state (green chip), you can open the Review Hub from its context menu to stage, review, and push the changes.
Quick State Filter
A row of pill-shaped tabs sits above the worktree list, letting you filter worktrees by their current agent state. The bar is visible whenever you have at least one secondary worktree.
Four tabs are available: All, Working, Waiting, and Finished. The Working, Waiting, and Finished tabs each show a count in parentheses (e.g. "Working (3)") that updates reactively as agent states change. Counts exclude the main worktree.
The active tab gets an accent-tinted background to make the current filter obvious. Clicking a tab that is already active resets the filter back to All, so you can toggle a filter on and off with the same button.
Each filter maps to specific worktree states:
- Working shows worktrees with at least one agent session in the working or running state
- Waiting shows worktrees where an agent is waiting for input (amber chip)
- Finished shows worktrees with the complete (green) or cleanup (purple) chip
The quick filter composes with the search bar and the advanced filter popover using AND logic. All three can be active simultaneously, narrowing the list progressively.
Collapsed Cards
When a worktree card is collapsed, the full card body (git status, last commit, terminal list, quick actions) is hidden. What remains in the header row is the branch name and a set of compact session state indicators.
These indicators show the count of agent sessions in each non-idle state, using small colored icons:
| State | Icon | Color | Animated |
|---|---|---|---|
| Working | Spinning circle | Orange | Yes |
| Directing | Interacting circle | Blue | No |
| Waiting | Hollow circle | Grey (amber when prompting) | No |
| Running | Play icon | Blue | No |
| Completed | Checkmark circle | Green | No |
Each indicator is rendered as an icon followed by a count (e.g. a spinning circle and "2" for two working sessions). Hovering the indicator group shows a tooltip with the full summary, such as "3 sessions: 2 working, 1 waiting". Idle sessions are never shown in the collapsed view.
If all sessions are idle or there are no active sessions, no indicators appear and the collapsed card shows just the branch name. For full definitions of each session state, see AI Agents.
Primary Worktree Card
The main worktree card (typically your main, master, or develop branch) uses an enhanced two-row header layout that gives you a project-level overview at a glance.
The top row shows the project name (not the branch name), a sprout icon, a collapse button, and the actions menu. The second row (visible when expanded) shows the branch label, an upstream sync indicator, and an aggregate summary of all secondary worktrees.
Upstream sync
When your main branch is ahead of or behind the upstream remote, the second row shows commit counts: ↑N in green for commits ahead and ↓N in amber for commits behind. Hovering shows the full text (e.g. "3 commits ahead, 1 commit behind upstream").
Aggregate summary
When your project has secondary worktrees, the second row also shows a compact summary: a branch icon with the total worktree count, followed by colored state counts for working and waiting worktrees. The finished count only appears when there are no working or waiting worktrees. This prevents a mixed signal where finished worktrees might distract from active work that still needs attention.
Card body
When expanded, the primary worktree card body shows two summary rows:
- Worktree and agent counts with the same aggregate breakdown as the header (worktree count, working, waiting, finished)
- GitHub health pulse showing CI status, open PR count, and open issue count. This row only appears when GitHub health data is available, which requires a configured GitHub token. See GitHub Integration for token setup.
Search and Filters
At the top of the worktree sidebar, a unified search bar lets you find worktrees by name or branch. Type to search with a 200ms debounce, so the list filters as you type without flickering on every keystroke.
Press Escape to clear the search text and any active filters in one action. If the search is already clear, pressing Escape again blurs the input. The X button at the right edge of the search bar also clears both text and filters.
Advanced filter popover
The filter icon button inside the search bar opens a popover with detailed filter and sort options. When any non-search filter is active, a small green dot badge appears on the filter icon.
| Category | Options |
|---|---|
| Sort by | Date created, Recently updated, Alphabetical, Custom order |
| Group by type | Toggle on/off (groups worktrees by branch type prefix) |
| Status | Active, Dirty (uncommitted changes), Stale, Idle |
| Branch Type | Feature, Bugfix, Refactor, Chore, Docs, Test, Release, CI, Deps, Perf, Style, WIP, Main, Detached, Other |
| GitHub | Has Issue, Has PR, PR Open, PR Merged, PR Closed |
| Sessions | Has Terminals, Working, Running, Waiting, Completed |
| Activity | Last 15 min, 1 h, 24 h, 7 days |
The "Custom order" sort option is hidden when "Group by type" is enabled. A "Clear all filters" button appears at the bottom of the popover when any filter is active.
Visual Hierarchy
When you have many worktrees open, Canopy dims idle and stale cards to keep active work visually prominent. This muting reduces the visual weight of cards that do not currently need your attention.
A card is muted when it is idle or stale, has no waiting agents, is not the currently selected worktree, and is not being hovered or focused. Muted cards use a dimmer text color for branch names and other labels.
The key exception: any card with a waiting agent is never muted, even if the worktree is otherwise idle or stale. Waiting demands your attention, so these cards always render at full visual weight. The currently selected card and any card you hover over are also never muted.