-
-
Save non7top/0a0e226badb9bc3a7c8c31bb1c528149 to your computer and use it in GitHub Desktop.
Bash script for cleaning up old git branches from local and remote repositories
This file contains 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
#!/bin/bash | |
ME=$(basename $0) | |
function showHelp { | |
cat << EOF | |
Clean-up old git branches | |
Usage: $ME local show-last [AGE_IN_WEEKS] | |
$ME remote show-last REMOTE [AGE_IN_WEEKS] | |
$ME [-d] local remove-old AGE_IN_WEEKS | |
$ME [-d] remote remove-old REMOTE AGE_IN_WEEKS | |
$ME -h | |
Options: | |
AGE_IN_WEEKS Only include branches this many weeks old, or older | |
REMOTE Local alias for the remote repository | |
-h Show this help message and exit | |
-d Dry-run. Show what will be done if this flag is omitted | |
Examples: | |
Show who the last committer of each branch was. Here 'origin' is the | |
local alias for the remote repository. For a list of remote repositories | |
aliased to your local repository, run `git remote -v`: | |
$ME remote origin show-last | |
Note that this shows the committer, not the author. These can differ if | |
someone cherry-picked or merged a commit. | |
Remove local branch references for braches older than a year: | |
$ME local remove-old 52 | |
Show the remote branches that will be removed with the dry-run flag: | |
$ME remote -d remove-old origin 80 | |
Actually remove remote branches older than 80 weeks: | |
$ME remote remove-old origin 80 | |
EOF | |
} | |
function showTooFewArguments { | |
cat << EOF | |
ERROR: | |
Arguments missing. Which operation do you want to perform? | |
EOF | |
showHelp | |
} | |
function ageInWeeks { | |
local NOW=$(date +%s) | |
local SECONDS=$(( $NOW - $1 )) | |
local DAYS=$(( $SECONDS / 604800 )) | |
eval "$2"="$DAYS" | |
} | |
DRY_RUN=false | |
# Parse the short flags. | |
while getopts "h?d" opt; do | |
case $opt in | |
"h") | |
showHelp | |
exit | |
;; | |
"?") | |
showHelp | |
exit | |
;; | |
"d") | |
DRY_RUN=true | |
;; | |
esac | |
done | |
# Done with the short flags, shift over to the remaining arguments. | |
shift $(($OPTIND - 1)) | |
# Parse the command line arguments. | |
if [ $# -eq 0 ]; then | |
# Called without arguments, show help and exit. | |
showHelp | |
exit | |
fi | |
# Is the current dir a git repo? | |
git status > /dev/null || exit | |
TARGET=$1 | |
ACTION=$2 | |
MIN_AGE= | |
REMOTE= | |
if [ "$TARGET" == "local" ]; then | |
MIN_AGE=$3 | |
if [ $# -lt 2 ]; then | |
showTooFewArguments | |
exit 1; | |
fi | |
echo "Working on local branches only." | |
elif [ "$TARGET" == "remote" ]; then | |
REMOTE=$3 | |
MIN_AGE=$4 | |
if [ $# -lt 3 ]; then | |
showTooFewArguments | |
exit 1; | |
fi | |
echo "Working on remote branches only." | |
else | |
showHelp | |
exit 1 | |
fi | |
echo | |
echo "Last committer of each branch:" | |
echo | |
if [ -z "$MIN_AGE" ]; then | |
MIN_AGE=0 | |
fi | |
# The following lists the branches. This is the same for show-last and remove-old. | |
BRANCHES= | |
if [ "$TARGET" == "local" ]; then | |
BRANCHES=$(git branch --no-color --no-abbrev -v) | |
else | |
BRANCHES=$(git ls-remote -h $REMOTE) | |
fi | |
BRANCH_LIST='' | |
BRANCH_NAMES=(); | |
while read -r BRANCH | |
do | |
NAME= | |
SHA= | |
if [ "$TARGET" == "local" ]; then | |
NAME=$(echo "$BRANCH" | sed -e 's/* / /' | awk '{ print $1 }') | |
else | |
NAME=$(echo "$BRANCH" | sed -e 's^refs/heads/^^' | awk '{ print $2 }') | |
fi | |
if [[ $NAME =~ .*/HEAD ]]; then | |
continue | |
fi | |
SHA= | |
if [ "$TARGET" == "local" ]; then | |
SHA=$(echo "$BRANCH" | sed -e 's/* / /' | awk '{ print $2 }') | |
else | |
SHA=$(echo "$BRANCH" | awk '{ print $1 }') | |
fi | |
AGE="" | |
ageInWeeks $(git log -n 1 --format=format:"%ct" $SHA) AGE | |
if [ $AGE -lt $MIN_AGE ]; then | |
continue | |
fi | |
BRANCH_NAMES+=("$NAME") | |
BRANCH_LINE="$(printf '%-60s %4s %-12s' "${NAME}:" $AGE 'weeks') $(git log -n 1 --format=format:"%cN <%cE>" $SHA)" | |
BRANCH_LIST+="${BRANCH_LINE}\n" | |
done <<< "$BRANCHES" | |
if [ ${#BRANCH_NAMES[@]} -eq 0 ]; then | |
echo "No branches matching these criterea found." | |
exit | |
fi | |
# Sort by age. | |
printf "$BRANCH_LIST" | sort -k2 -n | |
if [ "$ACTION" == "remove-old" ]; then | |
echo | |
if [ $DRY_RUN == true ]; then | |
if [ "$TARGET" == "local" ]; then | |
echo "These local branches will be removed if you leave out the -d flag." | |
elif [ "$TARGET" == "remote" ]; then | |
echo "These remote branches will be removed from the remote repo if you leave out the -d flag." | |
fi | |
else | |
# Read one character from console. | |
read -p "Remove these branches older than $MIN_AGE weeks. Are you sure? [y/N] " -n 1 -r | |
echo | |
if [[ $REPLY =~ ^[Yy]$ ]] | |
then | |
echo "Removing branches..." | |
for BNAME in "${BRANCH_NAMES[@]}"; do | |
echo "${BNAME}..." | |
if [ "$TARGET" == "local" ]; then | |
git branch -D $BNAME | |
elif [ "$TARGET" == "remote" ]; then | |
git push $REMOTE :$BNAME | |
fi | |
done | |
else | |
echo "Aborting." | |
fi | |
fi | |
echo | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment