#!/usr/bin/env bash # # -------------------------------------------------------- # Usage # -------------------------------------------------------- # # git safedel [branch-name] # # -------------------------------------------------------- # Summary # -------------------------------------------------------- # # Performs a safe deletion of a branch by pulling the default branch from # origin, then rebasing the specified or current branch on the default branch. # Once complete, a branch deletion is attempted, however aborted if the branch # is not fully merged. # # Author: Tony Grosinger # # -------------------------------------------------------- # Dependencies # -------------------------------------------------------- # # * Git Top (Used to pull from the default branch and rebase) # # -------------------------------------------------------- # Installation # -------------------------------------------------------- # # Place the following in your .bashrc then place this file on your path. # # _git_safedel() { # if [[ "${COMP_LINE}" =~ ^git\ safedel.*$ ]]; then # __gitcomp_nl "$(__git_heads)" # else # __git_main # fi # } # # Ensure the following is in your .gitconfig alias section: # # default-branch = "!git symbolic-ref refs/remotes/origin/HEAD | cut -f4 -d/" # VERSION="v0.0.2" GREEN="\e[32m" BLUE="\e[34m" RED="\e[91m" CLEAR="\e[0m" DEFAULT_BRANCH=$(git default-branch) version() { echo "git safedel ${VERSION}" echo "" } usage() { echo "usage: git safedel [branch-name]" echo "" echo "If branch name is omitted, current branch will be deleted" echo "Only safe deletes are attempted. If delete fails, then command" echo "only acts as a rebase against the default branch." } print_cmd() { echo -e "${GREEN}>> ${CLEAR}${@}" } print_status() { echo -e "${BLUE}${@}${CLEAR}" } print_fatal() { echo -e >&2 "${RED}Fatal: ${CLEAR}${@}" } exec_cmd() { print_cmd ${1} ${1} } require_git_repo() { git rev-parse --is-inside-work-tree > /dev/null 2>&1 if [ $? != 0 ]; then print_fatal "Not a git repository" exit 1 fi } require_clean_work_tree() { # Outputs an error to stderr if working tree is not clean, then exits # Function retrieved from http://www.spinics.net/lists/git/msg142043.html # http://stackoverflow.com/questions/3878624/how-do-i-programmatically-determine-if-there-are-uncommited-changes # Update the index git update-index -q --ignore-submodules --refresh err=0 # Disallow unstaged changes in the working tree if ! git diff-files --quiet --ignore-submodules -- then echo >&2 "cannot $1: you have unstaged changes." git diff-files --name-status -r --ignore-submodules -- >&2 err=1 fi # Disallow uncommitted changes in the index if ! git diff-index --cached --quiet HEAD --ignore-submodules -- then echo >&2 "cannot $1: your index contains uncommitted changes." git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2 err=1 fi if [ $err = 1 ] then echo >&2 "Please commit or stash them." exit 1 fi } delete_branch() { local startbranch=${1} exec_cmd "git checkout -q ${DEFAULT_BRANCH}" exec_cmd "git branch -d ${startbranch}" if [ $? != 0 ]; then echo >&2 "Pull and rebase complete however branch could not be deleted." echo >&2 "Please manually delete branch." exit 1 fi } main() { require_git_repo local startbranch=`git rev-parse --abbrev-ref HEAD` if [ $# = 1 ]; then case "$1" in "-h"|"--help") usage exit 0 ;; "-v"|"--version") version exit 0 ;; *) startbranch=$1 ;; esac elif [ $# != 0 ]; then print_fatal "Invalid number of arguments provided" echo "" usage exit 1 fi print_status "Attempting to delete branch: ${startbranch}" if [ ${startbranch} = "${DEFAULT_BRANCH}" ]; then echo "Cannot delete branch \"${DEFAULT_BRANCH}\"" echo "Please switch branch or provide a branch name as an argument." exit 1 fi git show-ref --verify -q refs/heads/${startbranch} if [ $? != 0 ]; then print_fatal "Branch \"${startbranch}\" does not exist" exit 1 fi require_clean_work_tree exec_cmd "git top ${startbranch}" delete_branch $startbranch print_status "Branch deletion complete." } main "$@"