5 unstable releases
Uses new Rust 2024
| 0.5.1 | Apr 27, 2026 |
|---|---|
| 0.5.0 | Apr 13, 2026 |
| 0.4.0 | Apr 8, 2026 |
| 0.3.1 | Apr 6, 2026 |
| 0.3.0 | Apr 6, 2026 |
#158 in Development tools
105KB
2.5K
SLoC
xfeat
CLI utility for managing git worktrees across multiple repositories.
⚠️ Alpha: This project is under active development. APIs and behavior may change.
Rationale
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.
Overview
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.
Installation
Requirements
- git — xfeat relies on git worktrees
Install
Using cargo:
cargo install xfeat --locked
Using mise:
mise install github:just-sultanov/xfeat
Using 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/bin
Using 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 |
Quick Start
Set up environment
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/features
Using direnv:
Create an .envrc file:
export XF_REPOS_DIR=~/projects/store/repos
export XF_FEATURES_DIR=~/projects/store/features
Using mise env:
Add to mise.toml:
[env]
XF_REPOS_DIR = "~/projects/store/repos"
XF_FEATURES_DIR = "~/projects/store/features"
Initialize shell integration
Add to your ~/.zshrc or ~/.bashrc:
eval "$(xfeat init zsh)" # or: eval "$(xfeat init bash)"
Create your first feature
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 switching
3. Stay in sync with main:
xf sync checkout-v2
4. List all active features:
xf list
checkout-v2
payment-service (checkout-v2)
checkout-service (checkout-v2)
payment-refactor (empty)
5. Show paths:
xf list --path
checkout-v2
payment-service (checkout-v2) -> checkout-v2/payment-service
checkout-service (checkout-v2) -> checkout-v2/checkout-service
5. Done? Clean up:
xf remove checkout-v2
Each feature gets its own git worktrees, so you can switch between projects and features instantly without stashing or switching branches.
Nested repositories
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-service
Workflows
AI-assisted development
Run 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 develop
Now 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
codex
Both 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-v3
Multiple projects & features simultaneously
Switch 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 backend
View everything at a glance:
xf list
checkout-v2
payment-service (checkout-v2)
checkout-service (checkout-v2)
payment-refactor
payment-service (payment-refactor)
dashboard-redesign
frontend (dashboard-redesign)
backend (dashboard-redesign)
Commands
xfeat new
Create a new empty feature directory:
xf new <feature-name>
Example:
xf new STORY-123-add-payment
# or nested
xf new epic-1/story-123
This 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 frontend
xfeat add
Add 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-payment
Skips repositories that already have worktrees in the feature.
xfeat list
List all features with their worktrees and current branches:
xf list
xf list --path
Default 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).
xfeat sync
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 develop
Typical 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 PR
xfeat remove
Remove 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.
xfeat init
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.
Configuration
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.
License
MIT — see LICENSE for details.
Dependencies
~1–1.6MB
~28K SLoC