From a86c29cfe100aaaffcb97a94cde87e9b547553ad Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 2 Jul 2014 17:40:41 +0100 Subject: [PATCH] Put pre-commit hook back that was deleted on previous merge --- .githooks/pre-commit | 334 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100755 .githooks/pre-commit diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 000000000000..3ae5c2a03a40 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,334 @@ +#!/usr/bin/env bash +#============================================================================= +# Copyright 2010-2011 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +die() { + echo 'pre-commit hook failure' 1>&2 + echo '-----------------------' 1>&2 + echo '' 1>&2 + echo "$@" 1>&2 + 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=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +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 + +#----------------------------------------------------------------------------- +# Builtin whitespace checks. +bad=$(git diff-index --check --cached $against -- --ignore-space-at-eol) || die "$bad" + +# Approximate whitespace=tab-in-indent check with Git < 1.7.2. +git --version | grep -q " \(1\.[0-6]\|1\.7\.[01]\)" && +approx_tab_in_indent=true || approx_tab_in_indent=false +check_tab() { + 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 file modes and sizes. +mode_looks_exe() { + case "$1" in + *.bat) return 0 ;; + *.cmd) return 0 ;; + *.exe) return 0 ;; + *.com) return 0 ;; + esac + git cat-file blob "$2" | head -1 | grep "^#\!/" > /dev/null +} +mode_not_exe () { + echo "The file '$file' has looks executable but does not have an executable mode." +} +mode_bad_exe () { + echo "The file '$file' has executable mode but does not look executable." +} +mode_non_file () { + echo "The path '$file' has a non-file mode." +} +check_mode() { + case "$dst_mode" in + 100755) mode_looks_exe "$file" "$dst_obj" || mode_bad_exe ;; + 100644) mode_looks_exe "$file" "$dst_obj" && mode_not_exe ;; + 160000) ;; + *) mode_non_file ;; + esac +} + +size_max_KiB=$(git config hooks.MaxObjectKiB) +test -n "$size_max_KiB" || size_max_KiB=1024 +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 the limit. +We prefer to keep large files out of the main source tree, especially +binary files that do not compress well. This hook disallows large files. + +If it is vital that this file enters the repository, speak to a dev lead. +Remember that unit tests should typically not require loading files (see +http://www.mantidproject.org/Unit_Test_Good_Practice#Using_files_in_Unit_tests). +' +# Instructions for allowing ESSENTIAL files only: +# A limit for specific files or patterns may be set in ".gitattributes" with +# the "hooks.MaxObjectKiBYYMMDD" attribute (where YYMMDD is the current date). +# For example, the line +# +# *.c hooks.MaxObjectKiB121207=2048 +# +# sets a limit of 2048 KiB for C source files, but WILL ONLY WORK on the date +# matching the entry. This avoids the previous behaviour that once set the +# hook was forever disabled if the entry was not removed afterwards. +# See "git help attributes" for details on the .gitattributes format. +} +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() { + test "$dst_obj" != "$zero" || return + datesuffix=`date +%y%m%d` + hookname='hooks.MaxObjectKiB'$datesuffix + max_KiB=$(git check-attr $hookname -- "$file" | + sed "s/^[^:]*: ${hookname}: //") + 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 +} + +short_commit() { + git rev-parse --short "$1" 2>/dev/null || echo "$1" +} + +lookup_config_module_update() { + update=$(git config "hooks.$1.update") + + # Special-case "true" to accept any update. + test "{$update}" = "{true}" && echo '.' && return + + # Format is "aaaaaa..bbbbbb" for update aaaaaa => bbbbbb. + # Convert to regex "^aaaaaa[a-z0-9]* bbbbbb[a-z0-9]*$". + sha1ex='[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]*' + regex='^\('"$sha1ex"'\)\.\.\('"$sha1ex"'\)$' + echo "$update" | + sed -n "/$regex/ {s/$regex/"'^\1[a-z0-9]* \2[a-z0-9]*$/;p;}' | + grep '.' # Return false if result is empty. +} + +check_module() { + enabled=$(git config --get --bool hooks.submodule) || enabled=true + test "$enabled" = "false" && return + + # Allow merged submodule updates. + test -n "$merge_head" && + merge_obj=$(git rev-parse -q --verify "$merge_head:$file") && + test "$merge_obj" = "$dst_obj" && return + + # Allow module-only commits without extra work. + test -z "$diffs_normal" && return + + # Check if module update is allowed with other changes. + allow=$(lookup_config_module_update "$file") || allow='none' + if echo "$src_obj $dst_obj" | grep "$allow" > /dev/null; then + return + fi + src_short=$(short_commit "$src_obj") + dst_short=$(short_commit "$dst_obj") + echo 'A submodule link is staged for commit (among other changes): + + "'"$file"'" '"$src_short => $dst_short"' + +This may occur by accident so we require an extra step to commit. +If you intend to include this change in your commit, run + + git config "hooks.'"$file"'.update" '"$src_short..$dst_short"' + +to explicitly allow the change and try the commit again. Otherwise run + + git reset HEAD -- "'"$file"'" + +to unstage the change. Furthermore, if you did not intend to modify +the submodule at all, also run + + git submodule update -- "'"$file"'" + +to checkout the current version of the submodule in your work tree. +Test your changes again to see if they still work with the module. +Finally, try the commit again. +' + return 1 +} + +check_module_rewind() { + parent_name="$1" + parent_commit="$2" + base=$(GIT_DIR="$file/.git" \ + git merge-base $src_obj $dst_obj 2>/dev/null) || base='' + test "$base" != "$dst_obj" && return + parent_short=$(short_commit "$parent_commit") + src_short=$(GIT_DIR="$file/.git" short_commit "$src_obj") + dst_short=$(GIT_DIR="$file/.git" short_commit "$dst_obj") + echo 'This commit would rewind a submodule link: + + "'"$file"'" '"$src_short => $dst_short"' + +from the newer version in '"$parent_name"' ('"$parent_short"'). Run + + git reset '"$parent_name"' -- "'"$file"'" + git submodule update -- "'"$file"'" + +to checkout the newer version of the submodule in your work tree. +Then try the commit again. +' + return 1 +} + +diffs=$(git diff-index --cached $against -- | + sed -n '/^:[^:]/ {s/^://;p;}') +diffs_normal=$(echo "$diffs" | grep -v '^...... 160000') +diffs_module=$(echo "$diffs" | grep '^...... 160000') +bad=$( +test -n "$diffs_normal" && echo "$diffs_normal" | +while read src_mode dst_mode src_obj dst_obj status file; do + if test "$src_mode" != "$dst_mode" -a "$dst_mode" != "000000"; then + check_mode + fi + if test "$dst_mode" != "160000" -a "$dst_mode" != '000000'; then + check_size + fi +done +test -n "$diffs_module" && echo "$diffs_module" | +while read src_mode dst_mode src_obj dst_obj status file; do + check_module_rewind HEAD "$against" && + check_module || + break +done +) +test -z "$bad" || die "$bad" + +#----------------------------------------------------------------------------- +# Merge checks. +if test -n "$merge_head"; then + merge_diffs=$(git diff-index --cached $merge_head -- | + sed -n '/^:[^:]/ {s/^://;p;}') +else + merge_diffs='' +fi +merge_diffs_normal=$(echo "$merge_diffs" | grep -v '^...... 160000') +merge_diffs_module=$(echo "$merge_diffs" | grep '^...... 160000') +bad=$( +test -n "$merge_diffs_module" && echo "$merge_diffs_module" | +while read src_mode dst_mode src_obj dst_obj status file; do + check_module_rewind MERGE_HEAD "$merge_head" || + break +done +) +test -z "$bad" || die "$bad" + +#----------------------------------------------------------------------------- +# Style hooks. +#. "$GIT_DIR/hooks/pre-commit-style" + +#----------------------------------------------------------------------------- +# Chain to project-specific hook. +#. "$GIT_DIR/hooks/hooks-chain.bash" +#hooks_chain pre-commit "$@" + +# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab :