Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

git-worktree-tasks

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Quick Start

# Install latest release (run from your target folder)cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Create a task worktree (defaults to current branch)
gwtt create "my-feature"# List all worktrees
gwtt list
# Show status
gwtt status
# Cleanup when done
gwtt cleanup "my-feature"

Table of Contents


Installation

Option 1: Install Latest Release (Recommended)

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash
# Or with wget:
wget -qO- https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Run from the directory where you want gwtt to live:

cd$HOME/.local/bin
curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash

Custom install directory (default is current folder):

curl -fsSL https://raw.githubusercontent.com/pi2pie/git-worktree-tasks/main/scripts/install.sh | bash -s -- ~/.local/bin

Option 2: Using Makefile (Release Assets)

make install

Note: make install installs into the current directory (the repo root if you run it here).

Directly from the repo:

./scripts/install.sh

Option 3: Standard Go Install (Build From Source)

go install github.com/pi2pie/git-worktree-tasks@latest

Note:go install creates git-worktree-tasks. Release assets install gwtt and can optionally create a git-worktree-tasks symlink.

Option 4: Build Locally

make build
# Binaries in dist/

Uninstall

./scripts/uninstall.sh
# Or with Makefile:
make uninstall

If the binary is not in the current directory, pass the install path:

./scripts/uninstall.sh $HOME/.local/bin

Go install removal:

./scripts/go-uninstall.sh
# Or:
make go-uninstall

Requirements

  • curl or wget for release install
  • tar/unzip for release install archives
  • sha256sum or shasum for release checksum verification
  • Go 1.25.5+ for building from source
  • $GOPATH/bin in $PATH for go-install targets

Windows PATH Note: If you install gwtt.exe into a custom folder (e.g., C:\Users\<you>\bin), add that folder to your PATH and open a new terminal to pick it up.

Note

Ownership Change (v0.0.7+)

The repository ownership changed after v0.0.6. The old dev-pi2pie path no longer exists, so use the new module path for all installs and imports:

  • v0.0.7 and later:github.com/pi2pie/git-worktree-tasks

Binary Naming and Shell Configuration

  • Release assets ship the gwtt binary.
  • go install produces git-worktree-tasks.
  • If you prefer git-worktree-tasks, create an alias or symlink manually.

Set up the gwtt alias for convenience:

ShellConfig FileAlias Syntax
Bash~/.bashrcalias gwtt="git-worktree-tasks"
Zsh~/.zshrcalias gwtt="git-worktree-tasks"
Fish~/.config/fish/config.fishalias gwtt git-worktree-tasks

After adding, reload your shell (source ~/.bashrc, source ~/.zshrc, or exec fish).

Alternative: Create a symlink:

ln -s $(which git-worktree-tasks)$(dirname $(which git-worktree-tasks))/gwtt

Configuration

Precedence

Settings resolve in this order (highest precedence first):

  1. CLI flags (for example --theme, --mode / -m, --mask-sensitive-paths, --no-mask-sensitive-paths)
  2. Environment variables
  3. Project config (gwtt.config.toml or gwtt.toml in repo root)
  4. User config ($HOME/.config/gwtt/config.toml)
  5. Built-in defaults
# Environment variableexport GWTT_THEME=nord
# Disable color outputexport GWTT_COLOR=0
# Mode selectionexport GWTT_MODE=codex
# Dry-run path masking (1/true/on/yes to enable, 0/false/off/no to disable)export GWTT_DRY_RUN_MASK_SENSITIVE_PATHS=1
# List available themes
gwtt --themes

Theme Selection

[theme]
name = "nord"

Mode Selection

mode = "classic"# or "codex"

Other Defaults

Common defaults you can set once:

[ui]
color_enabled = true
[table]
grid = false
[dry_run]
mask_sensitive_paths = true# mask $HOME/%USERPROFILE% prefixes in --dry-run output
[list]
output = "table"field = "path"absolute_path = falsegrid = falsestrict = false
[status]
output = "table"absolute_path = falsegrid = falsestrict = false
[finish]
merge_mode = "ff"# ff, no-ff, squash, rebaseconfirm = true# set false to bypass prompts (same as --yes)cleanup = falseremove_worktree = falseremove_branch = falseforce_branch = false
[cleanup]
confirm = true# set false to bypass prompts (same as --yes)remove_worktree = trueremove_branch = trueworktree_only = falseforce_branch = false

[dry_run].mask_sensitive_paths defaults to true. Set it to false if you need raw absolute paths in --dry-run output.
When enabled, home-prefixed paths are rendered as $HOME/... on POSIX and %USERPROFILE%\\... on Windows. You can override this per-invocation with --mask-sensitive-paths=true|false, --no-mask-sensitive-paths, or via GWTT_DRY_RUN_MASK_SENSITIVE_PATHS. For bool flags, prefer --mask-sensitive-paths=false (with =) rather than --mask-sensitive-paths false.

Config File Location

Project: gwtt.config.toml or gwtt.toml in the repo root
User: $HOME/.config/gwtt/config.toml

