-
-
Save attiks/31a3f9b174543650472289287f08397c to your computer and use it in GitHub Desktop.
Delete Stale Branches
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
#!/usr/bin/env bash | |
usage() { | |
echo "Usage: $0 [-y] [-l]" 1>&2; | |
echo "Uses dry run by default, specify -y to actually delete" 1>&2; | |
echo "Use -l to delete local branches" 1>&2; | |
echo "Assumes the main branch is called 'main'" 1>&2; | |
echo "Set main branch using git config core.main-branch 'main'" 1>&2; | |
exit 1; | |
} | |
DRYRUN="--dry-run" | |
DELETELOCAL="" | |
while getopts "ylh" opt; do | |
case $opt in | |
y) DRYRUN="";; | |
l) DELETELOCAL="y";; | |
h) usage;; | |
esac | |
done | |
# Prune local "cache" of remote branches first. | |
git fetch --prune origin | |
# Get main branch name. | |
MAIN=$(git config --get core.main-branch) | |
if [ -z "$MAIN" ]; then | |
MAIN="main" | |
fi | |
echo -e "Main brach is \033[1;33m$MAIN\033[0m" | |
MAINEXISTS=$(git ls-remote --heads origin $MAIN) | |
if [[ -z "$MAINEXISTS" ]]; then | |
echo -e "\033[0;31mMain branch not found on remote\033[0m" | |
usage | |
fi | |
if [ -n "$DRYRUN" ]; then | |
echo -e "\033[1;33mDry run is active\033[0m" | |
fi | |
# Delete merged to main branches. | |
mergedBranches=$(git branch -r --merged origin/$MAIN | egrep -v "(^\\*|main|master|develop|dev|staging|sandbox)" | cut -d/ -f2-) | |
if [ -n "${mergedBranches}" ]; then | |
echo -e "\033[0;32mDeleting merged branches ...\033[0m" | |
git push $DRYRUN --delete origin ${mergedBranches} | |
fi | |
# Get branches with last (cherry picked) commit older than 5 months. | |
echo -e "\033[0;32mSearching for stale branches ...\033[0m" | |
staleTimestamp=$(date -d "now - 5 months" +"%s") | |
maybeStaleTimestamp=$(date -d "now - 2 weeks" +"%s") | |
notMergedBranches=$(git branch -r --no-merged origin/$MAIN | grep "^ origin/" | egrep -v "(^\\*|main|master|develop|dev|staging|sandbox)") | |
branchesToDelete="" | |
branchesToReview="" | |
for branch in ${notMergedBranches}; do | |
lastCommitInfo=$(git cherry origin/$MAIN ${branch} | grep -v "^-" | cut -d" " -f2 | xargs git show --format="%H|%ct|%cr|%an" --quiet | grep -v "^$(git rev-parse HEAD)" | tail -1) | |
lastCommitTimestamp=$(echo "${lastCommitInfo}" | cut -d"|" -f2) | |
if [ -z "${lastCommitTimestamp}" ] || [ ${lastCommitTimestamp} -lt ${staleTimestamp} ]; then | |
branchesToDelete+=" ${branch#origin/}" | |
elif [ ${lastCommitTimestamp} -lt ${maybeStaleTimestamp} ]; then | |
branchesToReview+="${branch#origin/}|${lastCommitInfo}"$'\n' | |
fi | |
echo -n . | |
done | |
echo # for new line after dots | |
# Delete branches with last (cherry picked) commit older than 5 months. | |
if [ -n "${branchesToDelete}" ]; then | |
echo -e "\033[0;32mDeleting stale branches ...\033[0m" | |
git push $DRYRUN --delete origin ${branchesToDelete} | |
else | |
echo -e "\033[0;32mNo stale branches found.\033[0m" | |
fi | |
# List stale branches. | |
echo -e "\033[1;33mBranches to review (may be stale):\033[0m" | |
echo "${branchesToReview}" | sort -t"|" -k5 | awk -F"|" 'NF {print $5 " changed branch \"" $1 "\" in project \"'${PWD##*/}'\" " $4}' | |
# Delete local merged branches. | |
if [ -n "$DELETELOCAL" ]; then | |
if [ -n "$DRYRUN" ]; then | |
echo -e "\033[1;33mWill delete local merged branches\033[0m" | |
git branch --merged | egrep -v "(^\\*|main|master|develop|dev|staging|sandbox)" | |
else | |
echo -e "\033[1;33mDelete local merged branches\033[0m" | |
git branch --merged | egrep -v "(^\\*|main|master|develop|dev|staging|sandbox)" | xargs git branch -d # | |
fi | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment