Skip to content

Instantly share code, notes, and snippets.

@zerowidth
Created February 28, 2012 22:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerowidth/1935703 to your computer and use it in GitHub Desktop.
Save zerowidth/1935703 to your computer and use it in GitHub Desktop.
git cleanup script
#!/bin/bash
# inspired by https://gist.github.com/1564252
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
BLUE=$(tput setaf 4)
CYAN=$(tput setaf 6)
RESET=$(tput sgr 0)
function header() {
echo $1 | tr '[:print:]' '-'
echo $BLUE$1$RESET
echo $1 | tr '[:print:]' '-'
}
function debug() {
[[ $DEBUG ]] && {
echo "-" $@ >&2
}
}
# Check for "real" merges
#
# Returns an empty string unless the branch has been merged.
function merged_normally() {
debug "checking normal merges"
git branch --list --merged origin/master $1
}
# Checks for branches that have been entirely cherry-picked into master
#
# Returns an empty string unless the branch has been merged.
function merged_via_cherry_pick() {
debug "checking merges via cherry-pick"
[[ -z `git cherry origin/master $1 | grep "+"` ]] && {
echo "merged"
}
}
# Checks for branches that have been applied as a squashed patch.
#
# This is fairly brittle - if there's any overlap at all with a feature branch
# that has multiple patches and other commits that touch the same things, this
# won't be able to figure out that the branch has been applied. For smaller
# feature branches and bugfixes that are kept rebased and applied quickly, this
# should suffice.
#
# Returns an empty string unless the branch has been merged.
function merged_via_patch() {
debug "checking merges via patch"
merge_base=`git merge-base origin/master $1`
patch_id=`git diff $merge_base $1 | git patch-id | cut -d' ' -f1`
# only look at 50 commits, especially for old branches. if it hasn't been
# applied as a patch by then, it probably hasn't been applied
for sha in `git rev-list $merge_base..origin/master | tail -50`; do
line=`git show $sha | git patch-id | grep $patch_id`
[[ $line ]] && {
echo $line
break
}
done
}
function usage() {
cat << usage
Usage: cleanup -f [remotes]
List or remove branches from your local working copy and the listed remotes that
have had their changes integrated into origin/master.
NOTE: -f must come first, before any other arguments!
Options:
-h: this help message
-f: actually do the cleanup, don't just list out the status
usage
}
force=0
[[ $ARGS == "--help" ]] && {
usage
exit
}
while getopts "hfo:" option
do
case $option in
h)
usage
exit
;;
f)
force=1
;;
?)
usage
exit
;;
esac
shift $((OPTIND-1)) # remove the arg from $@
done
# [[ "$@" = "" ]] && {
# echo -e "\nAt least one remote is required, e.g. '$(whoami)' or 'collab'\n"
# exit
# }
git diff --quiet
if [[ $? != 0 ]]; then
echo -e "\nSorry, can't do this on a dirty working copy! git stash first.\n"
exit
fi
[[ $force == "1" ]] && {
echo "${GREEN}updating remotes...${RESET}"
git fetch --all --prune
}
function cleanup() {
branch=$1
remote=$2
header $branch
if [[
"$(merged_normally $branch)" ||
"$(merged_via_cherry_pick $branch)" ||
"$(merged_via_patch $branch)" ]]; then
echo "${GREEN}branch has been applied$RESET"
[[ $force == 1 ]] && {
echo
read -p "delete ${branch}? [y/N] "
if [[ "$REPLY" == "y" ]]; then
if [[ $remote ]]; then
rbranch=`echo $branch | sed "s/^$remote\///"`
echo "${RED}removing remote branch $rbranch from ${remote}${RESET}"
git push $remote :$rbranch
else
echo "${RED}removing local branch ${branch}${RESET}"
git branch -D $branch
fi
else
echo "skipping..."
fi
}
# TODO figure out what other branches are the same based on SHA, and ask to
# delete all of them, instead of having explicit remotes
# git branch --all --contains <sha> will do it
else
merge_base=`git merge-base origin/master $branch`
unmerged=`git rev-list $merge_base..$branch | wc -l | sed 's/ //g'`
echo "$unmerged commits are not in master:"
echo
git --no-pager log --shortstat --oneline $merge_base..$branch
fi
echo
}
# if [[ $@ == "" ]]; then
for branch in `git branch | grep -v "*" | grep -v master | cut -c3-`; do
cleanup $branch
done
# fi
for remote in $@; do
for remote_branch in `git branch -r | grep " $remote/" | grep -v master`; do
cleanup $remote_branch $remote
done
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment