Skip to content

Instantly share code, notes, and snippets.

@aks
Last active November 7, 2019 18:43
Show Gist options
  • Select an option

  • Save aks/d33cd1855fbd0e258a51a3f647d9368a to your computer and use it in GitHub Desktop.

Select an option

Save aks/d33cd1855fbd0e258a51a3f647d9368a to your computer and use it in GitHub Desktop.
Bash script to squash all commits in the current branch (against master)
#!/usr/bin/env bash
# squash-all [BASE_BRANCH]
PROG="${0##*/}"
DIR="${0%/*}"
talk() { echo 1>&2 "$*" ; }
vtalk() { (( verbose )) && talk "$*" ; }
error() { talk "$*" ; exit 1 ; }
usage() {
(( $# > 0 )) && talk "$*"
cat 1>&2 <<USAGE
usage: $PROG [options] [BASE_BRANCH [BRANCH_NAME]]
This script will squash all the commits in BRANCH_NAME, if given. If
not given the current branch in the current workding directory is used.
If BASE_BRANCH is given, then it is used as the base-branch for the merge
that squashes the commits in the target branch. If BASE_BRANCH is not
given, "master" is assumed.
Unless -a is given, confirmation of actions is confirmed with a prompt
to the user for a yes or no answer.
Options:
-a go ahead; do the change
-h show this help
-n don't do any changes, but show the commands
-v be verbose
USAGE
exit
}
run() {
if [[ -n "$norun" ]]; then
talk "(norun) $@"
else
safe_run "$@"
fi
return 0
}
safe_run() {
if [[ -n "$verbose$norun" ]]; then
talk ">> $@"
fi
if ! eval "$@" ; then
code=$?
return $code
fi
return 0
}
set_branch() {
if [[ -n "$1" ]] ; then
branch_name="$1"
else
branch_name=`current_branch`
fi
}
current_branch() {
if [[ -z "$current_branch" ]] ; then
current_branch="$(git rev-parse --abbrev-ref HEAD)"
fi
echo "$current_branch"
}
set_base_branch() {
base_branch="${1:-master}"
}
validate_branch_name() {
if [[ -z "$branch_name" ]]; then
echo "No branch name! (detached HEAD?)"
exit
fi
}
validate_base_branch() {
case "$base_branch" in
master|mast|m) base_branch=master ;;
releases|release|rel|r) base_branch=releases ;;
*) error "Only 'releases' or 'master' can be used as a base branch!" ;;
esac
}
confirm_go_ahead() {
while (( ! go_ahead )) ; do
talk "Branch '${branch_name}' will be merged with '${base_branch}'"
talk " .. and all local commits squashed"
read -p "Okay? [yN]" ans
ans="${ans:-no}"
case "${ans,,}" in
yes|ye|y) go_ahead=1 ;;
no|n) error "Nothing done." ;;
*) talk "Please answer yes or no, or just <return> for 'no'" ;;
esac
done
}
get_merge_base_commit() {
vtalk "Computing common base commit for $base_branch and $branch_name .."
safe_run "git merge-base '$base_branch' '$branch_name'"
}
squash_commits() {
local base_commit="$1"
run "git reset --soft $base_commit && git commit -a"
}
maybe_checkout_branch() {
if [[ "$branch_name" != `current_branch` ]] ; then
vtalk "Checking out $branch_name."
run "git checkout $branch_name"
fi
}
##################################################
go_ahead= norun= verbose=
while getopts 'ahnv' opt ; do
case "$opt" in
h) usage ;;
a) go_ahead=1 ;;
n) norun=1 ;;
v) vebose=1 ;;
esac
done
shift $(( OPTIND - 1 ))
(( $# > 2 )) && usage "Too many arguments!"
set_base_branch "$1"
set_branch "$2"
validate_branch_name
validate_base_branch
confirm_go_ahead
set -x
maybe_checkout_branch
merge_base_commit=`get_merge_base_commit`
squash_commits $merge_base_commit
exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment