Scripts/Public/Remove-GitBranches.ps1

function Remove-GitBranches {
    param(
        [switch]$Help
    )

    if ($Help) {
        Write-Host "`nRemove-GitBranches:" -ForegroundColor Cyan
        Write-Host "A command for safely deleting local Git branches which have been deleted from remote repository." -ForegroundColor White
        Write-Host "`nUSAGE:" -ForegroundColor Yellow
        Write-Host " Remove-GitBranches" -ForegroundColor White
        Write-Host "`nOPTIONS:" -ForegroundColor Yellow
        Write-Host " -Help Displays this help message." -ForegroundColor White
        Write-Host "`nDESCRIPTION:" -ForegroundColor Yellow
        Write-Host " This command deletes local Git branches that no longer have a remote counterpart (except for the main and master branches)."
        Write-Host "`nBEHAVIOR:" -ForegroundColor Yellow
        Write-Host " - Prompts the user before deleting branches without remotes."
        Write-Host " - Automatically switches to 'main' or 'master' branch after execution if necessary." -ForegroundColor White
        return
    }

    if ((git rev-parse --is-inside-work-tree) -ne $true) {
        Write-Host "Not inside a Git repository" -ForegroundColor Red
        return
    }

    # Ensure UTF-8 during execution of the command
    $originalEncodingOfUser = [Console]::OutputEncoding
    [Console]::OutputEncoding = [System.Text.UTF8Encoding]::UTF8

    try {
        Invoke-GitCultureInvariant -args @('fetch','-p')

        $currentBranch = (git symbolic-ref --short -q HEAD).Trim()

        $masterBranch =
        if (branchExists 'master') { 'master' }
        elseif (branchExists 'main') { 'main' }

        $masterBranchExists = $null -ne $masterBranch

        # Check if current branch has a remote and if user wants to delete the current branch
        if (-not (hasRemote $currentBranch) -and $masterBranchExists -and $currentBranch -ne $masterBranch) {
            $deleteCurrent = (Read-Host "Current local branch '$currentBranch' has no remote; delete this and switch to '$masterBranch'? (Y/n)") -ne 'n'
        }

        $shouldSwitchBranch = $deleteCurrent -and $currentBranch -ne $masterBranch

        if ($shouldSwitchBranch -and -not $masterBranchExists) {
            Write-Host "Neither 'main' nor 'master' branches exist. Cannot switch branches after deleting current." -ForegroundColor Red
            return
        }

        $localBranches = (Invoke-GitCultureInvariant -args @('branch')).ForEach({ $_.Trim().Replace('* ','') })
        $remoteBranches = (Invoke-GitCultureInvariant -args @('branch','-r')).ForEach({ $_.Trim().Replace('origin/','') })

        $branchesToDelete = $localBranches | Where-Object {
            $branchHasNoRemote = $_ -notin $remoteBranches
            $branchIsNotMaster = $_ -ne 'master' -and $_ -ne 'main'
            $branchShouldBeDeletedIfCheckedOut = ($_ -ne $currentBranch) -or $deleteCurrent

            $branchHasNoRemote -and $branchIsNotMaster -and $branchShouldBeDeletedIfCheckedOut
        }

        if (-not $branchesToDelete) {
            Write-Host "No local branches to delete" -ForegroundColor Cyan
            return
        }

        Write-Host "The following local Git branches will be deleted:" -ForegroundColor Yellow
        $branchesToDelete | ForEach-Object { Write-Host " - $_" -ForegroundColor Magenta }

        if ((Read-Host "Delete these local branches? (Y/n)") -eq 'n') {
            Write-Host "No local branches were deleted" -ForegroundColor Cyan
            return
        }

        if ($shouldSwitchBranch) {
            Invoke-GitCultureInvariant -args @('checkout',$masterBranch)
        }

        $branchesToDelete | ForEach-Object {
            Invoke-GitCultureInvariant -args @('branch','-D',$_)
        }

        Write-Host "The local branches were deleted successfully" -ForegroundColor Green
    }
    finally {
        # Restore the user's original encoding
        [Console]::OutputEncoding = $originalEncodingOfUser
    }
}