You can trick Git into fixing the whitespace for you, by tricking Git into treating your changes as a patch. In contrast to the "pre-commit hook" solutions, these solutions add whitespace-fixing commands to Git.
Yes, these are hacks.
Robust solutions
The following Git aliases are taken from
my ~/.gitconfig
.
By "robust" I mean that these aliases run without error, doing
the right thing, regardless of whether the tree or index are dirty. However, they don't work if an interactive git rebase -i
is already in progress; see my ~/.gitconfig
for additional checks if you care about this corner case, where the git add -e
trick described at the end should work.
If you want to run them directly in the shell, without creating a Git
alias, just copy and paste everything between the double quotes
(assuming your shell is Bash like).
Fix the index but not the tree
The following fixws
Git alias fixes all whitespace errors in the index,
if any, but doesn't touch the tree:
# Logic:
#
# The 'git stash save' fails if the tree is clean (instead of
# creating an empty stash :P). So, we only 'stash' and 'pop' if
# the tree is dirty.
#
# The 'git rebase --whitespace=fix HEAD~' throws away the commit
# if it's empty, and adding '--keep-empty' prevents the whitespace
# from being fixed. So, we first check that the index is dirty.
#
# Also:
# - '(! git diff-index --quiet --cached HEAD)' is true (zero) if
# the index is dirty
# - '(! git diff-files --quiet .)' is true if the tree is dirty
#
# The 'rebase --whitespace=fix' trick is from here:
# https://mcmap.net/q/119233/-git-remove-trailing-whitespace-in-new-files-before-commit
fixws = !"\
if (! git diff-files --quiet .) && \
(! git diff-index --quiet --cached HEAD) ; then \
git commit -m FIXWS_SAVE_INDEX && \
git stash save FIXWS_SAVE_TREE && \
git rebase --whitespace=fix HEAD~ && \
git stash pop && \
git reset --soft HEAD~ ; \
elif (! git diff-index --quiet --cached HEAD) ; then \
git commit -m FIXWS_SAVE_INDEX && \
git rebase --whitespace=fix HEAD~ && \
git reset --soft HEAD~ ; \
fi"
The idea is to run git fixws
before git commit
if you have
whitespace errors in the index.
Fix the index and the tree
The following fixws-global-tree-and-index
Git alias fixes all whitespace
errors in the index and the tree, if any:
# The different cases are:
# - dirty tree and dirty index
# - dirty tree and clean index
# - clean tree and dirty index
#
# We have to consider separate cases because the 'git rebase
# --whitespace=fix' is not compatible with empty commits (adding
# '--keep-empty' makes Git not fix the whitespace :P).
fixws-global-tree-and-index = !"\
if (! git diff-files --quiet .) && \
(! git diff-index --quiet --cached HEAD) ; then \
git commit -m FIXWS_SAVE_INDEX && \
git add -u :/ && \
git commit -m FIXWS_SAVE_TREE && \
git rebase --whitespace=fix HEAD~2 && \
git reset HEAD~ && \
git reset --soft HEAD~ ; \
elif (! git diff-files --quiet .) ; then \
git add -u :/ && \
git commit -m FIXWS_SAVE_TREE && \
git rebase --whitespace=fix HEAD~ && \
git reset HEAD~ ; \
elif (! git diff-index --quiet --cached HEAD) ; then \
git commit -m FIXWS_SAVE_INDEX && \
git rebase --whitespace=fix HEAD~ && \
git reset --soft HEAD~ ; \
fi"
To also fix whitespace in unversioned files, do
git add --intent-to-add <unversioned files> && git fixws-global-tree-and-index
Simple but not robust solutions
These versions are easier to copy and paste, but they don't do the
right thing if their side conditions are not met.
Fix the sub-tree rooted at the current directory (but resets the index if it's not empty)
Using git add -e
to "edit" the patches with the identity editor :
:
(export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset
Fix and preserve the index (but fails if the tree is dirty or the index is empty)
git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset --soft HEAD~
Fix the tree and the index (but resets the index if it's not empty)
git add -u :/ && git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset HEAD~
Explanation of the export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .
trick
Before I learned about the git rebase --whitespace=fix
trick from this answer I was using the more complicated git add
trick everywhere.
If we did it manually:
Set apply.whitespace
to fix
(you only have to do this once):
git config apply.whitespace fix
This tells Git to fix whitespace in patches.
Convince Git to treat your changes as a patch:
git add -up .
Hit a+enterto select all changes for each file. You'll get a warning about Git fixing your whitespace errors.
(git -c color.ui=auto diff
at this point reveals that your non-indexed changes are exactly the whitespace errors).
Remove the whitespace errors from your working copy:
git checkout .
Bring back your changes (if you aren't ready to commit them):
git reset
The GIT_EDITOR=:
means to use :
as the editor, and as a command
:
is the identity.