Switch between branches like a pro - your Git branch switching yard
A powerful command-line tool for managing Git worktrees with intelligent GitHub Pull Request integration. Just like railroad turnouts route trains between tracks, Git Turnouts helps you seamlessly switch between multiple branches and work contexts.
Currently Supported Platforms: macOS, Linux (Unix-like systems)
In railroad terminology, a turnout (also called a "switch" or "point") is a mechanical installation that guides trains from one track to another. This perfectly mirrors what Git Turnouts does - it helps you smoothly switch between different development tracks (branches) without the friction of stashing, committing unfinished work, or losing context.
Key metaphor parallels:
- π€οΈ Multiple tracks = Multiple branches
- π Switching trains = Switching work contexts
- π Railroad junction = Git worktree workspace
- π Track routing = Branch management
- PR-Aware Worktree Creation: Automatically detect and checkout GitHub PRs by number or title
- Smart Branch Resolution: Intelligently handles local, remote, and new branches
- Organized Workspace: Creates worktrees in a structured hierarchy
- Automatic Opening: Open worktrees in your IDE (IntelliJ IDEA, VS Code) or other applications (iTerm, Warp, Finder)
- Bulk Removal: Remove multiple worktrees efficiently in a single command
- Protected Branches: Automatically protects main/master branches from deletion
- Safety Checks: Prevents branch conflicts and duplicate worktrees
- Progress Tracking: Shows detailed progress and summary statistics
- Unix-like OS (macOS, Linux)
- Git (2.5 or newer, with worktree support)
- Bash (3.2+)
- jq (for JSON parsing) - Install jq
- GitHub CLI (gh) (optional, for PR integration features) - Install gh
Platform Notes:
- Core features (worktree management, PR integration): Fully supported on macOS and Linux
- Automatic opening (
--openflag): Currently uses macOS-specific commands. On Linux, worktrees are created successfully but automatic opening in applications is not yet supported.
-
Clone this repository:
git clone https://github.com/andr3van/git-turnouts.git cd git-turnouts -
Make the script executable:
chmod +x git-turnouts
-
Add to your PATH (choose one method):
Option A: Symlink to a directory in your PATH
ln -s "$(pwd)/git-turnouts" /usr/local/bin/git-turnoutsOption B: Add to PATH in your shell profile
# Add to ~/.bashrc, ~/.zshrc, etc. export PATH="$PATH:/path/to/git-turnouts"
-
Verify installation:
git-turnouts --version
Git Turnouts can check which tools are installed and available on your system. This helps ensure all features work correctly and provides guidance for installing missing tools.
# Check all tools (required and optional)
git-turnouts config check
# Show detailed information including paths and purposes
git-turnouts config check --verbose
# Check only required tools
git-turnouts config check --required
# Check only optional tools
git-turnouts config check --optionalThese tools are necessary for core git-turnouts functionality:
- git (2.5+) - Version control and worktree operations
- bash (3.2+) - Script execution
- jq - JSON parsing for configuration and PR data
These tools enable additional features:
- gh - GitHub CLI for PR integration features
- shellcheck - Shell script linting for development
$ git-turnouts config check
π Tool Dependency Status
Required Tools:
Tool Version Purpose
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
git 2.39.0 Version control and worktree management
β
bash 3.2.57(1)-release Script execution (requires 3.2+)
β
jq 1.6 JSON parsing for GitHub PR integration
Optional Tools:
Tool Version Purpose
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
gh 2.40.0 GitHub Pull Request integration
β
shellcheck 0.9.0 Shell script linting for development
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Status: All required tools are installed β
Use --verbose to see full paths in addition to purposes:
$ git-turnouts config check --verbose
π Tool Dependency Status
Required Tools:
Tool Version
βββββββββββββββββββββββββββββββββββββββββββββββββ
β
git 2.39.0
Path: /usr/bin/git
Purpose: Version control and worktree management
β
bash 3.2.57(1)-release
Path: /bin/bash
Purpose: Script execution (requires 3.2+)
β
jq 1.6
Path: /usr/local/bin/jq
Purpose: JSON parsing for GitHub PR integrationIf any required tools are missing, you'll see installation guidance:
# macOS
brew install jq gh
# Linux (Debian/Ubuntu)
sudo apt-get install jq gh
# Linux (RHEL/CentOS)
sudo yum install jq gh# Create worktree with branch name
git-turnouts add feature-branch
# Create worktree with different folder and branch names
git-turnouts add my-folder feature-branch# Checkout PR by number
git-turnouts add 7113
# Search for PR by title (partial match)
git-turnouts add "feature name"
# Search for PR by exact title
git-turnouts add "Exact PR Title"
# Custom folder name with PR title search
git-turnouts add my-folder "PR Title"# Open in your editor (no automatic opening by default)
git-turnouts add feature-x --open code
# Open in a terminal
git-turnouts add feature-x --open iterm
# Use any command/application that accepts a directory path# Remove a single worktree
git-turnouts remove feature-branch
# Remove multiple worktrees (bulk operation)
git-turnouts remove feature-1 feature-2 feature-3
# Short alias
git-turnouts rm feature-branch# List all worktrees
git-turnouts list
# Short alias
git-turnouts lsCheck if worktrees are still tracking active remote branches and clean up stale ones:
# Check all worktrees against remote (safe, read-only)
git-turnouts verify
# Show detailed status for each worktree
git-turnouts verify --verbose
# Preview what would be cleaned up (dry-run)
git-turnouts verify --clean --dry-run
# Clean up stale worktrees (with confirmation)
git-turnouts verify --clean
# Clean up without confirmation
git-turnouts verify --clean --yesThe verify command helps you:
- Identify worktrees whose remote branches have been deleted
- Clean up stale worktrees after PRs are merged
- Keep your workspace organized and up-to-date
- Warn about unpushed commits before removal
- Check for uncommitted changes
- Respect protected branches (completely skip removal for protected worktrees)
- Show protection status in verbose mode with π‘οΈ indicator
Protected Branches in Verify:
The verify command clearly indicates protected branches in verbose mode:
- Active protected branches: Shown as
β branch-name β origin/branch-name exists (PROTECTED) - Stale protected branches: Shown as
π‘οΈ branch-name β branch deleted from remote (PROTECTED - will not be removed)
When cleaning up stale worktrees with verify --clean:
- Protected branches are shown in a separate "Protected branches (will be skipped)" section
- Completely skip removal (both worktree and branch are preserved)
- Include protected stale branches in the "Protected (stale)" count in the summary
This ensures important branches like develop or staging are never accidentally removed and are always clearly identified in the output.
Worktrees are organized in a clean hierarchy:
~/projects/
βββ my-project/ # Main repository
βββ worktree/
βββ my-project/ # Project-specific worktrees
βββ feature-1/
βββ feature-2/
βββ 7113/ # PR-based worktree
-
PR Number: If input is numeric (e.g.,
7113), directly fetches that PR- Checks PR state (open/closed/merged)
- Merged PRs: Blocks creation (branch likely deleted)
- Closed PRs: Allows creation with warning (branch may still exist)
- Open PRs: Proceeds normally
- Uses PR's branch name
- Fetches latest changes from remote
-
PR Title Search: Searches open PRs for matching titles
- Quoted strings = exact match
- Unquoted strings = partial match
- Uses PR's branch if found
-
Standard Branch: Falls back to normal Git branch resolution
- Checks for remote branch first
- Creates new branch from HEAD if needed
- Prevents checking out the same branch in multiple worktrees
- Protects main/master branches from deletion
- Validates target directories don't exist
- Handles merged/closed PRs appropriately
# Create worktrees for different features (one at a time)
git-turnouts add feature-authentication
git-turnouts add feature-dashboard
git-turnouts add feature-api
# Work on them simultaneously in different IDE windows# Quickly checkout PR #7113 for review
git-turnouts add 7113 --open code
# When done reviewing
git-turnouts remove 7113# Remove multiple completed feature branches at once
git-turnouts remove feature-1 feature-2 feature-3 pr-7113
# Progress tracking shows: [1/4], [2/4], [3/4], [4/4]Git Turnouts uses a single YAML configuration file that manages all your projects. The configuration file lives in the git-turnouts installation directory.
-
Create your configuration file:
git-turnouts config init
This creates
.config.ymlin the git-turnouts directory -
Edit your configuration:
# The init command shows you the path to edit vim ~/.../git-turnouts/.config.yml
-
View your current configuration:
git-turnouts config show
This shows the detected project name and effective settings
Your settings will be applied automatically across all projects!
See .config.yml.example for all available options with detailed comments. Here's a quick overview:
# Global settings apply to all projects
global:
# Base directory for all projects
# Project name is automatically added as a subdirectory
base_dir: ~/worktrees
# Files to copy from main worktree to new worktrees
copy_files:
- .editorconfig
- .env.example
- .nvmrc
# Project-specific settings
# List settings (copy_files, protected_branches): ADD TO global
# Scalar settings (base_dir, open_with, auto_prune): OVERRIDE global
# The 'name' must match your repository's directory name exactly
projects:
- name: my-app
base_dir: ~/custom/my-app # Overrides global base_dir
copy_files: # Adds to global copy_files
- .env.local # Combined: .editorconfig, .env.example, .nvmrc, .env.local
- name: another-project
base_dir: /tmp/another-projectHow it works:
- Project name is detected from your repository's directory name (e.g.,
/path/to/my-appβ project name ismy-app) - The project name is always added as a subdirectory for organization
- If a project-specific
base_direxists β use it:{base_dir}/{project}/{branch} - Else if global
base_direxists β use it:{base_dir}/{project}/{branch} - Else β auto-detect:
../worktree/{project}/{branch}
Example: With global.base_dir: ~/worktrees and project my-app:
- Worktrees created at:
~/worktrees/my-app/feature-x
The copy_files feature is one of Git Turnouts' most powerful workflow improvements. It automatically copies essential configuration files from your main worktree to every new worktree you create.
The problem it solves:
Many files are essential to run your application but are NOT in version control (listed in .gitignore):
.envfiles containing personal API keys, credentials, or secrets- Local IDE settings or personalized configurations
- Files with environment-specific values unique to your machine
Without copy_files, you'd need to manually recreate or copy these files for every single worktree - a tedious and error-prone process.
Why this matters:
- No manual setup - Each worktree is instantly ready to work with
- Never forget essential files - Stop worrying about missing
.envfiles or credentials - Consistency across worktrees - All your worktrees use the same local configuration
- Save time - Eliminate the tedious copy-paste routine for every new worktree
Common files to copy:
copy_files:
- .env # Personal environment variables, API keys, secrets (NOT in git)
- .env.local # Local development overrides (NOT in git)
- .editorconfig # Editor settings (indentation, formatting)
- .nvmrc # Node.js version for the project
- .ruby-version # Ruby version manager
- .prettierrc # Code formatting rules
- .eslintrc.js # Linting configuration
- .idea/codeStyles/ # IDE code style settingsReal-world example:
global:
base_dir: ~/worktrees
copy_files:
- .env # Contains your personal database credentials
- .env.local # Your local API keys
- .editorconfig
- .nvmrcWhen you run git-turnouts add feature-auth, it will:
- Create the worktree at
~/worktrees/my-app/feature-auth - Automatically copy
.env,.env.local,.editorconfig, and.nvmrcto the new worktree - Open in your IDE, ready to work immediately - no manual file copying, no missing credentials
Without copy_files: Every time you create a worktree, you must:
- Remember which files to copy
- Manually copy
.envfile with your credentials - Set up local configurations again
- Deal with "Cannot connect to database" errors when you forget
With copy_files: Every worktree is automatically set up with all your personal configurations. Just run the app - it works immediately. π
global:
open_with: codeglobal:
# All projects go to ~/worktrees/{project-name} by default
# (project name is automatically added)
base_dir: ~/worktrees
copy_files:
- .editorconfig
- .env.example
# But my-important-project goes to a specific location
projects:
- name: my-important-project
base_dir: ~/criticalResults:
- Most projects:
~/worktrees/my-app/branch-name - my-important-project:
~/critical/my-important-project/branch-name
global:
# main and master are already protected by default
protected_branches:
- develop
- staging
- production
- hotfixglobal:
# Base directory for worktrees (project name is auto-added)
# Default: ../worktree (relative to repo)
base_dir: ~/worktrees
# Automatic opening when creating worktrees
# Use any command that accepts a directory path
# Default: none (worktrees will not open automatically)
open_with: code
# Auto-prune after removing worktrees
# Default: true
auto_prune: true
# Files to copy from main worktree to new worktrees
# Useful for .env files, IDE configs, etc.
# Default: none
copy_files:
- .editorconfig
- .env.local
# Branches whose worktrees and branches cannot be removed
# Applies to both 'remove' and 'verify --clean' commands
# Note: main, master are always protected even if not listed
# Default: main, master only
protected_branches:
- develop
- staging
projects:
# Project-specific settings override global settings
- name: critical-app
base_dir: ~/production
open_with: idea
protected_branches:
- develop
- staging
- productionResults:
- Most projects:
~/worktrees/{project}/branch-name(opens in VS Code) - critical-app:
~/production/critical-app/branch-name(opens in IntelliJ IDEA, extra protected branches)
- Configuration is optional - git-turnouts works perfectly without any configuration file
- Configuration is centralized - one
.config.ymlfile in the git-turnouts directory manages all projects - Project names are detected automatically from the repository directory name
- Project name is always added as a subdirectory for organization
- Configuration hierarchy:
- List-based settings (
copy_files,protected_branches): Project-specific adds to global (additive) - Scalar settings (
base_dir,open_with,auto_prune): Project-specific overrides global (replacement)
- List-based settings (
- The
.config.yml.examplefile serves as a template and reference open_withcommands (e.g.,idea,code) must be available in your system PATH - refer to your IDE's documentation for setting up command-line tools
GitHub CLI is required for PR integration features.
Solution:
# macOS
brew install gh
# Linux
# See: https://cli.github.com/After installation, authenticate with GitHub:
gh auth loginNote: You can still use git-turnouts with branch names without gh installed - PR integration features will not be available.
jq is required for JSON processing.
Solution:
# macOS
brew install jq
# Linux (Debian/Ubuntu)
sudo apt-get install jq
# Linux (RHEL/CentOS)
sudo yum install jq
# Or download from: https://jqlang.github.io/jq/download/Git 2.5 or newer is required.
Solution:
# Check your current version
git --version
# macOS - upgrade via Homebrew
brew upgrade git
# Linux - upgrade via package manager
sudo apt-get update && sudo apt-get upgrade git # Debian/Ubuntu
sudo yum update git # RHEL/CentOSThe worktree base directory may not be writable.
Solution:
# Check permissions
ls -la ~/worktrees/
# Fix permissions
chmod u+w ~/worktrees
# Or use a different directory in your config
git-turnouts config init
# Edit .config.yml and set base_dir to a writable locationThe git-turnouts directory may not be writable.
Solution:
# Find the git-turnouts directory
which git-turnouts
# Fix permissions on the directory
chmod u+w /path/to/git-turnouts
# Or create a local config by setting environment variable
export GIT_TURNOUTS_CONFIG=~/.config/git-turnouts/config.ymlA directory with that name already exists in the worktree location.
Solution:
# Check what's there
ls -la ~/worktrees/my-project/
# Remove the conflicting directory if it's not a worktree
rm -rf ~/worktrees/my-project/branch-name
# Or choose a different folder name
git-turnouts add my-custom-name branch-nameGit prevents checking out the same branch in multiple worktrees.
Solution:
# List all worktrees to find where it's checked out
git worktree list
# Remove the existing worktree first
git-turnouts remove branch-name
# Or use a different branch
git-turnouts add new-branch-nameWorktree has uncommitted changes that would be lost.
Solution:
# Option 1: Commit the changes
cd path/to/worktree
git add .
git commit -m "Save work in progress"
# Option 2: Stash the changes
git stash save "Work in progress"
# Option 3: Manually delete (WARNING: loses changes)
rm -rf path/to/worktree
git worktree pruneThe PR doesn't exist or you don't have access to it.
Solution:
# Verify PR exists
gh pr view 123
# Check you're authenticated
gh auth status
# Try re-authenticating
gh auth login
# Verify you're in the correct repository
git remote -vGit Turnouts shows a warning but proceeds to create the worktree.
Explanation: Closed PRs that aren't merged can still have their branches available. The worktree will be created if the branch exists remotely.
Git Turnouts blocks worktree creation for merged PRs.
Reason: Merged PR branches are typically deleted from the remote repository and no longer exist.
Solution:
# Option 1: Work with the base branch where it was merged
git-turnouts add main
# Option 2: Checkout the specific commit if you need to review it
# (Use git log to find the merge commit hash)
# Option 3: If the branch still exists remotely, fetch it manually
git fetch origin branch-name
git-turnouts add branch-nameNo open PRs match your search query.
Solution:
# List all open PRs
gh pr list
# Try a different search term
git-turnouts add "different keywords"
# Use the branch name directly
git-turnouts add branch-nameThe application may not be installed or not in the expected location.
Solution:
# Verify application is installed and can be opened from terminal
# macOS examples:
open -a "IntelliJ IDEA" # IDE
open -a "Visual Studio Code" # IDE
open -a "iTerm" # Terminal
open -a "Warp" # Terminal
# Check your configuration
git-turnouts config show
# Change default application
git-turnouts config init
# Edit .config.yml and set defaults.open_with
# Or specify application per command
git-turnouts add branch-name --open codeApplication opening currently uses platform-specific commands.
Solution:
# Create worktree without automatic opening
git-turnouts add branch-name
# Then navigate manually
cd path/to/worktree # Path shown in output
# Or use your system's file manager/terminal
# macOS:
open path/to/worktree
# Linux:
xdg-open path/to/worktreeApplication Support:
- Any CLI tool that accepts a directory path argument (e.g.,
code,idea,subl,vim,emacs,cursor) - macOS applications via
open -acommand (e.g.,Warp,Sublime Text) - Custom wrapper scripts for special cases (see troubleshooting)
Examples of commonly used applications:
- IDEs:
code(VS Code),idea(IntelliJ IDEA),subl(Sublime),cursor(Cursor AI) - Editors:
vim,emacs,nano - Terminals: Create wrapper scripts for terminal applications
- File managers: Use
finderon macOS or create wrappers for other platforms
Tip: If your tool doesn't work automatically, create worktrees without the --open flag and navigate manually.
Your .config.yml file may have YAML syntax errors.
Solution:
# Backup your config
cp .config.yml .config.yml.backup
# Reset to example template
git-turnouts config init
# Or manually check YAML syntax
# Common issues:
# - Incorrect indentation (use spaces, not tabs)
# - Missing colons after keys
# - Unquoted strings with special characters
# View what's being read
git-turnouts config showMake sure you're editing the right config file.
Solution:
# Find where config should be
which git-turnouts
# Config should be in the same directory as the script
# Show current effective configuration
git-turnouts config show
# Shows detected project name and pathsYou must run git-turnouts from within a Git repository.
Solution:
# Navigate to your repository first
cd /path/to/your/repository
# Verify it's a git repo
git status
# Then run git-turnouts
git-turnouts listNetwork operations (fetching PRs, pulling branches) can be slow.
Explanation: This is normal for:
- Fetching large repositories
- Slow network connections
- First-time branch fetches
Git Turnouts will show progress where possible. Be patient during network operations.
- Check verbose error messages - git-turnouts provides detailed error information
- Enable debug mode (if needed):
bash -x git-turnouts add branch-name
- Open an issue: GitHub Issues
Contributions are welcome! Whether you're fixing bugs, adding features, improving documentation, or suggesting new ideas - all contributions are appreciated.
Getting Started:
- Read our CONTRIBUTING.md for detailed guidelines
- Fork the repository and create a feature branch
- Make your changes and test thoroughly
- Submit a Pull Request with a clear description
Areas where help is appreciated:
- π¨ New IDE/application adapters
- π Bug reports and fixes
- π Documentation and tutorials
- β¨ New features and enhancements
- π Platform support (Windows native, additional Unix-like systems)
For questions or discussions, open an issue on GitHub.
MIT License - see LICENSE file for details
Created to streamline Git worktree workflows with modern GitHub PR integration. Born from the need to work on multiple features, review PRs, and handle hotfixes without the constant context switching pain.
Built for developers who work on multiple features simultaneously and need to switch tracks without losing momentum. Like a well-designed railroad junction, Git Turnouts keeps your development workflow running smoothly.