#!/bin/bash
# Pre-commit script hacked from http://mitk.org/images/8/82/Pre-commit.sample
# Modified by F. Felici for enforcing git commit policies at SPC

die() {
  RED='\033[0;31m'
  RESET='\033[0m'
  echo -e ${RED}
  echo '-------------------------' 1>&2
  echo ' pre-commit hook failure ' 1>&2
  echo '-------------------------' 1>&2
  echo -e ${RESET}'' 1>&2
  echo "$@" 1>&2
  echo ' '
  echo '  to override this check (if you know what you are doing)'
  echo '  git commit --no-verify'
  echo ' '
  exit 1
}

zero='0000000000000000000000000000000000000000'

#-----------------------------------------------------------------------------
# Check for committer identity.
advice='
Use the commands

  git config --global user.name '\''Your Name'\''
  git config --global user.email '\''you@yourdomain.com'\''

to introduce yourself to Git before committing.'

# Ensure name and email are available.
git config --get user.name > /dev/null &&
git config --get user.email > /dev/null ||
die 'Identity not configured!' "$advice"

# Validate the name and email.
git config --get user.name | grep ' ' > /dev/null ||
die 'Set user.name to your Real Name (with a space), not a userid.' "$advice"
git config --get user.email | grep '^[^@]*@[^@]*$' > /dev/null ||
die 'Set user.email to an email address (userid@validdomain.com).' "$advice"

#-----------------------------------------------------------------------------
# Check content that will be added by this commit.
if git rev-parse --verify -q HEAD > /dev/null; then
  against=HEAD
else
  # Initial commit: diff against an empty tree object
        against=$(git hash-object -t tree /dev/null)
fi

# Merge ("git commit" after "git merge" with conflicts or --no-commit)
merge_head=$(git rev-parse -q --verify MERGE_HEAD) || merge_head=''

# Disallow non-ascii file names.  The printable range starts at the
# space character and ends with tilde.
if test "$(git diff --cached --name-only --diff-filter=A -z $against |
	   LC_ALL=C tr -d '[ -~]\0')"; then
	die 'Non-ascii file names may not be added:
'"$(git diff --cached --name-only --diff-filter=A $against)"
fi

#-----------------------------------------------------------------------------
### UNCOMMENT LINE BELOW TO ADD TRAILING WHITESPACE CHECKS
### bad=$(git diff-index --check --cached $against --) || die "$bad"
# Approximate whitespace=tab-in-indent check with Git < 1.7.2.
git --version | grep -q " \(1\.[0-6]\|1\.7\.[01]\([^0-9]\|\$\)\)" &&
approx_tab_in_indent=true || approx_tab_in_indent=false

check_tab() {
  echo tab-checking $1
	lines=$(git diff-index -p --cached $against -- "$1" |
	        grep '^+	') &&
	echo "$lines" |
	while read line; do
		echo "$1: tab in indent." &&
		echo "$line"
	done
}

# Reject addition of a line without a newline at end-of-file.
check_no_lf_at_eof() {
	lines=$(git diff-index -p --cached $against -- "$1" | tail -2)
	if echo "$lines" | head -1 | grep -q '^+' &&
	   echo "$lines" | tail -1 | grep -q '^\\ No newline'; then
		echo "$1: No newline at end of file"
	fi
}

# Custom whitespace checks.
check_whitespace() {
	ws=$(git check-attr whitespace -- "$file" |
	     sed 's/^[^:]*: whitespace: //')
	if $approx_tab_in_indent; then
		case ",$ws," in
			*,tab-in-indent,*) check_tab "$1" ;;
		esac
	fi
	case ",$ws," in
		*,no-lf-at-eof,*) check_no_lf_at_eof "$1" ;;
	esac
}
bad=$(git diff-index --name-only --cached $against -- |
    while read file; do
    check_whitespace "$file"
    done)
test -z "$bad" || die "$bad"

#-----------------------------------------------------------------------------
# Check binary files
check_binary() {
# git diff --numstat gives '-' at beginning of line for binary files.
# search for this pattern using grep
 if git diff --cached --numstat -- "$file" | grep -e '^-'>/dev/null; then
 echo "Binary file found: $file"
 echo '
In general, we want to avoid committing binary files
since they can usually be generated from the source files.
Some exceptions, for example small .pdf, .docx, .mat files, can be accepted.
'
 fi
}
bad=$(git diff-index --name-only --cached $against -- |
while read file; do
	check_binary "$file"
done)
test -z "$bad" || die "$bad"

#-----------------------------------------------------------------------------
# Check file size

# This hook disallows large files
# by default but can be configured.  A limit for specific files or patterns
# may be set in ".gitattributes" with the "hooks.MaxObjectKiB" attribute.
# For example, the line
#
#   *.c              hooks.MaxObjectKiB=2048
#
# sets a limit of 2048 KiB for C source files.  See "git help attributes"
# for details on the .gitattributes format.  If no attribute has been set
# for a given file then its size is limited by the local default.  Run
#
#   git config hooks.MaxObjectKiB $KiB
#
# to set the local default limit (0 to disable).

size_max_KiB=$(git config hooks.MaxObjectKiB)
test -n "$size_max_KiB" || size_max_KiB=512
size_too_large_once=""
size_too_large_once() {
	test -z "$size_too_large_once" || return ; size_too_large_once=done
	echo 'At least one file is staged for commit with size larger than its limit.
We prefer to keep large files out of the main source tree, especially
binary files that do not compress well.
'
}
size_too_large() {
	size_too_large_once
	echo "The path '$file' has size $file_KiB KiB, greater than allowed $max_KiB KiB."
}
size_validate_max_KiB() {
	test "$max_KiB" -ge "0" 2>/dev/null && return 0
	echo "The path '$file' has invalid attribute \"hooks-MaxObjectKiB=$max_KiB\"."
	return 1
}
check_size() {
        dst_obj=$(git ls-files -s "$file"|cut -d' ' -f 2)
	test -z "$dst_obj" && return
	max_KiB=$(git check-attr hooks.MaxObjectKiB -- "$file" |
		  sed 's/^[^:]*: hooks.MaxObjectKiB: //')
	case "$max_KiB" in
		'unset')       return ;; # No maximum for this object.
		'set')         max_KiB="$size_max_KiB" ;; # Use local default.
		'unspecified') max_KiB="$size_max_KiB" ;; # Use local default.
		*) size_validate_max_KiB || return ;;
	esac
	if test "$max_KiB" -gt "0"; then
		file_KiB=$(expr '(' $(git cat-file -s $dst_obj) + 1023 ')' / 1024)
		test "$file_KiB" -le "$max_KiB" || size_too_large
	fi
}

bad=$(git diff-index --name-only --cached $against -- |
while read file; do
	check_size "$file"
done)
test -z "$bad" || die "$bad"
