heyitsbmo
heyitsbmo

Reputation: 1755

Remove "no newline at end of file" changes from git stage

I performed a sed search/replace on a large codebase, and for every file that sed passed over, it added a newline at the end if none was there previously. While it is good convention to end the last line with \n, this is a huge diff that is irrelevant to what I was trying to accomplish. The change affected hundreds of files, which I don't want to manually check and git checkout by hand.

Is there any way to selectively add or remove staged changes such that files that have only "no newline at end of file" will be ignored?

Upvotes: 9

Views: 4278

Answers (2)

Joshua Goldberg
Joshua Goldberg

Reputation: 5333

What I did differs a bit from @BenMorris's solution, which finds files that only have the whitespace difference and no other changes. I wanted to discard the EOF newline change even if there are other valid changes in the same file:

To get a list of files with such differences:

git diff | grep '\(+++\|No newline at\)' | grep -B 1 'No newline at'

...or if your grep supports it, this will clean up the output to give just the paths:

git diff | grep '\(+++\|No newline at\)' | grep -B 1 'No newline at' | grep --perl-regexp -o '\+\+\+ b/\K.*'

That's enough to just point you to which files to clean up manually, but to automate the rest of the process....

Download and install splitpatch.rb if you don't have it

Make a complete patch of all staged changes in all files with EOF newline changes. (This includes a couple of grep -v's to avoid small issues with compatibility between git and the output of splitpatch.)

git diff --cached | grep '\(+++\|No newline at\)' | grep -B 1 'No newline at' | grep -Po '\+\+\+ b/\K.*' | xargs git  diff --cached   | grep -v "^diff --git a" | grep -v "^index "   > fullpatch.patch

Break it up into hunks:

/path/to/splitpatch.rb --hunk fullpatch.patch

This creates separate numbered files named Filename.extention.00#.patch, starting from 000. Apply, in reverse, only the last (highest-numbered) patch for each source file (The line below is bash-only, not zsh, due to a difference in globbing):

for f0 in *.000.patch; do (for f_all in ${f0/000/*}; do echo $f_all;  done) | tail -n -1;  done | xargs -n 1 git apply —reverse

It's not impossible there could be a "real" change caught in the same hunk with the EOF newline, so take a look with git diff and use an editor to undo any changes besides the last line.

Clean up:

ls *.patch
rm *.patch

When you add these small reverse changes to the index, you will have discarded the newline changes at EOF.

Upvotes: 0

heyitsbmo
heyitsbmo

Reputation: 1755

From the linked related issue, I found that git diff --ignore-all-space would ignore files with only whitespace changes. git diff --ignore-all-space --name-only doesn't work as I expected it to, but this does:

git diff --ignore-all-space | grep "+++"

This gave me a list of files that have changes other than whitespace, which is small enough that I can just add them all manually to the stage and commit.

Thanks to everyone for the comments, they were very helpful.

Upvotes: 6

Related Questions