16

I know removing trailing whitespace can be done with a pre-commit hook. I am interested in doing it manually. I read the question here:
Make git automatically remove trailing whitespace before committing - Stack Overflow
The answer closest to what I want is the "automatic version" from ntc2:

(export VISUAL=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset


That command works well except it seems to be only for changes on files that are already in the repo, not new files. I have a bunch of files that are new, meaning they aren't yet in the repo. I want to remove whitespace from those files so I tried add -A instead of -u but that didn't make a difference.

Community
  • 1
  • 1
loop
  • 3,460
  • 5
  • 34
  • 57
  • 1
    Do you mean "`git add -Ae` doesn't add new files at all"? Or: "the files are added, but not fixed"? – VonC Oct 03 '13 at 06:29
  • @VonC It doesn't work on files that are untracked or new (added for the first time but not yet committed). For me it shows `fatal: Empty patch. Aborted.` I'm using git version 1.8.3.msysgit.0. – loop Oct 03 '13 at 16:49
  • @test: if you had left a comment on my original answer, either asking how to make my command work, or linking to your question, I would have gotten a notification and could have told you about `add -N`. But, SO was smart enough to put your question in the "Related" section so I saw it when I edited my answer today. – ntc2 Nov 19 '13 at 04:19
  • vim users may also like this: http://stackoverflow.com/questions/356126/how-can-you-automatically-remove-trailing-whitespace-in-vim/13795287#13795287 – Michael Durrant Nov 19 '13 at 04:24

4 Answers4

33

To manually clean up whitespace from your last 3 commits, you can do this:

git rebase --whitespace=fix HEAD~3

When I work on a topic branch, I track the upstream branch (usually by creating it like this)

git checkout -b topic -t

Which allows me to drop the last argument from git rebase. So once I'm done & ready to merge, I can clean the whole topic branch quickly with:

git ws # aliased to rebase --whitespace=fix

Note that, unlike the HEAD~3 example, this will actually rebase your changes upon the upstream branch if it's changed! (But that's also what I want, in my workflow.)

Luke Usherwood
  • 3,082
  • 1
  • 28
  • 35
  • Luke that works but do you know why I can't use the command I asked about in my question? – loop Oct 06 '13 at 05:40
  • Beats me, I'm afraid; but given that whitespace=fix is intended for applying patches (including rebase) it doesn't surprise me that niggles arise. Stripping ws from untracked files is akin to editing files outside of the repo: this is not git's job. – Luke Usherwood Oct 07 '13 at 11:34
  • 2
    Hey, here's something I just concocted (from basic commands) to brush-up just the staged changes (leaving the selection of added files intact afterwards): `git commit -mTemp && git stash && git rebase HEAD~ --whitespace=fix && git reset --soft HEAD~ && git stash pop`. – Luke Usherwood Oct 07 '13 at 11:34
  • Thanks Luke I will try that next time – loop Oct 09 '13 at 04:28
  • You're my hero. Just had hundreds of whitespace errors to fix thanks to buggy eclipse formatter/save actions. :) – James Dec 11 '13 at 14:27
  • I love `git rebase --whitespace=fix` – Meredith Nov 19 '14 at 00:56
  • 2
    This is a good moment remark that, should you need to fix the initial commit as well, the incantation becomes `git rebase --whitespace=fix --root` (of course don't do this if you care about the clones of your repository) – badp Feb 10 '15 at 12:09
  • Thanks for --root! I'd been creating dummy/blank initial commits for this very reason. – Luke Usherwood Feb 11 '15 at 01:39
  • I've just had the chance to create a new repo, and found that `git rebase --whitespace=fix --root` didn't seem to work. It rebased but the whitespace remained in the commit. (git 2.9.2.windows.1) So, it's back to creating a blank initial commit for me... :-) – Luke Usherwood Aug 17 '16 at 19:00
8

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:

  1. hack hack hack, introduce whitespace error
  2. attempt to commit
  3. commit fails with whitespace error due to pre-commit hook
  4. git commit --no-verify to commit anyway
  5. git ws use 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.

jbyler
  • 7,200
  • 3
  • 34
  • 42
2

Simple fix

The command you quoted

(export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset

works if you first add the files you want to fix with git add -N <files you want to fix>. The add -N essentially tells Git to pretend you'd previously committed empty versions of the files.

Error you got

I don't understand why you get fatal: Empty patch. Aborted. error with add -Ae, but it looks like a bug, since doing plain git add -A . && git diff --cached shows that the patch should not actually be empty.

Better whitespace fixer

I recently updated my answer that you linked to with a better Git alias for fixing whitespace. Here's a rewrite of that alias using Luke's rebase trick and a less redundant control flow:

fixws =!"\
  if (! git diff-index --quiet --cached HEAD); then \
    \
    git diff-files --quiet `git rev-parse --show-toplevel` ; \
    export NEED_TO_STASH=$? ; \
    \
    git commit -m FIXWS_SAVE_INDEX && \
    if [ 1 = $NEED_TO_STASH ] ; then git stash save FIXWS_SAVE_TREE; fi && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ && \
    if [ 1 = $NEED_TO_STASH ] ; then git stash pop; fi ; \
  fi"

This fixes whitespace in the index, while preserving the index, and leaving the tree untouched. With this alias, you can fix unversioned files in the repo with

git add --all :/ && git fixws && git reset

But, it also handles the more common case of fixing up whitespace in a commit you're working on. It's complicated because it works even when the index or tree are clean.

Community
  • 1
  • 1
ntc2
  • 11,203
  • 7
  • 53
  • 70
  • If this leaves the tree untouched, doesn't that mean that any whitespace fixes in the index will show up in reverse in `git diff`, and will be reverted when you add affected files to the index? I guess that's why you have `git reset` in your pipeline, but it seems like that cancels out the benefit of leaving the tree untouched. – jbyler Mar 05 '14 at 23:56
  • @jbyler: if you're referring to the `reset` in the `add`, `fixws`, `reset` combo, then the point is to undo the initial `add`. Note that the default reset is `--mixed`, which only touches the index, not the tree. – ntc2 Mar 08 '14 at 01:22
  • Hi, I added your alias today and tried your better whitespace fixer command line. It didn't work initially because I had previously enabled the pre-commit hook for catching whitespace violations. I suspected what was happening so I disabled that and then ran your command and it worked. Thanks again! – loop Apr 08 '14 at 05:30
0

If you use emacs, you can use "M^x delete-trailing-whitespace" to delete them before saving the file. (it can be customized in your .emacs also)

vi seems to also allow this : http://vim.wikia.com/wiki/Remove_unwanted_spaces

Fabrice Jammes
  • 2,275
  • 1
  • 26
  • 39