Skip to content

feat: Automatic uv project isolation for git worktrees #22

@renekris

Description

@renekris

Feature Request: Automatic UV Project Isolation for Git Worktrees

Problem Statement

When running uv commands (e.g., uv sync, uv run, uv add) from within a git worktree, uv's default project discovery mechanism searches upward through parent directories to find pyproject.toml. This causes worktrees to discover and use the parent repository's configuration instead of their own.

Example scenario:

# Main repo structure
/repo/
  ├─ .worktrees/
  │   └─ issue-42-fix-auth/
  │       └─ pyproject.toml  (worktree-specific config)
  └─ pyproject.toml           (parent repo config)

# User navigates to worktree
cd /repo/.worktrees/issue-42-fix-auth

# Runs uv sync - expects to use worktree's pyproject.toml
uv sync

# BUT: uv discovers /repo/pyproject.toml instead
# Result: wrong dependencies installed, wrong scripts executed

Root Cause

UV's project discovery uses path.ancestors() to traverse upward until finding a pyproject.toml. In worktree scenarios (.worktrees/<branch-name>), it always finds the root repository's pyproject.toml first.

Source: uv/workspace.rs

Impact

  • Incorrect dependency installation: Worktree installs packages for parent project instead of its own
  • Wrong script execution: uv run executes scripts from wrong project context
  • Environment pollution: Changes to dependencies in worktree affect parent repo and other worktrees sharing same .venv
  • Impossible debugging: "Works on my machine" - last writer wins, non-deterministic behavior
  • Test flakiness: Tests pass in one worktree, fail in another due to environment state changes

Proposed Solution

Implement automatic uv project isolation in ghwt using .envrc files (direnv integration) as primary mechanism with fallback wrapper script.

Implementation Details

1. Create .envrc file in each worktree:

# .worktrees/issue-42-fix-auth/.envrc
# Auto-generated by ghwt for uv project isolation
export UV_PROJECT="/absolute/path/to/repo/.worktrees/issue-42-fix-auth"
export UV_PROJECT_ENVIRONMENT="/absolute/path/to/repo/.worktrees/issue-42-fix-auth/.venv"

How it works:

  • Users install direnv (one-time setup)
  • When entering a worktree via cd, direnv automatically loads .envrc
  • UV_PROJECT environment variable forces uv to use worktree's pyproject.toml
  • No manual intervention required - completely automatic

2. Create fallback wrapper script for non-direnv users:

#!/usr/bin/env bash
# scripts/uv-isolated.sh
export UV_PROJECT="$(pwd)"
export UV_PROJECT_ENVIRONMENT="$(pwd)/.venv"
uv "$@"

Usage without direnv:

cd repo/.worktrees/issue-42-fix-auth
./scripts/uv-isolated.sh sync  # Forces isolation

3. Update .gitignore to prevent committing isolation files:

# UV isolation files in worktrees
.worktrees/*/.envrc
.worktrees/*/uv-isolated.sh
.worktrees/*/.env.local

Benefits

Automatic isolation: No manual --project flag required by user
Persistent: Configuration survives across sessions
Shell-agnostic: Works with bash, zsh, fish, etc.
Reversible: Users can direnv deny if needed
Widely adopted: Standard tooling in modern dev environments
Fallback available: Wrapper script for users without direnv
Zero breakage: Doesn't affect users who don't use uv

Verification Commands

Users can verify uv is using correct project context:

cd repo/.worktrees/issue-42-fix-auth
uv project info
# Expected output:
# Project: /home/user/repo/.worktrees/issue-42-fix-auth
# Lockfile: /home/user/repo/.worktrees/issue-42-fix-auth/uv.lock

Edge Cases Handled

Edge Case Handling Strategy
Existing .envrc in worktree Append to existing file with marker # GHWT-UV-ISOLATION
User manually runs uv --project /other/path Explicit --project flag takes precedence over UV_PROJECT env var (correct behavior)
User sets UV_PROJECT globally .envrc overrides global setting due to export UV_PROJECT= syntax
Multiple worktrees open simultaneously Each has its own .envrc, direnv loads/unloads on cd
User runs uv from subdirectory of worktree UV_PROJECT points to worktree root, so uv finds worktree's pyproject.toml (correct)

Implementation Plan

  1. Add _create_envrc_file() method to WorktreeCreator class
  2. Add _create_uv_wrapper_script() method to WorktreeCreator class
  3. Update worktree creation flow to call these methods
  4. Add unit tests for isolation file creation
  5. Add integration tests for full worktree creation with isolation
  6. Update README.md with UV isolation section
  7. Update WT_TASK_TEMPLATE.md with uv usage instructions
  8. Update .gitignore to exclude isolation files

Documentation Requirements

Add to README.md:

  • Installation instructions for direnv
  • How automatic isolation works
  • Fallback wrapper script usage
  • Verification commands
  • Troubleshooting for edge cases

Acceptance Criteria

  • .envrc file created in each worktree with correct UV_PROJECT path
  • Wrapper script scripts/uv-isolated.sh created and executable
  • Unit tests pass for file creation
  • Integration tests pass for full workflow
  • README.md updated with complete documentation
  • .gitignore updated to exclude isolation files
  • Manual testing confirms isolation works with direnv
  • Manual testing confirms wrapper script works without direnv

Additional Notes

Why NOT share .venv:
Research confirms that sharing .venv across worktrees causes dependency conflicts, "last writer wins" behavior, and impossible-to-debug issues. Each worktree should have its own isolated virtual environment.

Related research:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions