ofsht
A command-line tool for managing Git worktrees with automation features.
Demo

Demonstrated features:
- Creating worktrees with the
addcommand - Listing all worktrees with
ls - Configuring and executing hooks for automation
- Removing multiple worktrees with
rm
Table of Contents
Features
🌳 Worktree Management
- Create worktrees from branches, tags, or commits
- List all worktrees with status information
- Navigate to worktrees by branch name
- Remove worktrees with automatic branch cleanup
- Interactive worktree selection with fzf integration
⚙️ Automation & Hooks
- Run commands after worktree creation (e.g.,
npm install) - Copy files from main repository (e.g.,
.env,.nvmrc) - Create symlinks for shared directories (e.g.,
.vscode) - Execute cleanup commands before worktree deletion
- Customize worktree paths with
{repo}and{branch}variables
💻 Shell Integration
- Automatic directory changing for
addandcdcommands - Shell completion for Bash, Zsh, and Fish
- Automatic zoxide integration for quick navigation
📝 Configuration
- Local configuration (
.ofsht.tomlin project root) - Global configuration (
~/.config/ofsht/config.toml) - XDG Base Directory specification support
Installation
From crates.io
From Homebrew
Using mise
If you're using mise for development tool management:
# Install latest version
# Or install specific version
# Or add to mise.toml
This method works via mise's ubi backend, which automatically:
- Detects your platform and downloads the appropriate binary
- Manages versions alongside your other development tools
- Works without requiring ofsht to be in the mise registry
Supported platforms: Linux (x86_64), macOS (Intel/Apple Silicon)
From Binary Releases
Download pre-built binaries from the releases page.
# macOS (Apple Silicon)
|
# macOS (Intel)
|
# Linux (x86_64)
|
From Source
Requires Rust 1.70+:
Quick Start
# Create a new worktree and navigate to it (with shell integration)
# Create a worktree based on a specific branch
# Create from a remote branch
# Create from a tag
# List all worktrees
# Navigate to a worktree (with shell integration)
# Remove a worktree
[!TIP] The
addcommand automatically navigates to the new worktree when shell integration is enabled. Usecreateinstead if you want to stay in the current directory. See Shell Integration for setup.
Usage
Basic Operations
Create a Worktree
ofsht provides two commands for creating worktrees:
add: Creates a worktree and prints its path to stdout (for shell integration to navigate automatically)create: Creates a worktree but stays in the current directory (no path output)
# Create and navigate to worktree (with shell integration)
# /absolute/path/to/worktrees/my-project/feature-awesome
# Create a new worktree (stays in current directory)
# Created worktree at: ../worktrees/my-project/feature-awesome
# Create from a specific start point
# Create with tmux integration (requires running inside tmux)
List Worktrees
# List all worktrees
# /path/to/my-project d070cdf [main]
# /path/to/worktrees/my-project/feature-awesome d070cdf [feature-awesome]
# Show only paths (useful for scripting)
# /path/to/my-project
# /path/to/worktrees/my-project/feature-awesome
Navigate to a Worktree
# With shell integration (automatic cd)
# Without shell integration
# Interactive selection with fzf (when no branch name provided)
Remove a Worktree
# Remove by path
# Remove by branch name
# Remove current worktree
# Remove multiple worktrees
# Interactive selection with fzf (when no target provided)
# Remove prunable worktrees (those whose directories have been manually deleted)
# Works with branch names, absolute paths, or relative paths
[!NOTE]
ofsht rmcan remove worktrees even if their directories have been manually deleted. Git marks such worktrees as "prunable" (still registered in Git but directory missing), andofshthandles them gracefully by cleaning up the Git registration.
Shell Integration
ofsht provides shell integration for automatic directory changing with cd and add commands.
Setup (one-time):
# Bash - Add to ~/.bashrc
# Zsh - Add to ~/.zshrc
# Fish - Add to ~/.config/fish/config.fish
|
After setup, reload your shell configuration:
Usage (after shell integration):
# Create and automatically navigate to the new worktree
# You're now in the new worktree directory!
# Navigate to an existing worktree
# You're now in the another-feature worktree!
# Remove current worktree and return to main repository
# You're now back in the main repository!
# If you want to create without navigation, use:
How it works: The shell-init command generates a wrapper function that intercepts cd, add, and rm subcommands, automatically executing cd after the operation completes.
[!CAUTION] The shell wrapper only works in interactive shells. You must add the
eval "$(ofsht shell-init <shell>)"line to your shell's rc file (~/.bashrc,~/.zshrc, or~/.config/fish/config.fish) and reload it. The wrapper will not work in non-interactive scripts.
Configuration
ofsht uses a two-tier configuration system with clear inheritance rules:
Configuration Priority:
- Local config (
.ofsht.tomlin project root): Only[worktree]and[hooks.*]settings - Global config (
~/.config/ofsht/config.toml): All settings, including integrations - Built-in defaults: Fallback values if no config files exist
[!IMPORTANT] Integration settings (
[integration.zoxide],[integration.fzf],[integration.tmux],[integration.gh]) are ONLY read from global config, never from local config. Placing them in.ofsht.tomlwill silently have no effect.
Local Configuration
Create .ofsht.toml in your project root:
[]
# Customize worktree directory template
= "../worktrees/{repo}/{branch}"
[]
# Run commands after worktree creation
= ["pnpm install", "echo Setup complete!"]
# Copy files from main repository
= [".env", ".nvmrc"]
# Create symlinks (source in repo -> destination in worktree)
= { = ".vscode" }
[]
# Run commands before worktree deletion
= ["echo Cleaning up..."]
Generating Configuration Files
Use ofsht init to generate configuration files with default settings:
# Generate both global and local config files
# Generate only global config
# Generate only local config (in current project)
# Overwrite existing config files
[!WARNING]
ofsht init --forcewill overwrite both global and local config files if they exist. This will destroy any existing hook configurations or custom settings. Use with caution.
Global Configuration
Create ~/.config/ofsht/config.toml for global settings (or use ofsht init --global):
[]
# Default directory template for all projects
= "../{repo}-worktrees/{branch}"
[]
# Enable/disable zoxide integration
= true # Default: true
[]
# Enable/disable fzf integration
= true # Default: true
# Additional fzf command-line options
= ["--height=50%", "--border"]
[]
# Configure tmux integration behavior
= "auto" # "auto" (default, flag-based), "always", "never"
= "window" # "window" (default) or "pane"
[]
# Enable/disable GitHub integration
= true # Default: true
GitHub Integration
When the gh CLI is installed and authenticated, you can create worktrees directly from GitHub issues or pull requests:
# Create worktree from GitHub issue
# Creates a worktree with branch name from the issue
# Create worktree from GitHub pull request
# Creates a worktree from the PR's head branch
[!IMPORTANT] Requirements for GitHub Integration:
ghCLI must be installed (https://cli.github.com/)- You must be authenticated with GitHub (run
gh auth login)- Command must be run inside a repository connected to GitHub
Without these requirements,
ofsht add #123will fail with "gh: command not found" or authentication errors.
The GitHub integration automatically:
- Fetches issue/PR information using
ghCLI - Generates appropriate branch names
- Creates the worktree with the correct base branch
To disable GitHub integration, set enabled = false in global config:
[]
= false
zoxide Integration
When zoxide is installed and enabled (default), created worktrees are automatically registered with zoxide for quick navigation:
# After creating a worktree
# or
# You can now jump to it using zoxide
# Disable zoxide integration in global config (~/.config/ofsht/config.toml)
tmux Integration
ofsht can automatically create tmux windows or panes when creating worktrees.
Configuration Options:
# In ~/.config/ofsht/config.toml
[]
= "auto" # or "always", "never"
= "window" # or "pane"
-
behavior:"auto"(default): Only use tmux when--tmuxflag is specified"always": Automatically create tmux window/pane for every worktree"never": Disable tmux integration entirely
-
create:"window"(default): Create a new tmux window"pane": Split current window horizontally
CLI Overrides:
# Temporarily enable tmux (overrides config)
# Temporarily disable tmux (overrides config, even with behavior="always")
# With behavior="always" in config, tmux is used by default
[!CAUTION] tmux integration requires running
ofsht addinside an active tmux session. Using--tmuxflag orbehavior = "always"outside of tmux will fail with an error. Remote sessions via SSH must preserve the$TMUXenvironment variable.
Window/Pane Behavior:
- New windows are automatically focused and set to the worktree directory
- Window names are sanitized from branch names (e.g.,
"feature/login"→"feature·login") - Panes are created with horizontal split in the current window
Requirements:
- Must be running inside a tmux session when tmux integration is active
- tmux binary must be available in PATH
Shell Completion
ofsht provides dynamic shell completion with smart branch and worktree name suggestions.
To see setup instructions for your shell:
# Display setup instructions
Quick setup:
# Bash - Add to ~/.bashrc
# Zsh - Add to ~/.zshrc
# Fish - Add to ~/.config/fish/config.fish
| )
After setup, you'll get intelligent completions:
ofsht add feature <TAB>- When specifying a start point, lists branches, remote refs, and tagsofsht rm <TAB>- Lists worktree names (and flags, following standard CLI conventions)ofsht cd <TAB>- Lists worktree names
Common Workflows
Working on Multiple Features Simultaneously
# Create worktrees for different features
# Switch between them easily
# Or use zoxide for quick navigation
Code Review Workflow with GitHub PR Integration
# Review a pull request
# Review code, run tests, etc.
# Work on an issue
# Implement the feature
Maintenance Cycle: Create → Test → Cleanup
# Create a new feature branch
# Run setup hooks automatically (defined in .ofsht.toml)
# - npm install / pnpm install
# - Copy .env and other config files
# - Link shared directories
# Do your work, test, commit
# Return to main and clean up
# Or clean up from within the worktree
Managing Multiple Projects
# Configure global worktree directory template
# in ~/.config/ofsht/config.toml:
# [worktree]
# dir = "~/worktrees/{repo}/{branch}"
# Now all projects use consistent structure:
# ~/worktrees/project-a/feature-x
# ~/worktrees/project-b/feature-y
# ~/worktrees/project-c/bugfix-z
FAQ
What's the difference between add and create?
add: Creates a worktree and prints its path to stdout, enabling shell integration to automatically navigate to itcreate: Creates a worktree but stays in the current directory, useful for scripting or when you don't want to navigate
With shell integration enabled (eval "$(ofsht shell-init bash)"), add will automatically cd to the new worktree.
Do I need to install zoxide, fzf, or tmux?
No, all integrations are optional:
- zoxide: If not installed, worktrees are created normally without zoxide registration
- fzf: If not installed, interactive selection commands (
ofsht cd/ofsht rmwithout arguments) will show an error - tmux: Only required if you use the
--tmuxflag or setbehavior = "always"in config
[!TIP] All integrations gracefully degrade if the tools are not available. You can install them later and they'll automatically work without any configuration changes. Start using
ofshtright away and add integrations as needed.
Can I use ofsht with GitHub Desktop or other Git GUIs?
Yes! ofsht uses standard git worktree commands under the hood, so all worktrees are visible to Git GUIs. However, features like hooks and integrations are specific to the ofsht CLI.
What happens if I manually delete a worktree directory?
Git marks manually deleted worktrees as "prunable". You can still remove them with ofsht rm <branch-name>, which will clean up the Git registration. The deletion hooks won't run since the directory is already gone.
Why use ofsht instead of plain git worktree?
ofsht enhances git worktree with:
- Automation: Hooks for running commands, copying files, and creating symlinks
- Better UX: Interactive selection with fzf, shell integration for auto-navigation
- Integrations: Automatic zoxide registration, tmux window creation, GitHub issue/PR support
- Configuration: Project-specific and global settings for consistent workflows
Think of it as git worktree with quality-of-life improvements for daily development workflows.
How do I migrate from using plain git worktree?
Your existing worktrees created with git worktree add are fully compatible with ofsht:
ofsht lswill show all worktreesofsht cd <branch>can navigate to existing worktreesofsht rm <branch>can remove worktrees created withgit worktree add
Simply start using ofsht add for new worktrees to take advantage of hooks and integrations.
Documentation
- CONTRIBUTING.md - Development guide and architecture details
- CHANGELOG.md - Version history
- TEST.md - Manual testing procedures