I like Luke's answer, except for the limitation that you need to either manually specify the base commit, or use a rebase-style workflow, where your history is linearized.  I propose a modification that doesn't need an extra argument and doesn't change the topology of your commit graph.  As a shell command:
git rebase --whitespace=fix --onto $(git merge-base HEAD @{u})
Or as a ~/.gitconfig alias:
ws = "!git rebase --whitespace=fix --onto $(git merge-base HEAD @{u})"
I prefer this because sometimes I want to rebase my changes, but if I think think there might be a merge conflict I prefer to merge, so that both my original change and the conflict resolution will be recorded in the history.  That way I can later second-guess the conflict resolution and redo it if necessary.
Given that I don't always rebase, I prefer not to mix whitespace-fixing with rebasing; hence this modification to Luke's answer.
In addition, I enable the default pre-commit hook which aborts on whitespace errors:
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
This gives the following workflow, which I like because it's manual enough that I know what's going on but automated enough not to get in the way:
- hack hack hack, introduce whitespace error
- attempt to commit
- commit fails with whitespace error due to pre-commit hook
- git commit --no-verifyto commit anyway
- git wsuse the alias to fix
Note on the usage of --onto: It's not necessary here, but I find it easier to reason about how the rebase works this way.  In Luke's version, HEAD~3 is the <upstream> in the man page, while in my version <upstream> keeps its default value of the real upstream of the branch.  You wind up with the same result either way though.