@zyoung-ff/pando
v0.1.0
Published
A TypeScript-based CLI for managing Git worktrees with automation-first design
Maintainers
Readme
Pando
A TypeScript-based CLI for managing Git worktrees with automation-first design
Pando makes it effortless to work on multiple branches simultaneously using Git worktrees. Built for modern developer workflows, it provides both human-friendly commands and machine-readable output for CI/CD automation.
Features
- 🌳 Worktree Management: Create, list, and remove git worktrees with ease
- 🤖 Automation-First: Every command supports
--jsonflag for scripting and AI agents - 🎯 Developer-Friendly: Interactive prompts when flags aren't provided
- ⚡ Fast: Built with TypeScript for type safety and performance
- 🔧 Extensible: Clean architecture makes adding new commands simple
Installation
Using Homebrew (macOS/Linux)
brew tap zpyoung/pando
brew install pandoUsing pnpm
pnpm install -g @zyoung-ff/pandoUsing npm
npm install -g @zyoung-ff/pandoFrom source
git clone https://github.com/zpyoung/pando.git
cd pando
pnpm install
pnpm build
pnpm linkQuick Start
# Initialize configuration (optional but recommended)
pando config init
# Create a new worktree for a feature branch
pando add --path ../feature-x --branch feature-x
# List all worktrees
pando list
# Remove a worktree (interactive selection or direct with --path)
pando remove
pando remove --path ../feature-x
# View current configuration and sources
pando config showCommands
pando add
Create a new git worktree (supports both creating new branches and checking out existing branches)
Flags:
-p, --path: Path for the new worktree (optional ifworktree.defaultPathis configured)-b, --branch: Branch to checkout or create-c, --commit: Commit hash to base the new branch on-f, --force: Force create branch even if it exists (uses git worktree add -B)--no-rebase: Skip automatic rebase of existing branch onto source branch-j, --json: Output in JSON format
Automatic Rebase: When checking out an existing branch, pando automatically rebases it onto the current branch. This keeps your feature branches up-to-date. If the rebase fails (e.g., conflicts), a warning is shown but the worktree is still created. Use --no-rebase to skip this behavior, or set worktree.rebaseOnAdd = false in config.
Examples:
# Create new branch in worktree
pando add --path ../feature-x --branch feature-x
# Checkout existing branch into worktree
pando add --path ../existing --branch existing-branch
# Using config default path (if worktree.defaultPath is set in .pando.toml)
pando add --branch feature-x
# OR using shorthand (positional argument)
pando add feature-x
# From specific commit
pando add --path ../hotfix --branch hotfix --commit abc123
# Force reset existing branch to commit
pando add --path ../feature --branch feature-x --commit abc123 --forcepando list
List all git worktrees
Flags:
-v, --verbose: Show detailed information-j, --json: Output in JSON format
Examples:
pando list
pando list --jsonpando remove
Remove a git worktree
Flags:
-p, --path: Path to the worktree to remove (optional - will prompt interactively if omitted)-f, --force: Force removal even with uncommitted changes-k, --keep-branch: Keep the local branch (do not delete it)-d, --delete-branch: Delete associated branch after removing worktree (none|local|remote)none: Don't delete any brancheslocal: Delete local branch only (default)remote: Delete both local and remote branches
-j, --json: Output in JSON format (requires --path)
Branch Deletion:
- By default, the local branch is deleted when removing a worktree
- Use
--keep-branchto preserve the branch - Before deleting, checks if branch is merged (use
--forceto skip this check) - Remote branch deletion requires confirmation unless
--forceis used - Use
worktree.deleteBranchOnRemovein config to change default behavior
Examples:
# Interactive selection (select from list)
pando remove
# Direct removal with path (deletes local branch by default)
pando remove --path ../feature-x
pando remove --path ../feature-x --force
# Keep the branch when removing worktree
pando remove --path ../feature-x --keep-branch
# Remove worktree and delete both local and remote branches
pando remove --path ../feature-x --delete-branch remote --force
# Interactive multi-select with force flag
pando remove --forcepando clean
Clean stale git worktrees (merged branches, gone upstream, prunable)
Detection Categories:
- Merged: Branch fully merged into main/master (or specified target branch)
- Gone: Remote tracking branch was deleted upstream
- Prunable: Worktree directory was already deleted
Flags:
--fetch: Rungit fetch --prunebefore detection to update remote tracking branch state (configurable:clean.fetch)-f, --force: Skip confirmation prompts and clean all stale worktrees-k, --keep-branch: Keep local branch after worktree removal (configurable:worktree.deleteBranchOnRemove = "none")--dry-run: Show what would be removed without acting-t, --target-branch: Branch to check merges against (default: main or master) (configurable:worktree.targetBranch)-j, --json: Output in JSON format
Examples:
# Interactive selection of stale worktrees
pando clean
# Fetch latest and detect stale worktrees
pando clean --fetch
# See what would be cleaned without acting
pando clean --dry-run
# Clean all stale worktrees without prompts
pando clean --force
# Check merges against develop branch
pando clean --target-branch develop
# Keep branches after removing worktrees
pando clean --keep-branch
# JSON output for scripting
pando clean --jsonpando symlink
Move a file from the current worktree to the main worktree and replace it with a symlink. Useful for keeping configuration files, dependencies, or other shared files in sync across all worktrees.
Arguments:
FILE: File to symlink (required)
Flags:
-f, --force: Overwrite file in main worktree if it exists--dry-run: Simulate the operation without making changes-j, --json: Output in JSON format
Examples:
# Move .env file to main worktree and symlink it
pando symlink .env
# Preview what would happen
pando symlink package.json --dry-run
# Overwrite existing file in main worktree
pando symlink config.json --force
# Use with JSON output
pando symlink .env --jsonUse Cases:
- Environment files (
.env,.env.local): Share environment configuration across worktrees - Lock files (
package-lock.json,pnpm-lock.yaml): Ensure consistent dependency resolution - IDE settings (
.vscode/settings.json): Share editor configuration - Build cache directories: Avoid duplicate downloads/compilation
pando branch backup
Create a timestamped backup of a branch. Backup branches are named backup/<sourceBranch>/<timestamp> where timestamp is UTC YYYYMMDD-HHmmss format.
Flags:
-b, --branch: Source branch to backup (default: current branch)-m, --message: Optional message to store with the backup-j, --json: Output in JSON format
Examples:
# Backup the current branch
pando branch backup
# Backup with a descriptive message
pando branch backup -m "Before risky refactor"
# Backup a specific branch
pando branch backup --branch main
# Backup with JSON output for scripts
pando branch backup --branch feature/auth -m "Pre-merge backup" --jsonUse Cases:
- Before rebasing: Create a safety net before interactive rebase
- Before merging: Snapshot a feature branch before merging to main
- Experimenting: Save current state before trying something risky
- Checkpointing: Regular backups during long-running work
pando branch restore
Restore a branch to a previous backup state. Resets the target branch to match the commit of a selected backup branch.
Flags:
-b, --branch: Target branch to restore (default: current branch)--backup: Backup branch to restore from (interactive selection if omitted)-f, --force: Skip confirmation prompt-d, --delete-backup: Delete the backup branch after successful restore-j, --json: Output in JSON format (requires--backup)
Safety Features:
- Checks for uncommitted changes before restoring current branch
- Prevents restoring branches checked out in other worktrees
- Shows commits that will become unreachable before confirmation
- Requires explicit
--backupflag with--json(no interactive mode)
Examples:
# Interactive restore - select from available backups
pando branch restore
# Restore from a specific backup
pando branch restore --backup backup/main/20250117-153045
# Restore a specific branch with force (no confirmation)
pando branch restore --branch main --backup backup/main/20250117-153045 --force
# Restore and delete the backup afterward
pando branch restore --backup backup/feature/20250117-100000 -f -d
# JSON output for automation (requires explicit backup)
pando branch restore --backup backup/feature/20250117-100000 --force --jsonUse Cases:
- Undo a rebase: Restore to pre-rebase state if conflicts are too complex
- Recover from mistakes: Quickly get back to a known good state
- A/B testing approaches: Restore and try a different implementation
Configuration
Pando supports flexible configuration through multiple sources with a clear priority hierarchy.
Config Commands
pando config init
Generate a configuration file with defaults and helpful comments.
Flags:
-g, --global: Create user-level config at~/.config/pando/config.toml--git-root: Create config at git repository root-f, --force: Overwrite existing config file-m, --merge: Merge missing defaults into existing config (default behavior)--no-merge: Error if config already exists-j, --json: Output in JSON format
Examples:
# Create project config in current directory
pando config init
# Create user-level (global) config
pando config init --global
# Create config at git repository root
pando config init --git-root
# Overwrite existing config
pando config init --force
# Add missing defaults to existing config
pando config init --mergepando config show
Display the current effective configuration with source information.
Flags:
-j, --json: Output in JSON format
Examples:
# Show current config with sources
pando config show
# JSON output for scripts
pando config show --jsonConfiguration Locations
Pando discovers configuration from multiple locations:
| Location | File | Use Case |
|----------|------|----------|
| Current directory | .pando.toml | Project-specific settings |
| Git root | .pando.toml | Repository-wide defaults |
| Project files | pyproject.toml, package.json, etc. | Embedded in existing config |
| User home | ~/.config/pando/config.toml | User-level defaults |
Configuration Priority
Settings are merged with the following priority (highest to lowest):
- CLI flags - Always win (e.g.,
--path,--no-rebase) - Environment variables -
PANDO_*prefixed variables - Project
.pando.toml- In current directory or parent directories - Project files -
pyproject.toml[tool.pando],package.json"pando", etc. - Global config -
~/.config/pando/config.toml - Built-in defaults - Sensible defaults for all options
Project vs User Config
Project config (.pando.toml in repo):
- Shared with team via version control
- Project-specific worktree paths and patterns
- Checked into git
User config (~/.config/pando/config.toml):
- Personal preferences across all projects
- Default behaviors you always want
- Not shared with team
# Create user-level config
pando config init --globalConfig File Format
Pando uses TOML format for configuration:
# Rsync Configuration
[rsync]
enabled = true
flags = ["--archive", "--exclude", ".git"]
exclude = ["dist/", "node_modules/"]
# Symlink Configuration
[symlink]
patterns = ["package.json", ".env*"]
relative = true
beforeRsync = true
# Worktree Configuration
[worktree]
defaultPath = "../worktrees" # Default parent directory for worktrees
rebaseOnAdd = true # Rebase existing branches when adding worktree
deleteBranchOnRemove = "none" # Delete branch on worktree remove: "none", "local", "remote"
targetBranch = "main" # Target branch for merge checks (used by clean command)
# Clean Configuration
[clean]
fetch = false # Run git fetch --prune before detectionEmbedding in Project Files
Instead of a separate .pando.toml, you can embed configuration in existing project files:
pyproject.toml (Python projects):
[tool.pando]
[tool.pando.worktree]
defaultPath = "../worktrees"package.json (Node.js projects):
{
"pando": {
"worktree": {
"defaultPath": "../worktrees"
}
}
}Cargo.toml (Rust projects):
[package.metadata.pando]
[package.metadata.pando.worktree]
defaultPath = "../worktrees"Worktree Default Path
The worktree.defaultPath setting allows you to specify a default parent directory for worktrees:
- Relative paths are resolved from the git repository root
- Absolute paths are used as-is
- When creating a worktree with
--branchbut no--path, the branch name is appended to the default path - Branch name sanitization: Forward slashes (
/) in branch names are automatically converted to underscores (_) for filesystem safety
Example:
[worktree]
defaultPath = "../worktrees"# Creates worktree at ../worktrees/feature-x (relative to git root)
pando add --branch feature-x
# Branch names with slashes are sanitized
pando add --branch feature/auth
# Creates: ../worktrees/feature_authEnvironment Variables
All configuration options can be set via environment variables using the PANDO_ prefix:
# Set default worktree path
export PANDO_WORKTREE_DEFAULT_PATH="../worktrees"
# Disable automatic rebase on existing branches
export PANDO_WORKTREE_REBASE_ON_ADD=false
# Delete local branch when removing worktree
export PANDO_WORKTREE_DELETE_BRANCH_ON_REMOVE=local
# Set target branch for merge checks (clean command)
export PANDO_WORKTREE_TARGET_BRANCH=main
# Always fetch before detecting stale worktrees
export PANDO_CLEAN_FETCH=true
# Now you can omit --path
pando add --branch feature-xEnvironment variable format:
- Prefix:
PANDO_ - Pattern:
PANDO_SECTION_KEY - Example:
PANDO_WORKTREE_DEFAULT_PATH→worktree.defaultPath
Environment variables override file-based configuration but are overridden by explicit command-line flags.
Automation & JSON Output
All commands support the --json flag for machine-readable output:
# Use in scripts
worktrees=$(pando list --json)
# Parse with jq
pando list --json | jq '.[] | select(.branch == "feature-x")'Development
# Install dependencies
pnpm install
# Run in development mode
pnpm dev list
# Build
pnpm build
# Run tests
pnpm test
# Lint & format
pnpm lint
pnpm formatProject Structure
pando/
├── src/
│ ├── commands/ # Command implementations
│ ├── utils/ # Shared utilities
│ └── index.ts # Main entry point
├── test/ # Tests
├── bin/ # Executable scripts
└── dist/ # Compiled outputRequirements
- Node.js >= 18.0.0
- Git >= 2.5.0 (for worktree support)
Troubleshooting
Error Messages and Stack Traces
Pando uses clean error messages for expected errors (like "file already exists" or "not a git repository"). You should not see stack traces for these errors.
If you see a stack trace for a validation error, this indicates a bug - please report it!
Common error types:
- Validation Errors: Clean error messages without stack traces (use
--force, missing files, invalid arguments) - Operation Errors: Runtime failures with context (network errors, permission issues, git command failures)
- Internal Errors: Stack traces indicating bugs that should be reported
JSON Output for Scripts
All commands support --json flag for machine-readable output:
# Merge missing defaults into existing config
pando config init --json
# Output: {"status":"success","action":"merged","added":[...],"addedCount":2}
# Check exit codes in scripts
pando add --path ../feature --branch feature --json
if [ $? -ne 0 ]; then
echo "Command failed"
fiDebug Mode
For detailed debugging, run commands with Node.js debug environment:
# Enable debug output
NODE_DEBUG=pando pnpm dev list
# Or with node inspector
node --inspect bin/dev.js listCommon Issues
"Not a git repository"
- Make sure you're running pando from within a git repository
- Check
git statusworks in your current directory
"Worktree path already exists"
- The target path already has a directory/file
- Use a different path or remove the existing path first
"Worktree has uncommitted changes"
- The worktree you're trying to remove has uncommitted changes
- Commit or stash changes first, or use
--forceto remove anyway (WARNING: will lose changes)
"rsync is not installed"
- Install rsync for file syncing features
- macOS:
brew install rsync - Ubuntu/Debian:
apt install rsync - Or use
--skip-rsyncto disable file syncing
Contributing
Contributions are welcome! Please read ARCHITECTURE.md and DESIGN.md to understand the project structure and design decisions.
License
MIT © zpyoung
Why "Pando"?
Pando is a clonal colony of aspen trees that shares a single root system - much like how git worktrees share a single repository!
