404

I have my text editor to automatically trim trailing whitespace upon saving a file, and I am contributing to an open source project that has severe problems with trailing whitespace.

Every time I try to submit a patch I must first ignore all whitespace-only changes by hand, to choose only the relevant information. Not only that, but when I run git rebase I usually run into several problems because of them.

As such I would like to be able to add to index only non-whitespace changes, in a way similar that git add -p does, but without having to pick all the changes myself.

Does anyone know how to do this?

EDIT: I cannot change the way the project works, and they have decided, after discussing it on the mailing list, to ignore this.

0

13 Answers 13

465

@Frew solution wasn't quite what I needed, so this is the alias I made for the exact same problem:

alias.addnw=!sh -c 'git diff -U0 -w --no-color --src-prefix=a/ --dst-prefix=b/ "$@" | git apply --cached --ignore-whitespace --unidiff-zero -'

Or you can simply run:

git diff -U0 -w --no-color --src-prefix=a/ --dst-prefix=b/ | git apply --cached --ignore-whitespace --unidiff-zero -

Update

Added options -U0, and --unidiff-zero respectively to workaround context matching issues, according to this comment.

Basically it applies the patch which would be applied with add without whitespace changes. You will notice that after a git addnw your/file there will still be unstaged changes, it's the whitespaces left.

The --no-color isn't required but as I have colors set to always, I have to use it. Anyway, better safe than sorry.

Warning

While this trick works as-is, if you try to use it to drop blank line changes with --ignore-blank-lines then things get complicated. With this option, git diff will just drop some chunks, making the resulting patch bogus since the line numbers in the destination file are going to be off.

28
  • 7
    This worked well for me, however I had to use git apply --ignore-whitespace otherwise the patch would not apply for obvious reasons.
    – jupp0r
    Commented Oct 10, 2012 at 13:46
  • 134
    There should really be an option to git add, like git add -w that did this.
    – Jarl
    Commented Apr 18, 2014 at 15:40
  • 7
    This gives me problems with patch does not apply and error while searching for... Any ideas?
    – DTI-Matt
    Commented Jul 27, 2015 at 14:17
  • 18
    didn't work for me. Got a patch does not apply error. Commented Sep 15, 2015 at 16:57
  • 16
    If you're getting 'patch failed' due to whitespace in context as @bronson points out, this revised command works (It generates a patch with no context): git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero. This isn't risky because the index is already as up-to-date as can be, so it's a reliable base for the patch. Commented May 12, 2016 at 15:34
46

Create a patch file containing only the real changes (excluding lines with only whitespace changes), then clean your workspace and apply that patch file:

git diff > backup
git diff -w > changes
git reset --hard
patch < changes

Review the remaining differences, then add and commit as normal.

The equivalent for Mercurial is to do this:

hg diff > backup
hg diff -w > changes
hg revert --all
hg import --no-commit changes

15
  • 5
    @jww The core of the original poster's question is "how to avoid committing white-space only changes to source control". The OP happens to be using Git, but this also applies to every source control system I've ever used. This answer shows the correct procedure if someone happens to be using Mercurial. I could imagine someone else might also contribute a solution for people using Sublesion, etc. Commented Jul 24, 2015 at 10:30
  • 4
    @jww and @ pagid: I edited my answer to specifically address Git, using the same approach as my solution for Mercurial. In my view, StackOverflow is more than just another Q+A forum - it also has a role as a repository of knowledge. People other than the original poster might benefit from the answers given, and their circumstances vary. That's why I believe answers conveying a general principle are valid, rather than targeting only a single specific situation. Commented Jul 24, 2015 at 18:28
  • 13
    This is actually the cleanest, most understandable, and most unbreakable of the approaches that I have seen.
    – Kzqai
    Commented Jun 30, 2016 at 11:38
  • 1
    @Gangnus It is intended to feed the contents from the file "changes" into the "patch" command. Is it possible you have a deficient shell that does not support this kind of redirection? Commented Jan 24, 2018 at 21:02
  • 1
    @Gangnus If you download Git Bash or Cygwin Bash the script will run on Windows. Or if you are on Windows 10, it could be even easier with the new subsystem! Commented Jan 26, 2018 at 9:15
41

This works for me:

If you want to keep a stash around, this works

git stash && git stash apply && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

I don't like the stashes, but I have run into a bug in git + cygwin where I lose changes, so to make sure that stuff went to the reflog at least I set up the following:

git add . && git commit -am 'tmp' && git reset HEAD^ && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

Basically we create a diff that doesn't include the space changes, revert all of our changes, and then apply the diff.

9
  • 1
    +1. You might want to do a git stash instead of checkout, to have a backup of your changes, at least until it is tested. Commented Jun 10, 2011 at 19:59
  • 1
    You'll end up with a lot of stashes and basically you don't really need to do all of this. It works but I think it's a bit messy Commented Aug 22, 2011 at 15:16
  • 3
    I agree with Colin. If the script works, then there should be no need to create a stash. What might be good to consider though would be to run stash, then stash pop. Popped stashes can be recovered if necessary, but you won't end up with a lot of stashes otherwise. This also leaves an extra file lying around
    – Casebash
    Commented Feb 7, 2012 at 0:51
  • 1
    How about skipping binary files? When trying to apply the above snippet, I get errors that the patch cannot be applied without the full index line! What beats me is that I didnt even touch these files/binaries in the first place!
    – tver3305
    Commented Apr 13, 2012 at 12:21
  • 1
    I think at the end of the first command "git rm foo.patch" should just be "rm foo.patch". Otherwise very helpful thanks.
    – Jack Casey
    Commented Jul 2, 2012 at 5:19
