Git Worktrees in Tempest
Git worktrees are Tempest’s foundation for isolated, parallel development. Each workspace operates in its own git worktree, allowing you to work on multiple branches simultaneously without stashing, switching contexts, or losing uncommitted changes. This page explains exactly how Tempest implements worktrees and manages the supporting infrastructure.What Are Git Worktrees and Why Tempest Uses Them
A git worktree is a git feature that lets you check out multiple branches into separate directories simultaneously. Unlike traditional branch switching, which requires the working tree to be clean (committing or stashing uncommitted work), worktrees bypass this friction entirely. Tempest uses worktrees for three reasons:- Isolation: Each agent workspace is a separate branch checked out into its own directory. The agent can work freely without affecting your main branch or other open workspaces.
- No stashing required: When you open a workspace on branch A, work on branch B in a second workspace, then return to the first, your uncommitted changes on branch A are still there. No git stash pop needed.
- Parallel work: Run multiple agents simultaneously on different branches. Each operates in its own worktree and can commit, push, and create pull requests independently.
Worktree Creation: The Complete Flow
When you create a workspace in Tempest, a series of git and file system operations run. Here is the exact flow implemented increate_terminal_worktree:
Step 1: Ensure At Least One Commit Exists
Step 2: Prune Stale Worktree Metadata
.git/worktrees/<name> references left behind by previous failed or incomplete worktree removals. Without this, a previous crash could block worktree creation.
Step 3: Check for Pre-Existing Paths
If a directory already exists at the target path (.tempest/<name>), Tempest checks whether it is registered as a git worktree:
Step 4: Create the Worktree
- Creates a new branch named
<name> - Checks it out into
.tempest/<name>/ - Registers it in
.git/worktrees/<name>/
.git directory via .git file (not a directory).
Step 5: Exclude the .tempest-pid Sidecar
After creation, Tempest writes a file to the worktree’s git metadata to exclude a process ID sidecar:
info/exclude file is git’s local-only equivalent to .gitignore. Entries here never enter version control and are not shared. This prevents the .tempest-pid file (which tracks the PTY session) from showing up as an untracked file in git status.
Step 6: Copy Gitignored-but-Needed Files
Files listed in.tempest entries in the project root’s .gitignore are copied into the worktree so the agent can access them without recommitting them:
Step 7: Link Large Dependency Directories
Rather than copyingnode_modules or .venv (which can be 200-800 MB and tens of thousands of files), Tempest creates links:
On Windows:
Step 8: Validate the Worktree Is Not Empty
.git directory and nothing else, Tempest returns an error:
The .tempest/ Directory Layout
After a successful worktree creation, the directory structure looks like this:
.git file inside .tempest/workspace-name/ is a text file containing a gitdir reference:
Branches and Worktrees: One-to-One Mapping
Tempest creates one worktree per workspace, and each worktree has exactly one associated branch. The mapping is:- Worktree name = Branch name
- Both are user-provided when creating the workspace
- The branch is created at worktree creation time (via
git worktree add -b <name>) - If you delete the worktree, the branch remains (you must delete it separately via git UI or the Diff pane)
auth-feature creates:
- A git branch
refs/heads/auth-feature - A worktree at
.tempest/auth-feature/ - Both map to the same workspace session
Symlinks and Junctions: Platform-Specific Linking Strategy
Tempest linksnode_modules and .venv differently on Windows vs Unix to match platform conventions:
Windows: Directory Junctions
On Windows, Tempest uses thejunction crate to create directory junctions:
std::fs::remove_dir(&link) which strips the reparse point without following it into the real directory.
Unix/macOS: Symbolic Links
On Unix, Tempest uses standard symbolic links:rm symlink, which deletes the link without touching the target.
Why Both Exist
Windows junctions and Unix symlinks have different semantics:- Junctions: Transparent to most tools, no special permissions, atomic remove.
- Symlinks: Transparent on modern systems, but older Windows tools may not support them.
Worktree Removal: Cleanup Order
Removing a worktree is not justrm -rf. Multiple steps ensure directory locks are released and git metadata is cleaned up:
Step 1: Remove Directory Links First
Before any directory deletion:remove_dir_all, the OS will traverse into the target and delete the real dependency directory, corrupting the project.
Step 2: Remove the Worktree via Git
.git/worktrees/.
Step 3: Retry with Retries (Windows Fallback)
Ifgit worktree remove fails (common on Windows due to PTY process handle lag), Tempest retries direct removal up to 6 times with 500 ms gaps:
Step 4: Last Resort: Shell Command (Windows Only)
If Rust’sfs::remove_dir_all fails after retries with OS error 32 (handle still held), fall back to the shell:
Step 5: Prune Dangling Git Metadata
.git/worktrees/<name> references left behind if the above steps partially succeeded.
Edge Cases and Error Handling
Uncommitted Changes
When you open a workspace on branch A, make changes, then open a second workspace on branch B, the changes on branch A are preserved in the worktree. They do not affect branch B because each worktree has its own working tree. If you try to delete a worktree with uncommitted changes, Tempest uses--force to ignore them:
Detached HEAD State
Worktrees are always on a branch created at creation time. If you manually check out a commit (detaching HEAD), git operations still work; you can commit to the detached HEAD, then switch back to the branch or create a new branch. Tempest does not prevent this, but the workspace name will refer to the original branch while HEAD is detached. Switching branches via the Diff pane branch menu will reattach to the named branch.Missing Parent Commit
If you delete the parent branch that the worktree branched from, the worktree remains valid. The branch still exists and points to the same commit; git can still track history. However, branch-switching and merging operations may behave unexpectedly. This is an advanced edge case and Tempest does not guard against it.Repository With No Commits Yet
If the repository has never been committed to, Tempest creates an initial commit before creating the worktree. This happens silently during workspace creation and ensures git operations work.Git Hooks: Co-Author Attribution
When a workspace is created, Tempest installs a git hook to automatically add a co-author line to commits. This identifies work done by Tempest and its agent.Hook Installation
The hook is written to the worktree’s local hooks directory:write_coauthor_hook):
Hook Scope
The hook is installed in the worktree’s.git/hooks/ directory, so it only affects commits in that worktree. The main project is not affected. Different worktrees can have different hooks.
Disabling the Hook
Users can disable the co-author line in the Diff pane via a checkbox. When unchecked, the co-author line is not added to the commit message.Hook Removal
When a workspace is closed, the hook is removed viaremove_coauthor_hook:
Summary
Git worktrees in Tempest provide isolated, stash-free parallel development. Each workspace is a separate branch checked out into.tempest/<name>/, with node_modules and .venv linked for performance. Worktree creation validates repository state, copies environment files, and installs a co-author hook. Removal cleans up links, git metadata, and handles Windows process handle lag with retries and fallbacks. The whole system is transparent to the user; the Diff pane and RightSidebar provide a high-level view of branches, changes, and commits.