Skip to content

Instantly share code, notes, and snippets.

@feosuna1
Last active November 7, 2022 15:45
Show Gist options
  • Save feosuna1/14877 to your computer and use it in GitHub Desktop.
Save feosuna1/14877 to your computer and use it in GitHub Desktop.
Git hook for cleaning white spaces
#!/usr/bin/env bash
#==============================================================================#
# Remove whitespace errors from text files.
#
# If a whitespace error is found, it notifies the user and fixes the errors.
# This script will only run if the `git config` variable `apply.whitespace` is
# set to fix. You can use `git config` to make this change, use the `--global`
# to apply these changes globally to all your git repos:
# ```
# $ git config apply.whitespace fix
# ```
#
# You can configure this script to be a part of your pre-commit hooks. You can
# create an executable pre-commit file that references `git-clean-whitespace`:
# ```
# #!/usr/bin git-clean-whitespace
# ```
#
# Or you can create a symbolic link:
# ```
# ln -s ~/bin/git-clean-whitespace .git/hooks/pre-commit
# ```
#
# Alternatively, you can add this to your git template directory so that
# the script is automagically included with each git repo instance.
#==============================================================================#
set -e
function _error() {
>&2 echo "_error: $@"
}
function assert_git() {
local git=`which git`
if [[ -z "$git" ]]; then
_error 'unable to find the `git`'
exit 1
fi
}
function assert_in_git_repository() {
# Check to see if we are working within a git repository -- it's kind of hard to fix a whitespace in git if we are not
# in a git repo.
if [[ $? -ne 0 ]]; then
_error 'you must invoke from a working git repository.'
exit 2
fi
}
function exit_quietly_if_in_middle_of_merge() {
local merge_head=`git rev-parse -q --verify MERGE_HEAD`
if [[ -n "$merge_head" ]]; then
exit 0
fi
}
function exit_quietly_if_whitespace_not_configured_to_fix() {
local apply_whitespace="$@"
if [[ -z "$apply_whitespace" || "$apply_whitespace" == 'nowarn' ]]; then
exit 0
fi
}
function get_files_with_whitespace_errors() {
git diff --cached --check "$@" | sed '/^\+/d ; s/:.*//' | uniq
}
function _fix_whitespace_errors_for_file() {
# Get the diff of what is currently staged
local file="$@"
local diff=`git diff --cached -- "$file"`
# Unstage the diff so when we reapply it will fix the whitespaces for us
echo "$diff" | git apply --cached -R - > /dev/null 2>&1
# Add the diff back on, this time using the 'apply' command and it will fix the whitespace errors
echo "$diff" | git apply --cached --whitespace=fix - > /dev/null 2>&1
}
function fix_whitespaces_errors() {
# Figure out which files have whitespace errors and strip them
local files_with_whitespace_errors=`get_files_with_whitespace_errors`
if [[ -z "$files_with_whitespace_errors" ]]; then
return
fi
local git_toplevel_dir=`git rev-parse --show-toplevel`
while IFS= read -r file; do
local file="$git_toplevel_dir/$file"
echo "warning: removing whitespaces for $file"
_fix_whitespace_errors_for_file "$file"
if [[ -n `get_files_with_whitespace_errors -- $file` ]]; then
# For some reason, EOF newline errors don't get fixed on the first pass,
# but they get fixed on the second one.
_fix_whitespace_errors_for_file "$file"
fi
done <<< "$files_with_whitespace_errors"
}
assert_git
assert_in_git_repository
exit_quietly_if_in_middle_of_merge
apply_whitespace=`git config apply.whitespace`
exit_quietly_if_whitespace_not_configured_to_fix $apply_whitespace
if [[ "$apply_whitespace" != 'fix' ]]; then
exec get_files_with_whitespace_errors
else
fix_whitespaces_errors
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment