g-commit-push.ps1

param(
    [Parameter(ValueFromPipeline)]
    [string]$Message,
    [switch]$Amend,
    [string[]]$Include,
    [string[]]$Exclude
)

begin {
    . (Join-Path $PSScriptRoot 'g-error-vectors.ps1')
}

process {
    $repo = Get-Location

    $branch = git -C $repo branch --show-current 2>$null
    if (-not $branch) { Write-Host "not a git repo"; exit 1 }

    $cfg = Get-GitboxConfig -RepoPath $repo
    if ($branch -eq $cfg.BaseBranch) {
        Write-Host "commit-abort: on base branch '$branch' -- create a feature branch first: gitbox b ""feat/name"""
        exit 1
    }
    if ($cfg.Upstream) {
        $originUrl = git -C $repo remote get-url origin 2>$null
        if ($originUrl -and $originUrl.Contains($cfg.Upstream)) {
            Write-Host "fork guard: origin points to upstream '$($cfg.Upstream)' -- reconfigure origin to your fork"
            exit 1
        }
    }

    # must run before git add -A; staged files cannot be selectively removed without resetting the index
    $pending = git -C $repo status --porcelain 2>$null | Where-Object { $_ } | ForEach-Object { $_.Substring(3) }
    $blocked = $pending | Where-Object { $_ -match $SecretPattern }
    if ($blocked) {
        Write-Host "secret guard: blocked -- remove these files before committing:"
        $blocked | ForEach-Object { Write-Host " $_" }
        exit 1
    }

    $allExcludes = @()
    if ($Exclude)                         { $allExcludes += $Exclude }
    if ($cfg.NeverStage -and $cfg.NeverStage.Count -gt 0) { $allExcludes += $cfg.NeverStage }

    if ($Include) {
        $addOut = git -C $repo add -- @Include 2>&1
    } else {
        $addOut = git -C $repo add -A 2>&1
        if ($LASTEXITCODE -eq 0 -and $allExcludes.Count -gt 0) {
            foreach ($pat in $allExcludes) {
                git -C $repo restore --staged -- $pat 2>$null | Out-Null
            }
        }
    }
    if ($LASTEXITCODE -ne 0) { Write-Host "stage failed"; if ($VerbosePreference -ne 'SilentlyContinue') { $addOut | ForEach-Object { Write-Host " $_" } }; exit 1 }
    $staged = (git -C $repo diff --cached --name-only 2>$null | Measure-Object -Line).Lines
    if ($staged -eq 0) { Write-Host "nothing to commit"; exit 0 }

    if (-not $Message) {
        $cfg = Get-GitboxConfig -RepoPath $repo
        if ($cfg.Editor) {
            $Message = Invoke-GitboxEditor -Template "# Commit message (first line is subject)`n# Lines starting with # are stripped"
            if (-not $Message) { Write-Host "no commit message; aborting"; exit 1 }
        } else {
            try {
                $Message = Read-Host " commit message"
            } catch {
                Write-Host "no commit message; pass a message or enable Editor in .gitbox.json"
                exit 1
            }
            if (-not $Message) { Write-Host "no commit message; aborting"; exit 1 }
        }
    }

    if ($Amend) {
        $commitOut = if ($Message) {
            git -C $repo commit --amend -m $Message 2>&1
        } else {
            git -C $repo commit --amend --no-edit 2>&1
        }
        if ($LASTEXITCODE -ne 0) { Write-Host "amend failed"; if ($VerbosePreference -ne 'SilentlyContinue') { $commitOut | ForEach-Object { Write-Host " $_" } }; exit 1 }
        $sha = git -C $repo rev-parse --short HEAD 2>$null

        $pushOut = git -C $repo push -u origin $branch --force-with-lease 2>&1
        if ($LASTEXITCODE -ne 0) {
            Write-Host "push failed: origin/$branch"
            if ($VerbosePreference -ne 'SilentlyContinue') { $pushOut | ForEach-Object { Write-Host " $_" } }
            exit 1
        }

        Write-Host "staged $staged |amended $sha |pushed origin/$branch (force)"
    } else {
        $commitOut = git -C $repo commit -m $Message 2>&1
        if ($LASTEXITCODE -ne 0) { Write-Host "commit failed"; if ($VerbosePreference -ne 'SilentlyContinue') { $commitOut | ForEach-Object { Write-Host " $_" } }; exit 1 }
        $sha = git -C $repo rev-parse --short HEAD 2>$null

        $pushOut = git -C $repo push -u origin $branch 2>&1
        if ($LASTEXITCODE -ne 0) {
            Write-Host "push failed: origin/$branch"
            if ($VerbosePreference -ne 'SilentlyContinue') { $pushOut | ForEach-Object { Write-Host " $_" } }
            exit 1
        }

        Write-Host "staged $staged |committed $sha |pushed origin/$branch"
    }
    exit 0
}