Skip to content

Instantly share code, notes, and snippets.

@tored
Created May 16, 2025 08:07
Show Gist options
  • Save tored/eaddb2a048fda2cc6954d5c167dd0073 to your computer and use it in GitHub Desktop.
Save tored/eaddb2a048fda2cc6954d5c167dd0073 to your computer and use it in GitHub Desktop.
Git clean merged branches locally and optionally remote
#!/usr/bin/env php
<?php
/**
* Git cleanup script: deletes merged branches locally (and optionally on remote).
*
* Usage:
* php git-clean-merged.php [--with-remote] [--help]
*
* Options:
* --with-remote Also delete branches on origin
* --help Show this help message
*
* Default behavior is to delete only local merged branches.
*/
// Parse command-line options
$options = getopt('', ['with-remote', 'help']);
if (isset($options['help'])) {
echo <<<EOT
Usage: php git-clean-merged.php [options]
Options:
--with-remote Also delete branches on the remote (origin)
--help Show this help text
This script removes all local branches that have been merged into the default branch.
Use --with-remote to also delete them from the remote repository (origin).
After cleanup, it switches back to the branch you started from.
EOT;
exit(0);
}
// Check if we're in a Git repository
if (!is_dir('.git') && shell_exec('git rev-parse --git-dir 2>/dev/null') === null) {
echo "Error: Not inside a Git repository.\n";
exit(1);
}
// Save current branch
$currentBranch = trim(shell_exec("git rev-parse --abbrev-ref HEAD"));
if (!$currentBranch) {
echo "Error: Unable to determine current branch.\n";
exit(1);
}
// Determine default (base) branch
$rawBaseBranch = shell_exec("git symbolic-ref refs/remotes/origin/HEAD 2>&1");
if (!$rawBaseBranch) {
echo "Error: Failed to determine the base branch. Make sure origin/HEAD is set.\n";
exit(1);
}
$baseBranch = trim(str_replace('refs/remotes/origin/', '', $rawBaseBranch));
echo "Default branch detected: $baseBranch\n";
// Switch to base branch
echo "Switching to $baseBranch...\n";
shell_exec("git checkout $baseBranch 2>&1");
// Fetch and prune remote branches
echo "Running git fetch --prune...\n";
shell_exec("git fetch --prune 2>&1");
// Find merged local branches
echo "Finding merged local branches...\n";
$mergedBranches = shell_exec("git branch --merged");
if (!$mergedBranches) {
echo "No merged branches found or an error occurred.\n";
shell_exec("git checkout $currentBranch 2>&1");
exit(1);
}
foreach (explode("\n", trim($mergedBranches)) as $branchLine) {
$branchLine = trim($branchLine);
if ($branchLine === '') continue;
if (strpos($branchLine, '*') === 0) {
$branchLine = ltrim($branchLine, '* ');
}
if ($branchLine === $baseBranch) continue;
echo "Deleting local branch '$branchLine'...\n";
shell_exec("git branch -d $branchLine 2>&1");
if (isset($options['with-remote'])) {
echo "Deleting remote branch '$branchLine' from origin...\n";
shell_exec("git push origin --delete $branchLine 2>&1");
}
}
// Switch back to original branch
if ($currentBranch !== $baseBranch) {
echo "Returning to original branch '$currentBranch'...\n";
shell_exec("git checkout $currentBranch 2>&1");
}
echo "Cleanup complete.\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment