Private/GitHelpers.ps1

<#
.SYNOPSIS
    Git helper functions for release validation.
#>


function Test-GitRepository {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [string]$Path = (Get-Location).Path
    )

    try {
        $null = git -C $Path rev-parse --git-dir 2>$null
        return $LASTEXITCODE -eq 0
    }
    catch {
        return $false
    }
}

function Get-CurrentBranch {
    [CmdletBinding()]
    [OutputType([string])]
    param()

    $branch = git rev-parse --abbrev-ref HEAD 2>$null
    if ($LASTEXITCODE -ne 0) {
        throw "Failed to get current branch"
    }
    return $branch
}

function Test-WorkingDirectoryClean {
    [CmdletBinding()]
    [OutputType([bool])]
    param()

    $status = git status --porcelain 2>$null
    return [string]::IsNullOrWhiteSpace($status)
}

function Test-BranchUpToDate {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param()

    # Fetch latest from remote (silently)
    git fetch --quiet 2>$null

    $branch = Get-CurrentBranch
    $remote = "origin/$branch"

    # Check if remote tracking branch exists
    $remoteExists = git rev-parse --verify $remote 2>$null
    if ($LASTEXITCODE -ne 0) {
        return [PSCustomObject]@{
            IsUpToDate = $true
            AheadBy    = 0
            BehindBy   = 0
            Message    = "No remote tracking branch (new branch)"
        }
    }

    $ahead = (git rev-list "$remote..HEAD" --count 2>$null) -as [int]
    $behind = (git rev-list "HEAD..$remote" --count 2>$null) -as [int]

    $isUpToDate = ($ahead -eq 0) -and ($behind -eq 0)
    $message = if ($isUpToDate) { "Up to date with remote" }
    elseif ($ahead -gt 0 -and $behind -gt 0) { "Diverged: $ahead ahead, $behind behind" }
    elseif ($ahead -gt 0) { "$ahead commit(s) ahead of remote" }
    else { "$behind commit(s) behind remote" }

    return [PSCustomObject]@{
        IsUpToDate = $isUpToDate
        AheadBy    = $ahead
        BehindBy   = $behind
        Message    = $message
    }
}

function Get-LatestGitTag {
    [CmdletBinding()]
    [OutputType([string])]
    param()

    $tag = git describe --tags --abbrev=0 2>$null
    if ($LASTEXITCODE -ne 0) {
        return $null
    }
    return $tag
}