Created
August 6, 2025 12:47
-
-
Save readingdancer/7204604171a5f44f8e4c2c1d1af2b8d8 to your computer and use it in GitHub Desktop.
A PowerShell script to clean up all old merged in branches both locally and on the origin server.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # First run this script with the test parameter to see what it plans to delete | |
| # .\Clean-GitBranches.ps1 -Test | |
| param( | |
| [string]$TopBranch = $(git rev-parse --abbrev-ref HEAD), | |
| [switch]$Test | |
| ) | |
| Write-Host "`nπ Scanning for branches merged into '$TopBranch'..." -ForegroundColor Cyan | |
| # Fetch latest from remote | |
| git fetch --prune | |
| # Checkout the top branch to ensure weβre comparing correctly | |
| git checkout $TopBranch | |
| # Get merged branches (excluding current and protected ones) | |
| $mergedBranches = git branch --merged | ForEach-Object { | |
| $_.Trim().TrimStart('*').Trim() | |
| } | | |
| Where-Object { $_ -ne $TopBranch -and $_ -ne 'main' -and $_ -ne 'master' } | |
| foreach ($branch in $mergedBranches) { | |
| $isRemote = $false | |
| $isLocal = $true | |
| # Check if the remote branch exists | |
| git ls-remote --exit-code --heads origin $branch 2>$null | Out-Null | |
| if ($LASTEXITCODE -eq 0) { | |
| $isRemote = $true | |
| } | |
| if ($Test) { | |
| $localMark = if ($isLocal) { "π’" } else { "βͺ" } | |
| $remoteMark = if ($isRemote) { "π" } else { "βͺ" } | |
| Write-Host "$localMark$remoteMark $branch" -ForegroundColor Gray | |
| } else { | |
| Write-Host "π§Ή Deleting local branch: $branch" -ForegroundColor Yellow | |
| git branch -d $branch | |
| if ($isRemote) { | |
| Write-Host "π Deleting remote branch: origin/$branch" -ForegroundColor Magenta | |
| git push origin --delete $branch | |
| } else { | |
| Write-Host "β οΈ Remote branch 'origin/$branch' does not exist or already deleted." -ForegroundColor DarkGray | |
| } | |
| } | |
| } | |
| if ($Test) { | |
| Write-Host "`nβ Test complete. No branches were deleted." -ForegroundColor Green | |
| } else { | |
| Write-Host "`nβ Local Cleanup complete." -ForegroundColor Green | |
| } | |
| Write-Host "`nπ Scanning for remote branches with no local counterpart..." -ForegroundColor Cyan | |
| # Get all remote branches (without origin/) | |
| $remoteBranches = git branch -r | ForEach-Object { | |
| $_.Trim() | |
| } | Where-Object { | |
| ($_ -notmatch '->') -and ($_ -ne 'origin/HEAD') | |
| } | ForEach-Object { | |
| $_ -replace '^origin/', '' | |
| } | |
| # Get all local branches | |
| $localBranches = git branch | ForEach-Object { | |
| $_.Trim().TrimStart('*').Trim() | |
| } | |
| # Find remote branches that don't exist locally | |
| $remoteOnlyBranches = $remoteBranches | Where-Object { | |
| $localBranches -notcontains $_ | |
| } | |
| if ($remoteOnlyBranches.Count -eq 0) { | |
| Write-Host "β No remote-only branches found." -ForegroundColor Green | |
| return | |
| } | |
| Write-Host "`nπ Remote-only branches found:" -ForegroundColor Cyan | |
| $remoteOnlyBranches | ForEach-Object { Write-Host "ποΈ $_" } | |
| if ($Test) { | |
| Write-Host "`nπ§ͺ Test mode active. No branches were deleted." -ForegroundColor Yellow | |
| return | |
| } | |
| # Confirm deletion | |
| $confirm = Read-Host "`nβ Delete all these remote-only branches? (y/n)" | |
| if ($confirm -eq 'y') { | |
| foreach ($branch in $remoteOnlyBranches) { | |
| Write-Host "π» Deleting origin/$branch..." -ForegroundColor Magenta | |
| git push origin --delete $branch | |
| } | |
| Write-Host "`nβ All remote-only branches deleted." -ForegroundColor Green | |
| } else { | |
| Write-Host "`nβ No remote branches deleted." -ForegroundColor Yellow | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment