Skip to content
Snippets Groups Projects
Forked from Core Modules / dune-common
2069 commits behind the upstream repository.
dune-git-whitespace-hook 5.37 KiB
#!/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='^(dune\.module|README|README\.SVN|COPYING|INSTALL|TODO)$|^[^/]*(\.md|\.pc\.in)$|^doc/.*\.md$'

# By default, we disallow tabs in indents and trailing whitespace in C/C++ and CMake source files
TAB_IN_INDENT_DEFAULT='(^|/)CMakeLists\.txt$|(\.cpp|\.hpp|\.cc|\.hh|\.c|\.h|\.cmake|\.sh|\.py)$'

# 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
  export TRAILING_WHITESPACE_FILES
  git diff-index --cached --name-only $against                  \
    | perl -ne 'print if /$ENV{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
  export TAB_IN_INDENT_FILES
  git diff-index --cached --name-only $against                  \
    | perl -ne 'print if /$ENV{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