Skip to content

Instantly share code, notes, and snippets.

@srathbun
Created June 30, 2014 12:48
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 srathbun/9b891162c462fc0ec9ea to your computer and use it in GitHub Desktop.
Save srathbun/9b891162c462fc0ec9ea to your computer and use it in GitHub Desktop.
This file assists git bisects by breaking a commit up on a separate branch
#!/bin/bash -e
# Helps you run a bisect script against your repo and determine which hunk caused a failure
###############################################################################
set -e # exit on command errors (so you MUST handle exit codes properly!)
set -E # pass trap handlers down to subshells
set -o pipefail # capture fail exit codes in piped commands
#set -x # execution tracing debug messages
# Error handler
on_err() {
echo ">> ERROR: $?"
FN=0
for LN in ${BASH_LINENO[@]}; do
[ "${FUNCNAME[$FN]}" = "main" ] && break
echo ">> ${BASH_SOURCE[$FN]} $LN ${FUNCNAME[$FN]}"
FN=$(( $FN + 1 ))
done
}
trap on_err ERR
# Exit handler
declare -a EXIT_CMDS
add_exit_cmd() { EXIT_CMDS+="$*;$(echo)"; }
on_exit(){ eval "${EXIT_CMDS[@]}"; }
trap on_exit EXIT
# Get command info
CMD_PWD=$(pwd)
CMD="$0"
CMD_DIR="$(cd "$(dirname "$CMD")" && pwd)"
# Defaults and command line options
[ -n "$VERBOSE" ] || VERBOSE=1
[ -n "$DEBUG" ] || DEBUG=
[ -n "$SCRIPT" ] || SCRIPT=
# Basic helpers
out() { echo "`date +%Y%m%dT%H%M%SZ`: $*"; }
err() { out "$*" 1>&2; }
vrb() { [ -n "$VERBOSE" -a "$VERBOSE" -gt 0 ] && out "$@" || true; }
dbg() { [ -n "$DEBUG" -a "$DEBUG" -gt 0 ] && err "$@" || true; }
die() { err "EXIT: $1" && [ -n "$2" ] && exit $2 || exit 1; }
# Show help function to be used below
show_help() {
awk 'NR>1{print} /^(###|$)/{exit}' "$CMD"
echo "USAGE: $(basename "$CMD") [arguments]"
echo "ARGS:"
MSG=$(awk '/^NARGS=-1; while/,/^esac; done/' "$CMD" | sed -e 's/^[[:space:]]*/ /' -e 's/|/, /' -e 's/)//' | grep '^ -')
EMSG=$(eval "echo \"$MSG\"")
echo "$EMSG"
}
# Parse command line options (odd formatting to simplify show_help() above)
NARGS=-1; while [ "$#" -ne "$NARGS" ]; do NARGS=$#; case $1 in
# SWITCHES
-h|--help) # This help message
show_help; exit 1; ;;
-d|--debug) # Enable debugging messages (implies verbose)
DEBUG=$(( $DEBUG + 1 )) && VERBOSE="$DEBUG" && shift && echo "#-INFO: DEBUG=$DEBUG (implies VERBOSE=$VERBOSE)"; ;;
-v|--verbose) # Enable verbose messages
VERBOSE=$(( $VERBOSE + 1 )) && shift && echo "#-INFO: VERBOSE=$VERBOSE"; ;;
-s|--script) # Force continuation after failed commands
SCRIPT=$(( $SCRIPT + 1 )) && shift && [ -n "$VERBOSE" ] && echo "#-INFO: SCRIPT=$SCRIPT"; ;;
# # PAIRS
# -t|--thing) # Set a thing to a value (DEFAULT: $THING)
# shift && THING="$1" && shift && vrb "#-INFO: THING=$THING"; ;;
esac; done
[ -n "$DEBUG" ] && set -x
###############################################################################
if [[ -f ".git/BISECT_LOG" ]] || err "You must be bisecting for this script!"
git bisect run "$SCRIPT" # this will exit on the bad commit if found
if [[ $? > 127 ]] && err "Your script aborted the bisect, quitting..."
git checkout -b bisect_`date +%Y%m%dT%H%M%SZ` # create a bisect branch
git reset HEAD^ # get the commit before the bad one, but leave the changed files
# for each hunk, create a commit
while $( [[ expr $(git status --porcelain 2>/dev/null| grep "^ M" | wc -l) > 0 ]] ) do
git add -p << EOF
y
q
EOF
git commit -m 'Bisector commit'
done
# now run the script again against our new set of commits
git bisect bad HEAD
git bisect run "$SCRIPT"
# this should leave you on the bisect branch, on the first breaking hunk
# exit with zero for success, so we don't have the last exit code, which could be non zero
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment