-
Git Operations: Using subprocess calls to git CLI instead of gitpython
- Rationale: Simpler, no external dependencies, guaranteed compatibility
- All git commands wrapped in git.py module for consistency
-
Configuration: Using TOML format
- Local config:
.wt.tomlin repo root - Global config:
~/.wt.tomlin home directory - Local overrides global
- Local config:
-
Shell Integration: Using wrapper function approach
- CLI outputs target path on success for
wt switch - Shell wrapper intercepts and performs
cd - Different syntax for bash/zsh vs fish
- CLI outputs target path on success for
-
Exit Codes: Standardized across all commands
- 0: Success
- 1: General error
- 2: Invalid arguments
- 3: Git error
- 4: Worktree not found
- 5: User cancelled
wt-worktree/
├── pyproject.toml
├── PRD.md
├── notes.md
├── README.md
├── wt/
│ ├── __init__.py
│ ├── __main__.py # Entry point
│ ├── cli.py # Click commands
│ ├── config.py # Configuration management
│ ├── git.py # Git operations
│ ├── worktree.py # Worktree operations
│ ├── shell.py # Shell integration
│ └── prompts.py # User prompts
└── tests/
├── __init__.py
├── test_config.py
├── test_git.py
├── test_worktree.py
└── test_cli.py
- Core modules (git.py, config.py, worktree.py)
- Basic CLI commands (init, switch, list)
- Management commands (diff, delete, status)
- Advanced features (run, clean, config)
- Shell integration
- Comprehensive testing
- Use real git repositories for testing (not mocks)
- Create temporary test repos in /tmp
- Test both success and error cases
- Test user prompts with environment variable overrides
- Test shell integration with subprocess
-
Git Commit Signing in Tests
- Problem: Tests were failing because git was configured to sign commits, but signing was failing
- Solution: Added
git config commit.gpgsign falsein test fixtures - Lesson: Always disable GPG signing in test environments
-
Test Directory Cleanup Issues
- Problem: Tests were failing with
FileNotFoundErrorwhen callingos.getcwd()because previous tests had deleted the current directory - Solution: Wrapped
os.getcwd()in try-except and fallback to/tmp - Lesson: Be careful with directory changes in tests, always handle cleanup gracefully
- Problem: Tests were failing with
-
Remote References in Tests
- Problem: Tests were failing because default_base was set to
origin/mainbut test repos don't have remotes - Solution: Set
default_basetomainin test fixtures - Lesson: Make config values appropriate for test environment
- Problem: Tests were failing because default_base was set to
-
Uncommitted Files in Status Tests
- Problem: Test for clean worktree was failing because
.wt.tomlwas uncommitted - Solution: Commit the config file in the manager fixture
- Lesson: Ensure test setup leaves repository in expected state
- Problem: Test for clean worktree was failing because
-
TOML Library Compatibility
- Problem: Need to support Python 3.10 which doesn't have built-in tomllib
- Solution: Added conditional import with fallback to tomli package
- Lesson: Always consider Python version compatibility
-
Config Not Found in Secondary Worktrees
- Problem: When running wt commands from a secondary worktree, it would ask to run
wt initagain because it couldn't find.wt.toml - Initial Solution: Added
get_main_worktree_root()function to find main worktree - Better Solution: Simplified to use global config in
~/.wt.toml(or$WT_CONFIG/.wt.toml) - Rationale: Simpler design, no need to find main worktree, works the same everywhere
- Changes:
- Config now stored in one place (home directory by default)
- Removed local repo config concept
wt initis now optional (just sets custom defaults)- No need to run
wt initper repository
- Lesson: Sometimes the simplest solution is the best - global config is easier than per-repo config for this use case
- Problem: When running wt commands from a secondary worktree, it would ask to run
-
Missing Special Symbol Support in
wt run- Problem: The
wt runcommand didn't support the^(default) and-(previous) symbols, whilewt switchdid - Error: Running
wt run ^ "git status"orwt run - "git diff"resulted in "Error: Worktree not found" - Solution: Added special handling for both
^and-symbols in theruncommand (cli.py:379-409) to resolve them before looking up the worktree - Implementation:
- Added check
if name == "-":to find the previous worktree from.wt_previousfile and resolve it to the worktree name - Added check
elif name == "^":to get the default worktree usingctx.manager.get_default_worktree()and use its name
- Added check
- Lesson: Always ensure consistency across commands - if a special symbol works in one command, users will expect it to work in related commands too
- Problem: The
-
Implementing wt sync Command
- Problem: Need to sync worktrees with their upstream branches, handling stash/unstash, pull, rebase
- Solution:
- Added git operations for stash, pull, and rebase in git.py
- Implemented sync_worktree method in worktree.py to handle individual worktree sync
- Implemented sync_worktrees method to handle multiple worktrees
- Added sync command in cli.py with --all, --include, --exclude, --rebase options
- Implementation Details:
- Stash changes before pull using
git stash push --include-untracked - Pull from upstream using
git pull <remote> <branch> - Optionally rebase onto default base (origin/main)
- Restore stashed changes using
git stash pop - Handle conflicts by aborting rebase/merge and leaving repo in clean state
- Continue with other worktrees if one fails
- Stash changes before pull using
- Error Handling:
- Initially used
error()function which calls sys.exit, causing tests to fail - Fixed by using
warning()function instead to print errors without exiting - This allows the command to continue syncing other worktrees after failures
- Initially used
- Lesson: When implementing operations that process multiple items, use warning/info functions instead of error() to avoid early exit
-
Detached Worktree Name Preservation
- Problem: Multiple detached worktrees all showed as "(Detached)" in lists, making them indistinguishable
- Problem: Couldn't switch to detached worktrees by the name given during
wt switch -c <name> --detached - Solution:
- Store user-given names in worktree-specific git config using
git config --worktree worktree.name <name> - Retrieve stored names when listing worktrees
- Enable
extensions.worktreeConfigto support per-worktree config
- Store user-given names in worktree-specific git config using
- Implementation Details:
- Added
enable_worktree_config(),set_worktree_name(), andget_worktree_name()functions in git.py - Modified
create_worktree()to store names for detached worktrees - Modified
list_worktrees()to retrieve stored names or fallback to(detached-<commit>) - Fixed base branch selection: detached worktrees now use HEAD instead of default_base
- Added
- Key Insight: Git's
--worktreeconfig flag requiresextensions.worktreeConfigto be enabled first - Lesson: Per-worktree config in git requires enabling the worktreeConfig extension, and is the right way to store worktree-specific metadata
- Backward Compatibility: Added
_infer_name_from_path()to infer names from path patterns for detached worktrees created before this fix or via raw git commands - Fallback chain: stored config → inferred from path →
(detached-<commit>)