#!/bin/bash # # This script runs a given command over a range of Git revisions. Note that it # will check past revisions out! Exercise caution if there are important # untracked files in your working tree. # # This is adapted from Gary Bernhardt's dotfiles: # https://github.com/garybernhardt/dotfiles # # Example usage: # $ git-rev-run [-v] 'python runtests.py' origin/master..master set -e if [[ $1 == -v ]]; then verbose=1 shift fi test_command=$1 shift rev_args="$@" main() { abort_if_dirty_repo enforce_usage run_tests } abort_if_dirty_repo() { set +e git diff-index --quiet --cached HEAD if [[ $? -ne 0 ]]; then echo "You have staged but not committed changes that would be lost! Aborting." exit 1 fi git diff-files --quiet if [[ $? -ne 0 ]]; then echo "You have unstaged changes that would be lost! Aborting." exit 1 fi untracked=$(git ls-files --exclude-standard --others) if [ -n "$untracked" ]; then echo "You have untracked files that could be overwritten! Aborting." exit 1 fi set -e } enforce_usage() { if [ -z "$test_command" ]; then usage exit 1 fi } usage() { echo "usage: `basename $0` [-v] test_command ref [...]" } run_tests() { orig_checkout=`log_command git symbolic-ref --short -q HEAD || log_command git rev-parse HEAD` revs=`log_command git rev-parse $rev_args` for rev in $revs; do debug "Checking out: $(git log --oneline -1 $rev)" log_command git checkout --quiet $rev set +e if ! log_command $test_command; then debug "^^^ COMMAND FAILED! ^^^" log_command git reset --hard --quiet log_command git checkout --quiet $orig_checkout exit fi set -e log_command git reset --hard --quiet done log_command git checkout --quiet $orig_checkout debug "OK for all revisions!" } log_command() { debug "=> $*" eval "$@" } debug() { if [ $verbose ]; then echo $* >&2 fi } main