🌳 A fast, modern CLI tool for managing Git worktrees in bare repositories
Worktree Manager simplifies Git worktree operations, making it easy to work with multiple branches simultaneously in bare repositories. Perfect for CI/CD environments, shared development servers, or anyone who wants to streamline their Git workflow.
✨ Features
- Lightning fast - Built with Bun for maximum performance
- Easy setup - Clone or adopt any repo into a wtm-managed bare structure with one command
- Bare repository focused - Designed specifically for bare Git repositories
- Smart branch management - Automatic fetching and branch creation
- Automatic cleanup - Detect and remove merged worktrees safely
- Hook system - Extensible post-creation hooks for automation
- Clear output - Beautiful, informative command output
- Safe operations - Comprehensive validation and error handling
📦 Installation
Prerequisites
- Bun runtime (v1.0+)
- Git installed and configured
Install from npm
# Install globally with your preferred package manager bun install -g @jx0/wtm # or pnpm add -g @jx0/wtm # or npm install -g @jx0/wtm # or yarn global add @jx0/wtm # Verify installation wtm help
Development Setup
For contributors who want to work on the source code:
# Clone the repository git clone https://github.com/your-username/worktree-manager.git cd worktree-manager # Install dependencies bun install # Link for local development bun link # For development with auto-reload bun run dev
🚀 Quick Start
# Clone a repository into a wtm-managed bare structure wtm init git@github.com:user/myrepo.git # Or adopt an existing repo you've already cloned cd ~/projects/existing-repo wtm init # Either way, you end up with: # myrepo/ # ├── .git/ <- bare Git internals # └── main/ <- worktree with your code # Start working in the worktree cd myrepo/main # Create a new feature worktree wtm create feature-auth --from main # Work on your feature in the new shell... # (edit files, make commits, etc.) # Exit the shell when done exit # Back in the bare repository, list all worktrees wtm list # Clean up merged worktrees wtm cleanup
📖 Command Reference
wtm init <url> [path] — Clone
Clones a repository into a wtm-managed bare repository structure.
# Clone using repo name as directory wtm init git@github.com:user/myrepo.git # Clone with custom directory name wtm init git@gitlab.com:org/platform.git myproject
What it does:
- Extracts repository name from URL (or uses provided path)
- Creates directory with
.git/subdirectory for bare Git data - Clones the repository as a bare clone
- Configures fetch refspec for all branches
- Detects the default branch (from origin/HEAD, or main/master)
- Creates an initial worktree for the default branch
Supported URL formats:
git@github.com:user/repo.git(SSH)https://github.com/user/repo.git(HTTPS)ssh://git@gitlab.com/org/repo.git(SSH with protocol)
wtm init [path] — Adopt
Converts an existing git repository into wtm's bare-repo-with-worktrees structure, in place.
# Adopt the repo you're currently in cd ~/projects/myrepo wtm init # Or point at a repo by path wtm init ~/projects/myrepo
What it does:
- Validates the repo (clean working tree, has remote, not already bare, no detached HEAD)
- Moves all files to a temporary directory
- Converts
.git/to bare mode - Creates a worktree named after your current branch (e.g., on
main→myrepo/main/) - Restores gitignored and untracked files into the worktree
- If anything fails, automatically reverts to the original state
Preserves: gitignored files (.env, node_modules), untracked files, and local unpushed commits.
Requires: clean working tree — commit or stash uncommitted changes first.
Final structure (both clone and adopt):
myrepo/
├── .git/ <- bare Git internals
└── main/ <- worktree (named after your checked-out branch)
wtm create <name> --from <base_branch>
Creates a new worktree with a new branch based on the specified base branch, then spawns a new shell in that worktree.
# Create worktree from main branch wtm create feature-auth --from main # Create hotfix from master wtm create hotfix-123 --from master # Create feature branch from another branch wtm create review-pr --from feature-x
What it does:
- Validates you're in a bare repository
- Fetches the latest changes from the base branch
- Creates a new branch named
<name> - Creates a worktree directory at
./<name> - Executes the
.wtm/post_createhook if present in the new worktree - Spawns a new shell session in the worktree directory
Important: This command starts a new shell in the worktree. When you're done working, use exit to return to your original shell in the bare repository.
wtm checkout <name>
Creates a worktree from an existing remote branch if it doesn't already exist locally.
# Create worktree from existing remote branch
wtm checkout existing-remote-branchBehavior:
- If worktree already exists: displays a success message (but does NOT change your shell's directory)
- If worktree doesn't exist but remote branch exists: creates worktree from the remote branch
- If neither exists: shows available worktrees and creation instructions
Important Note: Due to how shells work, this command cannot change your current shell's directory. To work in a worktree:
- Use
wtm createwhich spawns a new shell in the worktree, OR - Manually
cdinto the worktree directory after creation
This command is primarily useful for creating worktrees from existing remote branches without specifying a base branch.
wtm list
Displays all worktrees in a formatted table.
Output:
Worktrees:
────────────────────────────────────────────────────────────────────────────────
feature-auth [feature-auth] a1b2c3d4e
main-work [main] f5g6h7i8j
(bare) (bare) k9l0m1n2o
wtm delete <name> [--force]
Removes a worktree and its associated files.
# Safe deletion (with confirmation if needed) wtm delete feature-auth # Force deletion (skips safety checks) wtm delete old-feature --force
Safety features:
- Cannot delete bare repository
- Validates worktree exists before deletion
- Force flag available for stuck worktrees
wtm cleanup [options]
Interactively find and delete worktrees whose branches have been merged upstream.
# Interactive mode - select which merged worktrees to delete wtm cleanup # Specify base branch for merge detection wtm cleanup --base main # Preview what would be deleted without actually deleting wtm cleanup --dry-run # Delete all merged worktrees without prompting wtm cleanup --yes
Options:
| Flag | Description |
|---|---|
--base <branch> |
Base branch for merge detection (auto-detected from origin/HEAD by default) |
--dry-run |
Show what would be deleted without deleting |
--yes |
Delete all merged worktrees without interactive prompts |
A worktree is considered safe to delete when ALL of these are true:
- Merged upstream - Branch commit is an ancestor of the base branch, OR the remote branch has been deleted (typically after a merge request is merged)
- No uncommitted changes - Working tree has no unstaged, staged, or untracked files
- No unpushed commits - No local commits that aren't reachable from the base branch
Protected branches (never suggested for cleanup):
main,master,next,prerelease
Example workflow:
$ wtm cleanup Using base branch: main Fetching latest from origin/main... Scanning worktrees for cleanup candidates... Found 2 worktree(s) safe to clean up: - feature-auth [feature-auth] - bugfix-123 [bugfix-123] ? Select worktrees to delete: [x] feature-auth [feature-auth] [ ] bugfix-123 [bugfix-123] ? Delete 1 worktree(s)? Yes Deleted worktree 'feature-auth' at /path/to/feature-auth Pruned stale worktree references. Cleanup complete. Deleted 1 worktree(s).
wtm help
Shows comprehensive help information including examples and features.
🪝 Hook System
Worktree Manager runs hook scripts at lifecycle events. Hooks live inside the worktree under .wtm/, so they can be committed to git and shared with the rest of the team — or .gitignored for personal-only setup. Hooks are run with bash; the executable bit is not required.
Discovery
For each hook, wtm looks at exactly one location:
<worktree>/.wtm/<hook-name>
If the file exists, it runs. If not, the hook is a silent no-op. Because hooks are checked out with the rest of the branch's tree, the hook that runs for a new worktree is the version committed on its base branch.
Available Hooks
post_create
Runs immediately after a worktree is created (via wtm create or wtm checkout), with the working directory set to the new worktree.
Environment Variables:
$WORKTREE_DIR— Absolute path to the new worktree$WORKTREE_NAME— Name of the worktree$BASE_BRANCH— Branch the worktree was created from$BARE_REPO_PATH— Path to the bare repository
Example .wtm/post_create:
#!/bin/bash # Committed at .wtm/post_create on your base branch echo "🪝 Setting up new worktree: $WORKTREE_NAME" # Install dependencies if package.json exists if [ -f "package.json" ]; then echo "📦 Installing dependencies..." pnpm install --silent fi # Copy environment files from bare repo if [ -f "$BARE_REPO_PATH/.env.example" ]; then echo "📄 Copying .env.example to .env" cp "$BARE_REPO_PATH/.env.example" ".env" fi echo "✅ Worktree setup complete!"
Setup (shared, team hook):
# In a worktree on the branch you want to share the hook from (e.g. main) mkdir -p .wtm $EDITOR .wtm/post_create git add .wtm/post_create git commit -m "chore: add wtm post_create hook" git push # Test by creating a new worktree from that branch wtm create test-feature --from main
Setup (personal, untracked hook):
If you want a hook that only runs on your machine, add .wtm/post_create to .git/info/exclude (which is per-clone and not committed) before creating the file. wtm will still discover and run it — git just won't track it. Avoid putting .wtm/ in the repo's .gitignore: that would block teammates from sharing hooks via the same path.
Security
Because .wtm/post_create is committed to git, anyone with merge access to a branch you check out can run arbitrary code on your machine the moment you wtm create or wtm checkout from it. This is the same trust model as npm install post-install scripts and any other build scripts in the repo. Treat hook changes with the same care as any other code review.
Migrating from earlier versions
Earlier versions of wtm looked for hooks at the bare repository root (e.g. <bareRoot>/post_create). That location is no longer consulted. To migrate:
- Move your existing hook to
.wtm/post_createon the branch(es) you want it to run on (commonly your default branch, so new worktrees inherit it). - Commit and push.
- Delete the old
<bareRoot>/post_createfile — it is now ignored by wtm and serves no purpose.
🏗️ Architecture
worktree-manager/
├── src/
│ ├── cli.ts # Command parsing and routing
│ ├── init.ts # Repository initialization
│ ├── worktree.ts # Core worktree operations
│ ├── cleanup.ts # Cleanup detection and UI
│ └── hooks.ts # Hook execution system
├── index.ts # Main entry point
├── package.json # Project configuration
└── README.md # Documentation
Key Components:
- InitManager: Clones or adopts repos into wtm-managed bare structure
- WorktreeManager: Core class handling Git operations
- CleanupManager: Detects merged worktrees and handles cleanup flow
- HookManager: Executes lifecycle hooks with proper environment
- CLI Parser: Robust argument parsing and command routing
🔧 Configuration
Worktree Manager works out of the box with standard bare repositories. No configuration files needed.
Repository Requirements:
# Must be a bare repository git config core.bare true # Should have remote configured git remote -v # origin git@github.com:user/repo.git (fetch) # origin git@github.com:user/repo.git (push)
🎯 Use Cases
Development Server Workflows
Perfect for shared development servers where multiple developers work on different features:
# Developer 1 wtm create user-authentication --from main # Developer 2 wtm create payment-integration --from main # Code review wtm create review-pr-123 --from feature-branch
CI/CD Environments
For automated build systems, you can use git worktree commands directly and use wtm for cleanup:
# Build script git worktree add -b build-$BUILD_ID build-$BUILD_ID origin/$BRANCH_NAME cd build-$BUILD_ID # ... run build process ... cd .. wtm delete build-$BUILD_ID --force
Note: wtm create spawns an interactive shell, so it's not suitable for automated scripts. Use git's native worktree add command for CI/CD, and wtm delete for cleanup.
Feature Development
Streamline your feature development workflow:
# Start new feature (spawns new shell in worktree) wtm create feature-dashboard --from main # Work on feature in the new shell... # (commits, testing, etc.) # Exit when done exit # Back in bare repo, work on another feature if needed wtm create hotfix-urgent --from main # Work on hotfix... exit # Clean up when done wtm delete feature-dashboard wtm delete hotfix-urgent
Periodic Cleanup
Keep your bare repository tidy by periodically cleaning up merged worktrees:
# See what can be cleaned up wtm cleanup --dry-run # Interactively select and delete merged worktrees wtm cleanup # Or automatically clean up all merged worktrees (useful in scripts) wtm cleanup --yes
This is especially useful when working with merge request workflows - after your MRs are merged and the remote branches are deleted, wtm cleanup will detect these and offer to remove the local worktrees.
🤝 Contributing
We welcome contributions! Please see our Contributing Guidelines for details.
Development
# Clone and setup git clone https://github.com/your-username/worktree-manager.git cd worktree-manager bun install # Run in development mode bun run dev # Build bun run build # Run tests (when available) bun test
Project Structure
- Keep core logic in
src/worktree.ts - Add new commands in
src/cli.ts - Hook system extensions go in
src/hooks.ts - Follow existing TypeScript patterns
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.
🔗 Links
Made with ❤️ and Bun
Worktree Manager - Because managing Git worktrees shouldn't be a tree of problems 🌳