public/New-WtwWorktree.ps1
|
function New-WtwWorktree { <# .SYNOPSIS Create a new git worktree with workspace and color assignment. .DESCRIPTION Creates a git worktree for the given task, generates a VS Code workspace file from the repo template, assigns a unique color, and registers it in the wtw registry. .PARAMETER Task Branch or task name for the new worktree. .PARAMETER Branch Override the git branch name (defaults to the task name). .PARAMETER Repo Target repo alias if not auto-detected from cwd. .PARAMETER Open Open the workspace in the configured editor after creation. .PARAMETER NoBranch Attach to an existing branch instead of creating a new one. .PARAMETER PrettyName Human-readable display name stored in the registry and used as the Superset workspace name (e.g. "035 Context Building 🔵"). .PARAMETER FolderName Override the worktree folder suffix (default: $Task). Final folder is "${repoName}_${FolderName}". Useful for long branch names: --folder p2 → snowmain1_p2. .PARAMETER Color Color assignment for the new workspace. Accepts: - 'random' (default when omitted): max-contrast pick from the palette - '#rrggbb' or 'rrggbb': literal hex - color name: looked up in the bundled colornames table (case-insensitive, spaces/hyphens ignored) — e.g. 'forest green', 'navy', 'sunset orange'. .EXAMPLE wtw create auth Create a worktree and branch named "auth" for the current repo. .EXAMPLE wtw create "my feature name" Normalizes to my_feature_name for branch, folder, and registry key. .EXAMPLE wtw create 035-context-building-function --name "035 Context Building 🔵" Creates the worktree and registers a pretty name used for Superset. #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string] $Task, [string] $Branch, [string] $Repo, [string] $PrettyName, [string] $FolderName, [string] $Color, [switch] $Open, [switch] $NoBranch ) $rawTask = $Task $Task = ConvertTo-WtwBranchSafeName -Name $Task if ([string]::IsNullOrWhiteSpace($Task)) { Write-Error "Task name is empty or invalid after normalization (input: '$rawTask')." return } if ($rawTask -ne $Task) { Write-Host " Normalized task/branch: $Task" -ForegroundColor DarkCyan Write-Host " (from: $rawTask)" -ForegroundColor DarkGray } $repoName, $repoEntry = Resolve-WtwRepo -RepoAlias $Repo if (-not $repoName) { return } if ($repoEntry.worktrees.PSObject.Properties.Name -contains $Task) { Write-Error "Worktree '$Task' already exists for $repoName. Use 'wtw go $Task' or 'wtw remove $Task' first." return } # Folder suffix (default: $Task). Normalized so spaces/casing don't sneak into paths. $folderSuffix = if ($FolderName) { ConvertTo-WtwBranchSafeName -Name $FolderName } else { $Task } # Reject a normalized empty suffix early — otherwise we'd build a path # like `${repoName}_` and silently collide with anything that already # uses the bare repo prefix. if ($FolderName -and [string]::IsNullOrWhiteSpace($folderSuffix)) { Write-Error "Folder name is empty or invalid after normalization (input: '$FolderName')." return } if ($FolderName -and $folderSuffix -ne $FolderName) { Write-Host " Normalized folder name: $folderSuffix" -ForegroundColor DarkCyan Write-Host " (from: $FolderName)" -ForegroundColor DarkGray } $worktreePath = Join-Path $repoEntry.worktreeParent "${repoName}_${folderSuffix}" if (Test-Path $worktreePath) { Write-Error "Path already exists: $worktreePath" return } if (-not $Branch) { $Branch = $Task } # Create git worktree Write-Host " Creating worktree..." -ForegroundColor Cyan $mainRepo = $repoEntry.mainPath if ($NoBranch) { $result = git -C $mainRepo worktree add $worktreePath $Branch 2>&1 } else { $result = git -C $mainRepo worktree add -b $Branch $worktreePath 2>&1 } if ($LASTEXITCODE -ne 0) { Write-Error "git worktree add failed: $result" return } Write-Host " Worktree: $worktreePath" -ForegroundColor Green Write-Host " Branch: $Branch" -ForegroundColor Green # Pick color: explicit hex/name/random, else default to random max-contrast pick. $colorKey = "$repoName/$Task" if ($Color) { $color = Resolve-WtwColorInput -Color $Color -ExcludeKey $colorKey if (-not $color) { Write-Error "Invalid --color '$Color'. Use 'random', a hex (#rrggbb / rrggbb), or a known color name." git -C $mainRepo worktree remove $worktreePath --force 2>$null | Out-Null return } } else { $color = Resolve-WtwColorInput -Color 'random' -ExcludeKey $colorKey } # Persist assignment so colors.json and registry stay in sync $colorsState = Get-WtwColors $colorsState.assignments | Add-Member -NotePropertyName $colorKey -NotePropertyValue $color -Force Save-WtwColors $colorsState Write-Host " Color: $color" -ForegroundColor Green # Pretty name: default to the folder suffix (i.e. path without the `${repoName}_` prefix); # always prepend a color-circle emoji that matches the assigned color so it surfaces in # SourceGit/Superset and any other UI that reads `prettyName`. if (-not $PrettyName) { $PrettyName = $folderSuffix } $PrettyName = Format-WtwPrettyNameWithCircle -Hex $color -Name $PrettyName Write-Host " Pretty: $PrettyName" -ForegroundColor Green # Generate workspace file $wsFile = $null $config = Get-WtwConfig # Use template source (.template file) if available, fall back to templateWorkspace $templatePath = if ($repoEntry.template -and (Test-Path $repoEntry.template)) { $repoEntry.template } elseif ($repoEntry.templateWorkspace -and (Test-Path $repoEntry.templateWorkspace)) { $repoEntry.templateWorkspace } else { $null } if ($config -and $templatePath) { $wsDir = $config.workspacesDir.Replace('~', $HOME) $wsDir = [System.IO.Path]::GetFullPath($wsDir) $wsFile = Join-Path $wsDir "${repoName}_${folderSuffix}.code-workspace" New-WtwWorkspaceFile ` -RepoName $repoName ` -Name "${repoName}_${folderSuffix}" ` -CodeFolderPath $worktreePath ` -TemplatePath $templatePath ` -OutputPath $wsFile ` -Color $color ` -Branch $Branch ` -WorktreePath $worktreePath ` -Managed | Out-Null Write-Host " Workspace: $wsFile" -ForegroundColor Green } else { Write-Host ' Workspace: (no template configured, skipped)' -ForegroundColor Yellow } # Register in registry $registry = Get-WtwRegistry $wtEntry = [PSCustomObject]@{ path = $worktreePath branch = $Branch workspace = $wsFile color = $color created = (Get-Date -Format 'o') prettyName = $PrettyName supersetWorkspaceId = $null } $registry.repos.$repoName.worktrees | Add-Member -NotePropertyName $Task -NotePropertyValue $wtEntry -Force Save-WtwRegistry $registry # Create Superset workspace (no-op when CLI absent or project not found) $supersetWsId = New-WtwSupersetWorkspace -RepoName $repoName -Branch $Branch -PrettyName $PrettyName -MainRepoPath $registry.repos.$repoName.mainPath if ($supersetWsId) { $registry.repos.$repoName.worktrees.$Task.supersetWorkspaceId = $supersetWsId Save-WtwRegistry $registry } # Register in SourceGit's managed repository list (no-op when app absent). # Pass the assigned hex so SourceGit's Bookmark gets the nearest of its 7 palette slots. Add-WtwSourceGitRepository -Path $worktreePath -Name $PrettyName -Hex $color if ($Open) { Open-WtwWorkspace -Name $Task -Repo $repoName } Write-Host '' Write-Host " Done! Use 'wtw go $Task' to switch." -ForegroundColor Green } |