The point in removing trailing whitespace is that if everyone does it always then you end up with a diff that is minimal, ie. it consists only of code changes and not whitespace
The following will get you most of the way there:
$ clean=`git diff --cached -b`; \ git apply --cached <(git diff --cached -R); \ echo "$clean" | git apply --cached -; \ clean=
For releases of git prior to 1.7.0, it fails if one or more files have all-whitespace changes. For example
$ git diff --cached -b diff --git a/file1 b/file1 index b2bd1a5..3b18e51 100644 diff --git a/file2 b/file2 new file mode 100644 index 0000000..092bfb9 --- /dev/null +++ b/file2 [...]
The empty delta (of file1
above, which really ought to be suppressed) makes git-apply
unhappy:
fatal: patch with only garbage at line 3
UPDATE: The 1.7.0 release of git fixes this issue.
Say our repository is in the following state:
$ git diff --cached diff --git a/foo b/foo index 3b18e51..a75018e 100644 --- a/foo +++ b/foo @@ -1 +1,2 @@ -hello world +hello world +howdy also
We could then run the above commands to fork the index and work tree:
$ git diff --cached diff --git a/foo b/foo index 3b18e51..1715a9b 100644 --- a/foo +++ b/foo @@ -1 +1,2 @@ hello world +howdy also $ git diff diff --git a/foo b/foo index 1715a9b..a75018e 100644 --- a/foo +++ b/foo @@ -1,2 +1,2 @@ -hello world +hello world howdy also
If all changes are whitespace-only, you'll see
error: No changes
I suspect fixing the index and leaving undesired changes in the work tree would surprise or even irritate most users, but that's the behavior the question asked for.
I'd been using a script based on @GregBacon's answer for years, but at some point git changed the output of git diff -b
so that the whitespace changes it didn't show as changes were included in the patch context lines in the after state rather than the before state.
I found if I reversed the clean diff and the corresponding apply, the script would work again.
So my complete script used to look like this:
#!/bin/bash
clean=`git diff --cached -b`
git apply --cached <(git diff --cached -R)
echo "$clean" | git apply --cached -
clean=
And now looks like:
#!/bin/bash
clean=`git diff --cached -b -R`
git apply --cached <(git diff --cached -R)
echo "$clean" | git apply --cached -R -
clean=
I also have a script to leave only whitespace changes in a commit. This is useful if while you fix something you make some whitespace fixes elsewhere in the file, and you want to commit the whitespace fixes first in a separate commit. It is just:
git apply --cached -R <(git diff --cached -w)
My solution on git 1.7.2.5 was as follows (starting without any changes staged):
git diff -w > temp.patch
git stash
git apply --ignore-space-change --ignore-whitespace temp.patch
# tidy up:
rm temp.patch
git stash drop
This leaves your repo back in the starting state with any whitespace only changes removed.
You can then stage your changes as usual.