git remove trailing whitespace in new files before commit
Asked Answered
P

4

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.

Preference answered 3/10, 2013 at 6:5 Comment(4)
Do you mean "git add -Ae doesn't add new files at all"? Or: "the files are added, but not fixed"?Superdreadnought
@Superdreadnought 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.Preference
@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.Williwaw
vim users may also like this: #356626Blear
U
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.)

Underline answered 3/10, 2013 at 10:22 Comment(9)
Luke that works but do you know why I can't use the command I asked about in my question?Preference
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.Underline
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.Underline
Thanks Luke I will try that next timePreference
You're my hero. Just had hundreds of whitespace errors to fix thanks to buggy eclipse formatter/save actions. :)Ghat
I love git rebase --whitespace=fixNeedy
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)Allegory
Thanks for --root! I'd been creating dummy/blank initial commits for this very reason.Underline
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... :-)Underline
S
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.

Sparling answered 5/3, 2014 at 23:47 Comment(1)
Nice usage of the --onto there. +1Superdreadnought
W
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.

Williwaw answered 19/11, 2013 at 3:30 Comment(3)
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.Sparling
@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.Williwaw
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!Preference
T
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

Tranquilizer answered 3/10, 2013 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.