Minimal config:

[theme]
name = "nord"

Usage Guide

Commands Overview

CommandAliasDescription
applyApply non-destructive changes between Codex worktree and local checkout (codex mode only)
overwriteDestructively replace destination with source changes in codex mode
createCreate a worktree and branch for a task
listlsList task worktrees
statusShow detailed worktree status
finishMerge a task branch into target
cleanuprmRemove a task worktree and/or branch

Creating Worktrees

# Basic usage (defaults to current branch)
gwtt create "my-task"# Explicit base override
gwtt create "my-task" --base main
# Reuse existing worktree (no error if exists)
gwtt create "my-task" --skip-existing
# Custom path
gwtt create "my-task" --path ../custom-location
# Preview without executing
gwtt create "my-task" --dry-run

Flags:

FlagShortDescription
--baseBase branch to create from (default: current branch)
--path-pOverride worktree path
--output-oOutput format: text, raw
--skip-existing--skipReuse existing worktree
--dry-runShow git commands without executing

Notes:

  • The default base is the current local branch (for example main, master, or dev).
  • If you are in a detached HEAD state, you must pass --base explicitly.

Listing Worktrees

# List all worktrees
gwtt list
# Filter by task name (contains match)
gwtt list "my-task"# Exact match
gwtt list "my-task" --strict
# Filter by branch
gwtt list --branch feature-branch
# Custom path layout still searchable by task name
gwtt create new-task -p ./.worktrees/new-task
gwtt list new-task -o raw
# Show absolute paths
gwtt list --abs
# Grid borders in table
gwtt list --grid
# Codex mode: list Codex-managed worktrees for this repo
gwtt --mode codex list

Flags:

FlagShortDescription
--output-oFormat: table, json, csv, raw
--field-fRaw output field: path, task, branch
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • list <task> first resolves task names from <repo>_<task> paths.
  • If the path does not match that convention, task lookup falls back to branch-backed inference for non-main, non-detached worktrees.
  • --branch remains the explicit/authoritative branch filter.

Note

If you place worktrees under a nested path inside the main repo (for example, ./.worktrees/<task>), add that root path (for example, .worktrees/) to .gitignore in the main checkout.

Checking Status

# Show status of all worktrees
gwtt status
# Filter by task
gwtt status "my-task"# Compare against specific target branch
gwtt status --target main
# Filter by exact task name
gwtt status --task "my-task"# Codex mode: show Codex-managed worktree status
gwtt --mode codex status

Status columns: Task, Branch, Path, Modified Time (RFC3339 UTC), Base, Target, Last Commit, Dirty, Ahead, Behind

Flags:

FlagShortDescription
--output-oFormat: table, json, csv
--targetTarget branch for ahead/behind comparison
--taskFilter by task name (enables strict match)
--branchFilter by branch name
--absolute-path--absShow absolute paths
--strictRequire exact task match
--gridRender table with grid borders

Task lookup behavior (classic mode):

  • status <task> uses the same task resolution as list <task> (path-first, then branch-backed fallback for eligible rows).
  • --branch remains the explicit/authoritative branch filter.

Finishing Tasks

# Merge task branch into target
gwtt finish "my-task" --target main
# Merge with cleanup (remove worktree + branch)
gwtt finish "my-task" --target main --cleanup
# Merge strategies
gwtt finish "my-task" --no-ff # No fast-forward
gwtt finish "my-task" --squash # Squash commits
gwtt finish "my-task" --rebase # Rebase before merge# Skip confirmation
gwtt finish "my-task" --cleanup --yes

Flags:

FlagDescription
--targetTarget branch (default: current branch)
--cleanupRemove worktree and branch after merge
--remove-worktreeRemove only the worktree after merge
--remove-branchRemove only the branch after merge
--force-branchForce delete branch (-D instead of -d)
--no-ffUse --no-ff merge
--squashUse --squash merge
--rebaseRebase task branch onto target first
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Applying Changes (Codex Mode)

# Non-destructive apply (default direction: worktree -> local)
gwtt --mode codex apply <opaque-id># Reverse non-destructive apply (local -> worktree)
gwtt --mode codex apply <opaque-id> --to worktree
# Destructive overwrite (requires confirmation unless --yes)
gwtt --mode codex overwrite <opaque-id> --to local
gwtt --mode codex overwrite <opaque-id> --to worktree --yes
# Compatibility alias for overwrite
gwtt --mode codex apply <opaque-id> --to worktree --force --yes
# Preview with structured plan + command echo
gwtt --mode codex apply <opaque-id> --dry-run

Notes:

  • In codex mode, <opaque-id> is the directory directly under $CODEX_HOME/worktrees.
  • apply is non-destructive and will not switch direction automatically on conflict.
  • On conflict, apply exits with a next-step hint for overwrite --to ....
  • overwrite resets/cleans the destination before transfer and is destructive by design.
  • --dry-run prints plan, preflight, and actions sections, then echoes the underlying git/copy operations.

