Public/New-WorkBranch.ps1

function New-WorkBranch {
    <#
    .SYNOPSIS
    Start a new working area for an isolated task, fix, or doc change.
 
    .DESCRIPTION
    New-WorkBranch creates a new working area starting from the current point and switches to it. Use it when starting a new feature, a bug fix, or a documentation pass that should not mix with the active working area.
 
    The command refuses to start a new working area when an unfinished merge, rebase, cherry-pick, revert, or bisect is in progress, or when there are unfinished conflicts. It also rejects names that are not valid working-area identifiers (spaces, control characters, leading dashes, and similar) and refuses to overwrite a working area that already exists.
 
    Each invocation writes a self-contained diagnostic log file. Successful runs log silently; failures throw a plain-English message and point at the log file with the technical detail.
 
    .PARAMETER Name
    The name of the new working area. Must be a valid identifier (no spaces, no control characters, no leading dash, etc.).
 
    .PARAMETER LogPath
    Override the directory where the diagnostic log for this run is written. Defaults to %LOCALAPPDATA%\GitEasy\Logs.
 
    .EXAMPLE
    New-WorkBranch -Name fix-readme
 
    .EXAMPLE
    Find-CodeChange; New-WorkBranch -Name docs-refresh
 
    .NOTES
    Safety:
    - Refuses to run during an unfinished merge, rebase, cherry-pick, revert, or bisect.
    - Refuses to run while there are unfinished conflicts.
    - Refuses to overwrite an existing working area.
    - Validates the name through git check-ref-format.
 
    .LINK
    Switch-Work
 
    .LINK
    Find-CodeChange
 
    .LINK
    Save-Work
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Name,

        [Parameter()]
        [string]$LogPath = ''
    )

    $repoRoot = $null
    try {
        $rootProbe = Invoke-GEGit -ArgumentList @('rev-parse', '--show-toplevel') -AllowFailure
        if ($rootProbe.ExitCode -eq 0) {
            $repoRoot = $rootProbe.Output | Select-Object -First 1
        }
    }
    catch {
        $repoRoot = $null
    }

    $session = Start-GELogSession -Command 'New-WorkBranch' -Repository ([string]$repoRoot) -LogPath $LogPath

    $userMessageOnFailure = "Could not start the new working area '$Name'."

    try {
        Assert-GESafeSave -Path ([string]$repoRoot) -LogPath $session.Path | Out-Null

        if (-not $repoRoot) {
            $rootResult = Invoke-GEGit -ArgumentList @('rev-parse', '--show-toplevel') -LogPath $session.Path
            $repoRoot = $rootResult.Output | Select-Object -First 1
        }

        $checkRef = Invoke-GEGit -ArgumentList @('check-ref-format', '--branch', $Name) -WorkingDirectory $repoRoot -LogPath $session.Path -AllowFailure
        if ($checkRef.ExitCode -ne 0) {
            throw "'$Name' is not a valid working-area name. Use letters, digits, dashes, slashes, underscores, and dots."
        }

        $existCheck = Invoke-GEGit -ArgumentList @('rev-parse', '--verify', '--quiet', "refs/heads/$Name") -WorkingDirectory $repoRoot -LogPath $session.Path -AllowFailure
        if ($existCheck.ExitCode -eq 0) {
            throw "A working area named '$Name' already exists. Use Switch-Work to move into it, or pick a different name."
        }

        if (-not $PSCmdlet.ShouldProcess($repoRoot, "Create working area '$Name'")) {
            Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS' -UserMessage 'Skipped (WhatIf).'
            return
        }

        Invoke-GEGit -ArgumentList @('checkout', '-b', $Name) -WorkingDirectory $repoRoot -LogPath $session.Path | Out-Null

        Write-Host "Started new working area '$Name'."

        $result = [PSCustomObject]@{
            Repository = $repoRoot
            Branch     = $Name
            Message    = "Working area '$Name' created and active."
        }

        Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS'
        return $result
    }
    catch {
        $err = $_

        $innerMessage = $err.Exception.Message
        if ($innerMessage -like 'git *') {
            $finalMsg = $userMessageOnFailure
        }
        else {
            $finalMsg = $innerMessage
        }

        Complete-GELogSession -Path $session.Path -Outcome 'FAILURE' -UserMessage $finalMsg -ErrorMessage $innerMessage

        throw "$finalMsg Details: $($session.Path)"
    }
}