English | Π ΡΡΡΠΊΠΈΠΉ
β οΈ Warning: GWW is not heavily tested, use at your own risk!
A CLI tool that wraps git worktree functionality with configurable path templates, condition-based routing, and project-specific actions.
- π Configurable path templates: Dynamic path generation using templates with functions like
path(n),branch(),norm_branch(),tag() - π Condition-based routing: Route repositories to different locations based on URI conditions (host, path, protocol, tags)
- π·οΈ Tag support: Pass custom tags via
--tagoption for conditional routing and path organization - βοΈ Project actions: Execute custom actions (file copies, commands) after clone or worktree creation
- π Shell completion: Bash, Zsh, and Fish completion support
- π Python 3.11+
- π§ Git
- π₯οΈ Unix-like system (Linux, macOS)
uv tool install "git+https://github.com/vadimvolk/git-worktree-wrapper.git"
gww --helppipx install "git+https://github.com/vadimvolk/git-worktree-wrapper.git"
gww --help# Clone the repository
git clone git@github.com:vadimvolk/git-worktree-wrapper.git
cd git-worktree-wrapper
# Install with uv
uv sync
# Run gww
uv run gww --help# From a local checkout
cd git-worktree-wrapper
python -m pip install .
gww --helpgww init configThis creates a default configuration file at ~/.config/gww/config.yml (Linux) or ~/Library/Application Support/gww/config.yml (macOS). Edit these 2 values: default_sources and default_worktrees. Check the tutorial section for routing details.
gww init shell zsh # or bash, or fishThis installs shell completion and aliases (gwc, gwa, gwr) for easier workflow. Follow the instructions printed by the command to enable them in your shell.
gwc https://github.com/user/repo.git
# Prompts: "Navigate to ~/Developer/sources/github/user/repo? [Y/n]"
# Navigates if you confirm (default: yes)cd ~/Developer/sources/github/user/repo
gwa feature-branch
# Prompts: "Navigate to ~/Developer/worktrees/github/user/repo/feature-branch? [Y/n]"
# Navigates if you confirm (default: yes)gwr feature-branch
# If worktree has uncommitted changes or untracked files:
# Prompts: "Force removal? [y/N]"
# Removes with --force if you confirm
# Otherwise: Removes worktree immediately
# Output: Removed worktree: ~/Developer/worktrees/github/user/repo/feature-branchgww pull
# Output: Updated source repository: ~/Developer/sources/github/user/repoNote: gww pull updates the source repository even from a worktree, as long as the source is clean and has main or master checked out. Useful for merge/rebase workflows.
gww pull # from any repository worktree
git rebase main # rebase your current changes to updated main branchCreate a backup first!
gww migrate ~/old-repos --dry-run
# Output:
# Would migrate 5 repositories:
# ~/old-repos/repo1 -> ~/Developer/sources/github/user/repo1
# ...
gww migrate ~/old-repos
# Copy (default): list, copy sources then worktrees, repair, summary
gww migrate ~/old-repos --inplace
# Move worktrees then sources, repair, clean empty foldersThe migrate command scans one or more directories for git repositories and migrates them to locations based on your current configuration. It's useful when:
- You've updated your configuration and want to reorganize existing repositories
- You're moving from manual repository management to GWW
- You need to consolidate repositories from different locations
Options:
--dry-run,-n: Show what would be migrated without making changes--copy(default): Copy repositories to new locations; list, validate, copy sources then worktrees, rungit worktree repair, then report summary. No folder cleanup.--inplace: Move repositories in place (worktrees first, then sources), rungit worktree repair, then recursively clean empty source folders.
Behavior:
- Accepts one or more paths; scans each and merges repo lists (deduplicated)
- Classifies each repo as source or worktree; uses source path template for sources and worktree path template for worktrees
- --inplace: Two passes (worktrees then sources), move and repair, then remove vacated dirs and empty parents up to input roots
- --copy: List sources and worktrees, validate destinations, copy sources then worktrees, repair relations, report summary
- Skips repositories without remotes, detached HEAD worktrees, or already at target
A minimal config file looks like:
# Folder where all sources are checked out with gwc. path(-2)/path(-1) generates 2-level subfolders based on repository URI. Like https://github.com/user/repo.git -> ~/Developer/sources/user/repo
default_sources: ~/Developer/other/sources/path(-2)/path(-1)
# Folder where all worktrees are checked out with gwa. norm_branch() works better with remote branches, e.g. origin/remote-branch -> origin-remote-branch
default_worktrees: ~/Developer/other/worktrees/path(-2)/path(-1)/norm_branch()The generated file will have more options commented out, including the functions reference.
Useful to separate e.g. open source projects (where you learn or get inspired) from your work projects.
# Still needed in case the config fails to find a section. You may prefer a non-nested sources structure, but make sure the result folder is unique
default_sources: ~/Developer/sources/host()-path(-2)-path(-1)
default_worktrees: ~/Developer/worktrees/host()-path(-2)-path(-1)-norm_branch()
sources:
# ... other rules
work:
when: "your.org.host" in host()
sources: ~/Developer/work/sources/path(-2)-path(-1)
worktrees: ~/Developer/work/sources/path(-2)-path(-1)-norm_branch()
That's enough to separate work sources from all others, but you can create more sections with various rules. The library uses simpleeval to evaluate templates, so you can use its operators and functions below to get necessary routing.
| Function | Description | Example |
|---|---|---|
uri() |
Get full URI string | uri() β "https://loca-repo-manager.com:8081/user/repo.git" |
host() |
Get URI hostname | host() β "loca-repo-manager.com" |
port() |
Get URI port (empty string if not specified) | port() β "8081" or "" usually |
protocol() |
Get URI protocol/scheme | protocol() β "https" / "ssh" / git |
path(n) |
Get URI path segment by index (0-based, negative for reverse) | path(-1) β "repo", path(0) β "user" |
| Function | Description | Example |
|---|---|---|
branch() |
Get current branch name | branch() β "feature/new/ui" |
norm_branch(replacement) |
Branch name with / replaced (default: "-") |
norm_branch() β "feature-new-ui", norm_branch("_") β "feature_new_ui" |
Need to checkout temporary projects separately? Add this to your config:
sources:
# ... other rules
temp:
when: tag_exist("temp") # See [tags section](#-tags) for details about tags
sources: ~/Downloads/temp/sources/time_id()-host()-path(-2)-path(-1)
worktrees: ~/Downloads/temp/worktrees/time_id()-host()-path(-2)-path(-1)-norm-branch()time_id(fmt) generates a datetime-based identifier (cached per template evaluation). Default format is "20260120-2134.03" (short, seconds accuracy unique). Use format codes for more detailed/nested results. Works properly if used multiple times.
worktrees: ~/Downloads/temp/worktrees/time_id("%Y")/time_id("%m")/time_id("%H-%M$.%S")/host()-path(-2)-path(-1)-norm-branch()Generates nested structure: YYYY/HH-MM.ss/host()-path(-2)-path(-1)-norm-branch()
Run actions after checking out a repository or adding a worktree. Common example: copying local.properties for Gradle projects.
actions:
- when: file_exists("settings.gradle") # Check if it's actually a Gradle project
after_clone:
- abs_copy: ["~/sources/default-local.properties", "local.properties"] # Copies your default file right after cloning the repo
after_add:
- rel_copy: ["local.properties"] # Inherit existing repository file to worktreeYou can have multiple when subsections in actions. After clone/add, the library goes top-to-bottom and executes all actions with matching when conditions.
Other functions available in the actions section:
| Action | Description | Example |
|---|---|---|
abs_copy |
Copy file from absolute path to relative destination in target directory | abs_copy: ["~/sources/default-local.properties", "local.properties"] |
rel_copy |
Copy file from source repository to worktree (relative paths) | rel_copy: ["local.properties"] or rel_copy: ["config.template", "config"] |
command |
Execute external command (runs in destination directory, template functions available) | command: "npm install" or command: "claude init" |
| Function | Description | Example |
|---|---|---|
source_path() |
Get absolute path to source repository or worktree root | source_path() β "/path/to/repo" |
dest_path() |
Get absolute path to destination (clone target or worktree) | dest_path() β "/path/to/worktree" |
file_exists(path) |
Check if file exists relative to source repository | file_exists("local.properties") β True |
dir_exists(path) |
Check if directory exists relative to source repository | dir_exists("config") β True |
path_exists(path) |
Check if path exists (file or directory) relative to source repository | path_exists("local.properties") β True |
Still not flexible enough? Here comes tags. Tags specified using command line param -t <tag-name>[=optional value] (or --tag) for clone / add commands. Tags available in configuration with:
| Function | Description | Example |
|---|---|---|
tag(name) |
Get tag value by name (returns empty string if not set) | tag("env") β "prod" |
tag_exist(name) |
Check if tag exists (returns boolean) | tag_exist("env") β True |
π·οΈ Tag Usage Example:
sources:
# Temporary checkout: Clone repositories to ~/Downloads/temp for quick access
# Usage: gwc <uri> -t temp
temp:
when: 'tag_exist("temp")'
sources: ~/Downloads/temp/time_id()-host()-path(-1)
worktrees: ~/Downloads/temp/time_id()-host()-path(-1)/norm_branch()
# Code review worktrees: Add worktrees to ~/Developer/worktree/code-review for review tasks
# Usage: gwa <branch> --tag review
review:
when: 'tag_exist("review")'
worktrees: ~/Developer/review/worktree/path(-1)/norm_branch()
# If used during clone, default source path is used
```bash
# Clone to temporary location
gwc https://github.com/user/repo.git -t temp
# Output: ~/Downloads/temp/repo
# Add worktree for code review
cd ~/Developer/sources/github/user/repo
gwa feature-branch --tag review
# Output: ~/Developer/worktree/code-review/repo/feature-branch
| Command | Description |
|---|---|
gwc <uri> [--tag key=value]... |
π₯ Clone repository to configured location (tags available in templates/conditions) |
gwa <branch> [-c] [--tag key=value]... |
β Add worktree for branch (optionally create branch, tags available in templates/conditions) |
gwr <branch|path> [-f] |
β Remove worktree |
gww pull |
π Update source repository (works from worktrees if source is clean and on main/master) |
gww migrate <path>... [--dry-run] [--copy | --inplace] |
π Migrate repositories to new locations |
gww init config |
βοΈ Create default configuration file |
gww init shell <shell> |
π Install shell completion (bash/zsh/fish) |
Note: gwc, gwa, and gwr are convenient shell aliases for gww clone, gww add, and gww remove respectively. They provide the same functionality with automatic navigation prompts. Install them with gww init shell <shell>.
Common Options:
--tag,-t: Tag in the formatkey=valueor justkey(can be specified multiple times).
# Re-run the install command to update to the latest version
uv tool install "git+https://github.com/vadimvolk/git-worktree-wrapper.git"
# Or use the update command (if available)
uv tool update gwwpipx upgrade gwwpython -m pip install --upgrade gwwuv tool uninstall gwwpipx uninstall gwwpython -m pip uninstall gww# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov
# Run only unit tests
uv run pytest tests/unit/
# Run only integration tests
uv run pytest tests/integration/uv run mypy src/gwwMIT