How can you combine git add patch -p mode with diff's ignore-all-space
Asked Answered
O

4

41

How can I do git add with patch mode but ignoring whitespace changes.

The use case is for when you've reformatted a file and also made changes to it. I want to commit the real code changes separately first (as shown by git diff -w path) and then commit the reformatting as a separate commit.

Oriane answered 4/7, 2011 at 12:40 Comment(5)
The 'g' option to git add --patch will help; whitespace changes will appear empty in the list of hunks to choose.Boland
Which version of git is the g option added in?Oriane
It was in 1.6.1-rc1-37, so the first official release with it would have been 1.6.2. (It is not actually an 'option', but a navigation menu selection that is used when a hunk is displayed.)Boland
Note that g will not do what I was thinking: I interpreted "whitespace changes" to mean "inserting or deleting blank lines". 'g' will show blank headers for those types of changes, but not for insertion/deletion of whitespace.Boland
Possible duplicate of Add only non-whitespace changesBassist
L
1

Note: This answer is old. 6 years down the road, the other answer by Justin is much better. Prefer to use git apply --cached

I suggest simply roundtripping a diff

Idea:

git diff --ignore-all-space | (git reset --hard && git apply)

Warning: this is fraught with danger because of the git reset there (it will not preserve changes to binary files as written). Perhaps you'd want a bash function similar to