19

Top-voted answer does not work in all cases, due to whitespace in the patch context according to users in the comments.

I revised the command as follows:

$ git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero

This generates a patch with no context. Shouldn't be a problem since the patch is short-lived.

Corresponding alias, again a revision of what was already provided by other users:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -
2
  • To only ignore indentation changes I had to use --ignore-space-change instead of -w. git diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero
    – Andrew
    Commented Jun 19, 2016 at 15:39
  • 1
    A word of warning not to use this lovely no context trick with --ignore-blank-lines else you will find diff chunks being patched at incorrect offsets if some of the 'white space' changes you're looking to ignore are empty line removals/additions. Commented Jul 23, 2018 at 7:13
14

Add an alias to your .gitconfig with this command:

git config --global alias.anw '!git -c diff.noprefix=false diff -U0 -w --no-color -- "$@" | git apply --cached --ignore-whitespace --unidiff-zero #'

Thanks to @Colin Herbert's answer for the inspiration.

Syntax Explanation

The final # must be quoted so it's not treated it as a comment inside the .gitconfig, but instead gets passed through and is treated as a comment inside the shell - it is inserted between the end of the git apply and the user-supplied arguments that git automatically places at the end of the command line. These arguments aren't wanted here - we don't want git apply to consume them, hence the preceding comment character. You may want to run this command as GIT_TRACE=1 git anw to see this in action.

The -- signals end of arguments and allows for the case that you have a file named -w or something that would look like a switch to git diff.

Double-quotes around $@ are required to preserve any user-supplied quoted arguments.

1
  • Excellent. This allowed me to add one file at a time. Other than adding a root directory for an argument, is there a way to make this work like 'git add -A'? Commented Jan 23, 2017 at 10:47
7

How about the following:

git add `git diff -w --ignore-submodules |grep "^[+][+][+]" |cut -c7-`

The command inside backquotes gets the names of files which have non-whitespace changes.

1
  • 2
    or just git add `git diff -w |grep '^+++' |cut -c7-` if submodules not used
    – karmakaze
    Commented Dec 23, 2013 at 15:27
2

Similar to @void.pointer's answer, but for fixing the most recent commit.

git reset --mixed HEAD^
git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero -

This leaves the whitespace changes unstaged and the rest staged.

0

It works for me :

git config apply.whitespace fix

Before each commit use command :

git add -up .
0

This is my hack.

git diff -w | grep "diff --git a/*" | sed -r 's#diff --git a/(.*) b(.*)#\1#g' | xargs git add 

git diff -w only shows files with non whitespace changes,

0

I have my text editor to automatically trim trailing whitespace upon saving a file

Doesn't your editor have proj / dir specific settings? Could just disable your whitespace preferences for this project. Seems like a much easier solve...

0

To stage only the changes that are not just whitespace changes, you can do:

git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero -

Optionally, to remove all un-staged changes afterwards (those changes that differ only in whitespace), you can do:

git checkout . (or, in newer gits, git restore .)
-1

I found a git pre-commit hook that removes trailing whitespace. However, if you can't get others to use this, then it might not be a valid solution.

  #!/bin/sh

  if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
     against=HEAD
  else
     # Initial commit: diff against an empty tree object
     against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  fi
  # Find files with trailing whitespace
  for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do
     # Fix them!
     sed -i 's/[[:space:]]*$//' "$FILE"
  done
  exit
2
  • 4
    This question is asking how to preserve trailing whitespace.
    – Douglas
    Commented Aug 18, 2010 at 20:50
  • @Douglas: one could probably use this answer to create a commit on a temporary branch, commit the real patch there and cherry-pick the diff only into the working branch somehow... Commented Feb 23, 2011 at 14:51
-2

You should first consider if the trailing whitespace is intentional. Many projects, including the Linux kernel, Mozilla, Drupal, and Kerberos (to name a few from the Wikipedia page on style) prohibit trailing whitespace. From the Linux kernel documentation:

Get a decent editor and don't leave whitespace at the end of lines.

In your case, the problem is the other way around: previous commits (and maybe current ones) did not follow this guideline.

I'd wager that no one really wants the trailing whitespace, and fixing the problem might be a welcome change. Other users might also be experiencing the same problem you are. It's also likely that the contributor(s) who are adding trailing whitespace are unaware that they are doing so.

Rather than trying to reconfigure git to ignore the problem, or disabling the otherwise desirable functionality in your editor, I'd start off with a post to the project mailing list explaining the problem. Many editors (and git itself) can be configured to deal with trailing whitespace.

2
  • 20
    It's not intentional, but I cannot change the way 100+ people who contribute the project think. They don't mind it, and won't accept patches with 1000+ changes what only deal with trailing whitespace. They know about the problem and have decided to ignore it. This discussion already happened in the list and was closed. In this case, it's me who needs to adapt to them.
    – Edu Felipe
    Commented Aug 18, 2010 at 19:13
  • 21
    Then configure your editor such that it doesn't trim trailing whitespace when working on this project's code.
    – jamessan
    Commented Aug 18, 2010 at 19:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.