CLI utility for managing git worktrees across multiple repositories.
⚠️ Alpha: This project is under active development. APIs and behavior may change.
Why? I work on several large products simultaneously. In total, that's 90+ repositories, and a feature often touches multiple products at once. This used to be a pain: constant branch switching, stashing, and a mess of open folders.
xfeat fixes this — each feature gets its own isolated workspace via git worktrees.
No context switching. No stashing. Perfect for parallel work with AI coding agents.
xfeat is designed for developers working on multiple projects simultaneously. Each project has its own workspace with repos (source repositories) and features (worktree branches) directories. Environment variables XF_REPOS_DIR and XF_FEATURES_DIR are scoped per-project, allowing isolated feature development across multiple repositories within a single project context.
By leveraging git worktrees, xfeat enables parallel development on multiple features without the overhead of cloning repositories or switching branches. Each feature gets its own isolated workspace, making it ideal for AI-assisted development where multiple coding agents can work on different features simultaneously without conflicts.
- git — xfeat relies on git worktrees
Using cargo:
cargo install xfeat --lockedUsing mise:
mise install github:just-sultanov/xfeatUsing curl (macOS / Linux):
# Install to ~/.local/bin (default)
curl -fsSL https://raw.githubusercontent.com/just-sultanov/xfeat/main/install.sh | bash
# Install to a custom directory
curl -fsSL https://raw.githubusercontent.com/just-sultanov/xfeat/main/install.sh | bash -s -- --prefix /usr/local/binUsing PowerShell (Windows):
# Install to $env:USERPROFILE\.local\bin (default)
irm https://raw.githubusercontent.com/just-sultanov/xfeat/main/install.ps1 | iex
# Install to a custom directory
irm https://raw.githubusercontent.com/just-sultanov/xfeat/main/install.ps1 | iex -ArgumentList '-Prefix', 'C:\tools'Using pre-built binaries:
Download the latest release from GitHub Releases.
| Platform | File |
|---|---|
| macOS (Apple Silicon) | xfeat-aarch64-apple-darwin.tar.gz |
| macOS (Intel) | xfeat-x86_64-apple-darwin.tar.gz |
| Linux (x86_64, static) | xfeat-x86_64-unknown-linux-musl.tar.gz |
| Windows (x86_64) | xfeat-x86_64-pc-windows-gnu.zip |
Choose one of the following methods to configure your project:
Using export:
export XF_REPOS_DIR=~/projects/store/repos
export XF_FEATURES_DIR=~/projects/store/featuresUsing direnv:
Create an .envrc file:
export XF_REPOS_DIR=~/projects/store/repos
export XF_FEATURES_DIR=~/projects/store/featuresUsing mise env:
Add to mise.toml:
[env]
XF_REPOS_DIR = "~/projects/store/repos"
XF_FEATURES_DIR = "~/projects/store/features"Add to your ~/.zshrc or ~/.bashrc:
eval "$(xfeat init zsh)" # or: eval "$(xfeat init bash)"Set up your project workspace and start developing features in parallel:
~/projects/store/
├── repos/
│ ├── payment-service/
│ ├── checkout-service/
│ └── frontend/
└── features/ # empty
1. Create a new feature and add worktrees:
xf new checkout-v2
xf add checkout-v2 payment-service checkout-service~/projects/store/
├── repos/
│ ├── payment-service/
│ ├── checkout-service/
│ └── frontend/
└── features/
└── checkout-v2/
├── payment-service/ # worktree on branch checkout-v2
└── checkout-service/ # worktree on branch checkout-v2
2. Work on your feature — each worktree is a fully independent git checkout:
cd "$XF_FEATURES_DIR/checkout-v2"
cd payment-service
# make changes, commit, push — no stashing, no branch switching3. Stay in sync with main:
xf sync checkout-v24. List all active features:
xf listcheckout-v2
payment-service (checkout-v2)
checkout-service (checkout-v2)
payment-refactor (empty)
5. Show paths:
xf list --pathcheckout-v2
payment-service (checkout-v2) -> checkout-v2/payment-service
checkout-service (checkout-v2) -> checkout-v2/checkout-service
5. Done? Clean up:
xf remove checkout-v2Each feature gets its own git worktrees, so you can switch between projects and features instantly without stashing or switching branches.
Repositories can be organized in groups using subdirectories. Worktrees preserve this structure:
# Example: repositories in $XF_REPOS_DIR
# repos/
# services/
# payment-service/
# checkout-service/
# libs/
# common-utils/
# Add nested repositories - xfeat creates matching structure
xf new story-123
xf add story-123 services/payment-service libs/common-utils$XF_FEATURES_DIR/
└── story-123/
├── services/
│ └── payment-service/ # worktree with branch story-123
└── libs/
└── common-utils/ # worktree with branch story-123
Tree output shows the nested structure:
xf list
story-123
services/payment-service (story-123)
services/common-utils (story-123)
bugfix-456
core-utils (bugfix-456)Feature names can also be nested:
xf new epic-1/story-123
xf add epic-1/story-123 services/payment-serviceRun multiple AI coding agents (Claude Code, Codex, Cursor, etc.) on different features simultaneously — each agent gets its own isolated workspace with no risk of conflicts:
# Start two features in parallel
xf new ai-payment-fix
xf add ai-payment-fix payment-service --from develop
xf new ai-checkout-v3
xf add ai-checkout-v3 checkout-service frontend --from developNow launch AI agents in separate terminals:
# Terminal 1 — Claude Code working on payment fix
cd "$XF_FEATURES_DIR/ai-payment-fix"
cd payment-service
claude
# Terminal 2 — Codex working on checkout redesign
cd "$XF_FEATURES_DIR/ai-checkout-v3"
cd checkout-service
codexBoth agents work independently on their own branches. Before merging, sync each feature with the latest main:
xf sync ai-payment-fix
xf sync ai-checkout-v3Switch between projects and juggle multiple features without losing context:
# Project A — e-commerce
cd ~/projects/store
export XF_REPOS_DIR=~/projects/store/repos
export XF_FEATURES_DIR=~/projects/store/features
xf new checkout-v2
xf add checkout-v2 payment-service checkout-service
# Project B — analytics dashboard
cd ~/projects/analytics
export XF_REPOS_DIR=~/projects/analytics/repos
export XF_FEATURES_DIR=~/projects/analytics/features
xf new dashboard-redesign
xf add dashboard-redesign frontend backendView everything at a glance:
xf listcheckout-v2
payment-service (checkout-v2)
checkout-service (checkout-v2)
payment-refactor
payment-service (payment-refactor)
dashboard-redesign
frontend (dashboard-redesign)
backend (dashboard-redesign)
Create a new empty feature directory:
xf new <feature-name>Example:
xf new STORY-123-add-payment
# or nested
xf new epic-1/story-123This creates an empty directory (supports nested paths like epic-1/story-123). Add worktrees with xf add:
xf add STORY-123-add-payment payment-service checkout-service frontendAdd worktrees for repositories to an existing feature:
xf add <feature-name> <repos...>
xf add <feature-name> <repos...> --from <branch>
xf add <feature-name> <repos...> --branch <branch-name>
xf add <feature-name> <repos...> --from <branch> --branch <branch-name>Examples:
# Add repos — branches named after the feature
xf add STORY-123-add-payment payment-service checkout-service
# Add nested repos — xfeat preserves directory structure
xf add story-123 services/payment-service libs/common-utils
# Add repos, branching from a specific source branch
xf add STORY-123-add-payment payment-service --from develop
# Add repos with a custom branch name
xf add STORY-123-add-payment payment-service --branch feature/TASK-123-add-payment
# Combine: branch from 'develop' with a custom name
xf add STORY-123-add-payment payment-service --from develop --branch feature/TASK-123-add-paymentSkips repositories that already have worktrees in the feature.
List all features with their worktrees and current branches:
xf list
xf list --pathDefault output:
STORY-123-add-payment
payment-service (STORY-123-add-payment)
checkout-service (STORY-123-add-payment)
STORY-456-redesign-checkout
frontend (STORY-456-redesign-checkout)
STORY-789-empty (empty)
With --path:
STORY-123-add-payment
payment-service (STORY-123-add-payment) -> STORY-123-add-payment/payment-service
checkout-service (STORY-123-add-payment) -> STORY-123-add-payment/checkout-service
STORY-456-redesign-checkout
frontend (STORY-456-redesign-checkout) -> STORY-456-redesign-checkout/frontend
Empty features (created with xf new but without worktrees yet) are shown with (empty).
Sync a feature with the latest main branch from source repos:
xf sync <feature-name>
xf sync <feature-name> --from <branch>For each worktree in the feature:
- Fetches latest changes from remote
- Rebases the feature branch onto
origin/<branch>(default: auto-detected from origin/HEAD) - Stops on first conflict with an error message
Example:
xf sync STORY-123-add-payment
xf sync STORY-123-add-payment --from developTypical workflow before merging:
xf sync STORY-123-add-payment # rebase onto latest main
# resolve any conflicts if needed
xf sync STORY-123-add-payment # verify clean sync
# merge or create PRRemove a feature and its worktrees. Prompts for confirmation by default:
xf remove <feature-name>
xf remove <feature-name> --yes # skip confirmation (for scripts)Example output:
Feature 'STORY-123-add-payment' contains:
- payment-service (STORY-123-add-payment)
- checkout-service (STORY-123-add-payment) ⚠ has uncommitted changes
Remove feature 'STORY-123-add-payment'? [y/N] y
Feature 'STORY-123-add-payment' removed.
Generate shell initialization code with autocompletion and xf wrapper function:
eval "$(xfeat init zsh)" # or: eval "$(xfeat init bash)"Supported shells: zsh, bash
The xf wrapper:
xf new <feature>— creates an empty feature directoryxf add <feature> <repos...>— adds worktrees to a featurexf remove <feature>— removes feature (with confirmation) andcds out if neededxf sync <feature>— syncs feature with mainxf listand other commands — proxied toxfeat- Tab completion for repository names (
xf add <TAB>), feature names (xf new <TAB>,xf remove <TAB>,xf sync <TAB>)
Shell scripts are stored in shell/ and embedded into the binary at compile time. They read XF_REPOS_DIR and XF_FEATURES_DIR from the environment on each invocation, making them compatible with tools like direnv. Tilde (~) in paths is expanded automatically.
Set environment variables per-project:
export XF_REPOS_DIR=~/projects/project-x/repos
export XF_FEATURES_DIR=~/projects/project-x/features| Variable | Description | Default |
|---|---|---|
XF_REPOS_DIR |
Directory containing source git repositories | ~/workspace/repos |
XF_FEATURES_DIR |
Directory where feature worktrees are created | ~/workspace/features |
Paths can be absolute (/tmp/repos), relative (./repos), or tilde-based (~/repos). All are resolved correctly.
MIT — see LICENSE for details.