function cleanup_patch()
{
    if [ $# -lt 1 ]; then 
        echo 'Must provide explicit paths (wildcards allowed)'; 
    else
        git diff --ignore-all-space -- "$@" |
            (git checkout HEAD -- "$@" &&
             git apply)
    fi
}

Afaict the seemingly useful --binary option to diff doesn't honour the whitespace ignore flags

Lepidolite answered 4/7, 2011 at 13:2 Comment(8)
I did it by redirecting diff's output to a file. Then using checkout path and finally apply with --ignore-whitespace. If you want to edit your answer I can accept it.Oriane
@Sam: if my answer helped you, that's enough. Why is using a temporary file substantially better? What you describe appears to be exactly what the cleanup_patch function does, only with added tedium. I might be missing somethingLepidolite
Well outputting to a file is just safety wheels for when you are not using diff & patch that often. You can check that the patch looks sane and if you screw after reseting/checkouting you can at least manually apply the patches changes. Iow me being a n00b. Anyway don't you at least think checking out a specific path is safer than reseting the whole working dir? Oh now I see your shell function actually does that X_X I'll continue thinking out loud. Shouldn't you use the --ignore-whitespace flag with apply?Oriane
@Sam: ignoring whitespace on apply is redundant. Glad you noticed the checkout in the bash function. By all means, use a temp file if you want. My preference would be to commit/reset HEAD^ or use git stash if I were doubting it. However, I usually cleanup patches manually anywayLepidolite
So why does apply have that flag?Oriane
@Sam: it is there in case you do need it, like all other options really. In this case we're feeding it a diff that ignores all space changes, so I'd consider it a bug if apply could ignore further white spaceLepidolite
git reset --hard will remove your current changesServiceman
@AnkurLoriya that's on purpose but I agree it makes the one liner approach to risky. The other answer should be the accepted answer. Will add a note at the top in a few minutesLepidolite
M
25

Here's an adaptation from a related question.

git diff -w --no-color | git apply --cached --ignore-whitespace

It has the benefit that you don't need to use stash, temporary files, or perform a reset --hard on your working folders.

Addendum

The solution above only stages changes except whitespace-only edits. This did not address patch, though using --patch to stage isn't straight forward in this situation.

Patch Option 1: Edit the diff in a text editor

There are many ways to implement this using a text editor. Vim is especially suited to this.

In the root directory of your repository, start Vim.

In normal mode, load the diff into an empty buffer with...

:r !git diff -w --no-color
:set ft=diff  # if you want syntax highlighting

Edit the diff and remove the parts you don't want to stage.

To stage the contents of the vim buffer, run the vim ex command...

:w !git apply --cached --ignore-whitespace

If you're a Vim afficionado, you could use visual mode to stage, too!

:<',>'w !git apply --cached --ignore-whitespace

You can commit the staged changes with the ex command...

:!git commit -m "message"
# or
:!git commit

Clear the buffer, read the unstaged changes, and repeat

:bd! | set ft=diff | r !git diff -w --no-color

Eventually, you'll be left with only whitespace changes to commit.

If you don't use Vim, you could also dump git diff into a file, edit the file, save, then feed the file into git apply. Commit and repeat until done. It's a bit tedious, but functional.

Patch Option 2: Patch reset

It's backwards from git add --patch, but once you've staged non-whitespace changes with...

git diff -w --no-color | git apply --cached --ignore-whitespace

...you can unstage chunks in patch mode with...

git reset --patch .

Keep in mind you're removing the changes that you want to keep staged. Repeat and commit as necessary until you only have whitespace changes left.

Merriemerrielle answered 12/3, 2013 at 5:32 Comment(4)
Well that's just fabulous. Very useful after Visual Studio poops all over web.config.Pfeffer
How does have so many upvotes. It completely fails to include git add --patch, allowing the user to go through all of their changes in hunks, but ignoring whitespace changes?Psalm
I completely overlooked "patch", Chucky. Updated with some alternatives to your solution. Thanks!Merriemerrielle
This can fail in some complicated cases when the context has whitespace errors that are ignored by git diff. The unidiff-zero options on diff and apply can help by removing all contexts: https://mcmap.net/q/393301/-is-there-a-way-to-show-only-whitespace-differences-with-git-diffLippizaner
P
4

If you want to do git add --patch but ignore all whitespace like the asker is asking, you can do this in one command:

git diff -w --no-color | git apply --cached --ignore-whitespace && git checkout -- . && git reset && git add -p

git diff -w --no-color creates a diff

git apply --cached --ignore-whitespace applies the diff ignoring whitepace, and indexes it

git checkout -- . removes the unindexed “whitespace” changes

git reset resets the index to just the non-whitespace changes

git add -p adds the non-whitespace changes in patch mode

Wrap this up in an alias, like so:

alias gwap=“git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero && git checkout -- . && git reset && git add -p”

Or if you're on a unix based system like I am:

gwap= !git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero && git checkout -- . && git reset && git add -p 

(Notice I added options -U0, and --unidiff-zero respectively to workaround context matching issues, according to this comment.)

Source: https://til.hashrocket.com/posts/696df00135-remove-whitespace-changes-then-git-add-p

Psalm answered 27/6, 2017 at 16:59 Comment(5)
It might also be useful to stash the whitespace changes and then pop them after this command, just for safety. I wasn't sure how to do this without stashing everything in the index. Does anyone have any idea?Psalm
I don't know of anyway to selectively stash, and I've looked extensively for that ability. However, this solution works great if your whitespace-only changes are caused by a formatting tool (e.g. gofmt or rustfmt). I just rerun the formatting tool after committing to restore the whitespace changes.Merriemerrielle
After git diff | git apply, maybe git stash --keep-index will work to save the whitespace changes. It'll stash all changes, but leave the index alone. git reset && git add -p, commit until done, then git stash pop should restore the whitespace changes. Inspired by this answer.Merriemerrielle
I gave that a try, Justin. But merge conflicts occur when popping the stash.Psalm
I feared as much. :/ I guess a temp file will have to suffice. Before git checkout -- ., git diff -U0 --no-color > some-temp-file, then at the end, <some-temp-file git apply --cached --unidiff-zero && rm some-temp-file.Merriemerrielle
L
1

Note: This answer is old. 6 years down the road, the other answer by Justin is much better. Prefer to use git apply --cached

I suggest simply roundtripping a diff

Idea:

git diff --ignore-all-space | (git reset --hard && git apply)

Warning: this is fraught with danger because of the git reset there (it will not preserve changes to binary files as written). Perhaps you'd want a bash function similar to

function cleanup_patch()
{
    if [ $# -lt 1 ]; then 
        echo 'Must provide explicit paths (wildcards allowed)'; 
    else
        git diff --ignore-all-space -- "$@" |
            (git checkout HEAD -- "$@" &&
             git apply)
    fi
}

Afaict the seemingly useful --binary option to diff doesn't honour the whitespace ignore flags

Lepidolite answered 4/7, 2011 at 13:2 Comment(8)
I did it by redirecting diff's output to a file. Then using checkout path and finally apply with --ignore-whitespace. If you want to edit your answer I can accept it.Oriane
@Sam: if my answer helped you, that's enough. Why is using a temporary file substantially better? What you describe appears to be exactly what the cleanup_patch function does, only with added tedium. I might be missing somethingLepidolite
Well outputting to a file is just safety wheels for when you are not using diff & patch that often. You can check that the patch looks sane and if you screw after reseting/checkouting you can at least manually apply the patches changes. Iow me being a n00b. Anyway don't you at least think checking out a specific path is safer than reseting the whole working dir? Oh now I see your shell function actually does that X_X I'll continue thinking out loud. Shouldn't you use the --ignore-whitespace flag with apply?Oriane
@Sam: ignoring whitespace on apply is redundant. Glad you noticed the checkout in the bash function. By all means, use a temp file if you want. My preference would be to commit/reset HEAD^ or use git stash if I were doubting it. However, I usually cleanup patches manually anywayLepidolite
So why does apply have that flag?Oriane
@Sam: it is there in case you do need it, like all other options really. In this case we're feeding it a diff that ignores all space changes, so I'd consider it a bug if apply could ignore further white spaceLepidolite
git reset --hard will remove your current changesServiceman
@AnkurLoriya that's on purpose but I agree it makes the one liner approach to risky. The other answer should be the accepted answer. Will add a note at the top in a few minutesLepidolite
E
1

A more robust and versatile version of @"Justin C"s answer is:

anw = !git diff -U0 -w --no-color -- \"$@\" | git apply --cached --ignore-whitespace --unidiff-zero "#"
  • With no argument - adds all tracked files' non-whitespace changes
  • Given files/directories - only adds non-whitespace changes in those locations

See this answer for more.

Extempore answered 14/9, 2016 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.