#git #git-worktree #worktree

app xfeat

CLI utility for managing git worktrees across multiple repositories

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

Custom license

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:

  1. Fetches latest changes from remote
  2. Rebases the feature branch onto origin/<branch> (default: auto-detected from origin/HEAD)
  3. 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 directory
  • xf add <feature> <repos...> — adds worktrees to a feature
  • xf remove <feature> — removes feature (with confirmation) and cds out if needed
  • xf sync <feature> — syncs feature with main
  • xf list and other commands — proxied to xfeat
  • 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