Cleanup

# Remove worktree and branch (with confirmation)
gwtt cleanup "my-task"# Remove only the worktree (keep branch)
gwtt cleanup "my-task" --worktree-only
# Force delete branch
gwtt cleanup "my-task" --force-branch
# Skip confirmation
gwtt cleanup "my-task" --yes
# Preview without executing
gwtt cleanup "my-task" --dry-run
# Codex mode: remove a Codex-managed worktree by opaque id
gwtt --mode codex cleanup <opaque-id>

Flags:

FlagDescription
--remove-worktreeRemove the task worktree (default: true)
--remove-branchRemove the task branch (default: true)
--worktree-onlyRemove only worktree, keep branch
--force-branchForce delete branch (-D)
--yesSkip confirmation prompts
--dry-runShow git commands without executing

Output Formats & Piping

The --output (-o) and --field (-f) flags enable powerful shell integrations.

Output Formats

FormatDescriptionAvailable In
tableHuman-readable table (default)list, status
jsonJSON arraylist, status
csvCSV with headerslist, status
rawSingle value, no decorationcreate, list
textStyled text output (default)create

Field Selection (for --output raw)

When using --output raw with list, specify which field to output:

FieldDescription
pathWorktree path (default)
taskTask name
branchBranch name

Piping Examples

Navigate to a worktree

# Change to task worktree directorycd"$(gwtt list my-task -o raw)"# Or using createcd"$(gwtt create my-task -o raw)"

Copy to clipboard

# Copy worktree path
gwtt list my-task -o raw | pbcopy # macOS
gwtt list my-task -o raw | xclip -selection clipboard # Linux# Copy task name
gwtt list my-task -o raw -f task | pbcopy
# Copy branch name
gwtt list my-task -o raw -f branch | pbcopy

Open in editor

# Open worktree in VS Code
code "$(gwtt list my-task -o raw)"# Open Worktree in Zed
zed "$(gwtt list my-task -o raw)"# Open in Cursor
cursor "$(gwtt list my-task -o raw)"

Scripting workflows

# Create and open in one command
code "$(gwtt create my-feature -o raw)"# List all branches as plain text
gwtt list -o json | jq -r '.[].branch'# Get paths for all worktrees
gwtt list -o json | jq -r '.[].path'# Filter dirty worktrees
gwtt status -o json | jq '.[] | select(.dirty == true)'# Count worktrees ahead of target
gwtt status -o json | jq '[.[] | select(.ahead > 0)] | length'

Shell function examples

# Fish: Create and cd to worktreefunctiongwtt-newset path (gwtt create $argv[1] -o raw)
and cd$path
end
# Bash/Zsh: Create and cd to worktreegwtt-new() {
local path
path=$(gwtt create "$1" -o raw)&&cd"$path"
}

Raw Output Fallback

When using --output raw with list:

  • If a matching worktree row exists (including custom path layouts), raw output uses that row.
  • If no matching worktree exists but the branch does, raw output falls back to a synthetic main-worktree row.
  • Fallback row output respects --field:
    • path -> main worktree path
    • branch -> fallback branch name
    • task -> -
  • Requires either a task filter or --branch flag.
  • --branch remains the explicit/authoritative branch selector.
# Returns path even if no worktree exists (fallback to main repo)
gwtt list feature-branch -o raw
# Field-aware fallback output
gwtt list feature-branch -o raw -f branch

Development

Build Targets

make build # Build binaries to dist/
make install # Install gwtt from latest release
make uninstall # Remove release install
make go-install # Install to $GOPATH/bin
make go-uninstall # Remove installed binaries
make clean # Remove dist/
make help# Show all targets

Testing and Linting

go test ./...
golangci-lint run

Project Structure

├── main.go # Entry point
├── cli/ # CLI command definitions
├── internal/ # Internal packages (config, git, worktree)
├── ui/ # UI/styling utilities
├── tui/ # Terminal UI components (preview)
├── examples/ # Example configs and shell functions
├── scripts/ # Installation scripts
├── docs/ # Documentation and plans
├── Makefile # Build targets
└── go.mod # Go module definition

Troubleshooting

$GOPATH/bin not in $PATH

# Check if it's in PATHecho$PATH| grep $(go env GOPATH)/bin
# Add to shell config if missingexport PATH="$(go env GOPATH)/bin:$PATH"

Permission Denied

# Use custom directory
./scripts/install.sh $HOME/.local/bin

Shell Alias Not Working

Reload your shell after adding the alias:

source~/.bashrc # Bashsource~/.zshrc # Zshexec fish # Fish

License

This project is licensed under the MIT License — see the LICENSE file for details.


Notes

  • Default worktree path: ../<repo>_<task>
  • Task names are slugified (lowercase, hyphens replace spaces)
  • Paths are relative by default; use --abs for absolute
  • Use --dry-run to preview git commands
  • Global flags: --mode (-m), --theme, --nocolor, --themes

About

A small CLI to manage task-based Git worktrees with predictable naming and cleanup flows.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages