Skip to content

Instantly share code, notes, and snippets.

@s1moe2
Created July 28, 2025 15:36
Show Gist options
  • Save s1moe2/62668353913c283dc04f508a206ba4b4 to your computer and use it in GitHub Desktop.
Save s1moe2/62668353913c283dc04f508a206ba4b4 to your computer and use it in GitHub Desktop.
Stale branch cleanup script
#!/bin/bash
# Script to delete merged remote branches except those in the no-delete list
# Usage:
# ./delete_merged_git.sh [target-branch] [--delete] [--old-days N]
# Default target branch is "develop"
# Use --delete to actually delete branches; otherwise it's a dry run
# Use --old-days N to also delete branches older than N days
TARGET_BRANCH="develop"
PERFORM_DELETE=false
OLD_DAYS=""
for arg in "$@"; do
if [[ "$arg" == "--delete" ]]; then
PERFORM_DELETE=true
elif [[ "$arg" == "--old-days" ]]; then
# Get the next argument as the number of days
shift
OLD_DAYS="$1"
elif [[ "$arg" != --* ]] && [[ "$arg" != "$OLD_DAYS" ]]; then
TARGET_BRANCH="$arg"
fi
done
# Branches to never delete (add your protected branches here)
NO_DELETE_LIST=(
"main"
"master"
"develop"
"staging"
"production"
"release"
)
# Function to check if branch is older than N days
is_branch_old() {
local branch="$1"
local days="$2"
if [ -z "$days" ]; then
return 1 # Don't consider old if no days specified
fi
local last_commit_date=$(git log -1 --format="%ct" "origin/$branch" 2>/dev/null)
if [ -z "$last_commit_date" ]; then
return 1 # Can't determine age
fi
local cutoff_date=$(date -d "$days days ago" +%s 2>/dev/null || date -v-${days}d +%s 2>/dev/null)
[ "$last_commit_date" -lt "$cutoff_date" ]
}
echo "๐Ÿ” Finding branches to delete..."
if [ -n "$OLD_DAYS" ]; then
echo " - Merged branches in origin/$TARGET_BRANCH"
echo " - OR branches older than $OLD_DAYS days"
else
echo " - Merged branches in origin/$TARGET_BRANCH"
fi
# Fetch latest remote refs
git fetch origin --prune
# Get list of merged remote branches, excluding HEAD
MERGED_BRANCHES=$(git branch -r --merged origin/$TARGET_BRANCH | grep -v 'origin/HEAD' | sed 's/origin\///' | xargs)
# Get all remote branches if we're checking for old branches
if [ -n "$OLD_DAYS" ]; then
ALL_BRANCHES=$(git branch -r | grep -v 'origin/HEAD' | sed 's/origin\///' | sed 's/^ *//' | xargs)
fi
echo ""
echo "๐Ÿ—‘๏ธ Branches to delete:"
BRANCHES_TO_DELETE=()
PROCESSED_BRANCHES=()
# Process merged branches
for branch in $MERGED_BRANCHES; do
# Skip if branch is in no-delete list
if [[ " ${NO_DELETE_LIST[@]} " =~ " ${branch} " ]]; then
continue
fi
BRANCHES_TO_DELETE+=("$branch")
PROCESSED_BRANCHES+=("$branch")
echo " - $branch (merged into $TARGET_BRANCH)"
done
# Process old branches if --old-days specified
if [ -n "$OLD_DAYS" ] && [ -n "$ALL_BRANCHES" ]; then
for branch in $ALL_BRANCHES; do
# Skip if already processed as merged
if [[ " ${PROCESSED_BRANCHES[@]} " =~ " ${branch} " ]]; then
continue
fi
# Skip if branch is in no-delete list
if [[ " ${NO_DELETE_LIST[@]} " =~ " ${branch} " ]]; then
continue
fi
# Check if old
if is_branch_old "$branch" "$OLD_DAYS"; then
BRANCHES_TO_DELETE+=("$branch")
last_commit=$(git log -1 --format="%ar" "origin/$branch" 2>/dev/null || echo "unknown")
echo " - $branch (last commit: $last_commit)"
fi
done
fi
if [ ${#BRANCHES_TO_DELETE[@]} -eq 0 ]; then
echo "โœ… No branches to delete (all are protected or don't meet criteria)"
exit 0
fi
if $PERFORM_DELETE; then
read -p "โš ๏ธ Really delete these ${#BRANCHES_TO_DELETE[@]} branches? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "๐Ÿšฎ Deleting branches..."
for branch in "${BRANCHES_TO_DELETE[@]}"; do
echo "Deleting origin/$branch..."
git push origin --delete "$branch"
done
echo ""
echo "โœ… Deleted ${#BRANCHES_TO_DELETE[@]} branches"
echo "โœ… Done!"
else
echo "โŒ Cancelled"
fi
else
read -p "โš ๏ธ[DRY RUN] Really delete these ${#BRANCHES_TO_DELETE[@]} branches? (y/N): " -n 1 -r
echo
for branch in "${BRANCHES_TO_DELETE[@]}"; do
echo "[DRY RUN] Deleting origin/$branch..."
done
echo "๐Ÿ’ก Dry run: Use '--delete' to actually remove these branches."
echo "๐Ÿงช Nothing was deleted."
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment