#!/bin/sh # dune-git-whitespace-hook # DO NOT TOUCH THE PRECEDING LINE # It is used by dunecontrol to enable automatic updates of the whitespace hook # DUNE pre-commit hook to enforce whitespace policy # This hook prevents adding lines with trailing whitespace and or tab characters # in line indentation for certain files (see the TRAILING_WHITESPACE_DEFAULT and # TAB_IN_INDENT_DEFAULT variables below for the default sets of files that will # be checked). # You can tell the hook which files should be inspected by setting the Git # configuration variables "hooks.whitespace.trailing" and "hooks.whitespace.tabinindent". # Those variables should contain valid Perl regular expressions. The names of modified # files will be matched against those regexes. # git-diff-index needs a valid commit to compare to if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # By default, we disallow trailing whitespace for the following files, but the check for C/C++ and CMake sources # happens in the tab-in-indent check to avoid confusing users with duplicate error messages TRAILING_WHITESPACE_DEFAULT='.*(Makefile.am|configure.ac|dune.module|README|README.SVN|COPYING|INSTALL|TODO|\.pc\.in)$' # By default, we disallow tabs in indents and trailing whitespace in C/C++ and CMake source files TAB_IN_INDENT_DEFAULT='.*(\.cpp|\.hpp|\.cc|\.hh|\.c|\.h|\.cmake|CMakeLists.txt)$' # Get user preferences TRAILING_WHITESPACE_FILES=$(git config hooks.whitespace.trailing) # Set default regex for disallowing trailing whitespace if the user did not set anything. # We need to check the return value of git-config to distinguish the case # when the user set an empty value if [ $? -ne 0 ]; then TRAILING_WHITESPACE_FILES="$TRAILING_WHITESPACE_DEFAULT" fi TAB_IN_INDENT_FILES=$(git config hooks.whitespace.tabinindent) # Set default regex for disallowing tabs if the user did not set anything. # We need to check the return value of git-config to distinguish the case # when the user set an empty value if [ $? -ne 0 ]; then TAB_IN_INDENT_FILES="$TAB_IN_INDENT_DEFAULT" fi # Unfortunately, we have to mess directly with the repository config file, # as git won't honor a custom config file specified by GIT_CONFIG # backup repository-local user setting for core.whitespace USER_WHITESPACE=$(git config --local --get core.whitespace) if [ $? -ne 0 ]; then USER_HAS_CUSTOM_WHITESPACE=0 else USER_HAS_CUSTOM_WHITESPACE=1 fi # Figure out how to call xargs to make sure it won't invoke its argument with # an empty argument list. BSD xargs will not do that by default, while GNU xargs # needs -r to do the same. So we start by checking whether xargs does the right # thing without options. Now there could be other obscure versions of xargs out # there (on clusters etc.) that behave in yet another way, so we try with -r as # well. If that fails, we throw a big error message at the user. # In the following line, xargs should not call false, so the return value should be 0. echo "" | xargs false if [ $? -ne 0 ]; then # Let's try with -r echo "" | xargs -r false if [ $? -ne 0 ]; then # Houston, we have a problem if [ -z "$DUNE_WHITESPACE_IGNORE_XARGS" ]; then echo "You seem to be lacking a version of xargs that is compatible to either BSD or GNU!" 1>&2 echo "Please file a bug report at http://dune-project.org about this issue with your exact operating system type and version!" 1>&2 echo "You can still use this hook by setting the environment variable DUNE_WHITESPACE_IGNORE_XARGS to 1, but please be aware" 1>&2 echo "that the hook might create false positives." 1>&2 echo "==============================================================" 1>&2 echo "Aborting the commit..." 1>&2 exit 99 else SILENTXARGS=xargs fi else SILENTXARGS="xargs -r" fi else SILENTXARGS=xargs fi fail=0 done=0 do_cleanup() { if [ $done -ne 1 ]; then echo "Error while executing whitespace checking pre-commit hook!" 1>&2 echo "There might still be whitespace errors in your commit!" 1>&2 fi if [ $USER_HAS_CUSTOM_WHITESPACE -eq 1 ]; then git config --replace-all core.whitespace "$USER_WHITESPACE" else git config --unset core.whitespace fi # be nice and let the commit go through if something went wrong along the # way and we did not record a failure exit $fail } trap do_cleanup EXIT # set custom value git config --replace-all core.whitespace trailing-space if [ -z "$TRAILING_WHITESPACE_FILES" ]; then git diff-index --check --cached $against -- result=$? else git diff-index --cached --name-only $against | perl -ne "print if /$TRAILING_WHITESPACE_FILES/" | $SILENTXARGS git diff-index --check --cached $against -- result=$? fi if [ $result -ne 0 ]; then fail=1 fi git config --replace-all core.whitespace trailing-space,tab-in-indent if [ -z "$TAB_IN_INDENT_FILES" ]; then git diff-index --check --cached $against -- result=$? else git diff-index --cached --name-only $against | perl -ne "print if /$TAB_IN_INDENT_FILES/" | $SILENTXARGS git diff-index --check --cached $against -- result=$? fi if [ $result -ne 0 ]; then fail=1 fi done=1 # trap will call the cleanup code