Skip to main content

svn: No newline at end of file

· 4 min read
Subversion logo

If you've used Subversion for any significant length of time, then you've probably run into the following message (or one just like it), near the end of a svn diff command.

\ No newline at end of file

Of course, you would have realised that this is because your editor has added a newline character to the end of the file in question, where the original had no such newline character.

For me, this happens most often when patching source code that I've imported from elsewhere, or when editing files under Linux that were originally created under Windows (most of my project contain both Linux and Windows components).

I experience this problem (a slight overstatement perhaps) when editing with either vi or nano (haven't bothered testing any other editors). And while I could probably tell those editors to not add the final newline character somehow, the default behavior is actually considered to be a good thing (indeed various C compilers will actually complain if the final newline character is not present).

And of course, this "no newline at end of file" message is not normally a problem - it just adds noise to svn diff outputs - both now, and in the future. And that's something I dislike quite strongly.

So, how to fix it? Well, the easiest way I've come up with, is a very simple head command to strip the very last byte of the file. It looks like this:

head -c-1 file.ext > file.tmp && mv file.tmp file.ext

As always, you should use commands like this (ie ones that overwrite existing files) with extreme caution. But this one should be fairly safe - as long as you don't run out of disk space ;)

The first part of the command simply strips off the last character from the file.ext file, and writes the output to file.tmp. The the second half of the command moves the file.tmp file back to file.ext, overwriting the original in the process. Because the two commands are separated by the && operator, the second command (mv) will not be executed unless the first command (head) completes successfully. But if you want to be extra cautious, then you can run the two commands separately, like this:

head -c-1 file.ext > file.tmp
# (you can manually check the file.tmp output file here)
mv file.tmp file.ext

I hope you find this small tip useful, I know I do :)

Update

Just a couple of more thoughts... first off, if you're dealing with files that have DOS-style line encodings (ie lines end in \r\n instead of simply \n), then you simply need to run the command twice to cut off both characters, or change the -c-1 to -c-2 instead. For example:

head -c-2 file.ext > file.tmp && mv file.tmp file.ext

Also, if you want to process a group of files, then the obvious thing to do is use xargs (such a great utility!). But, of course, xargs commands can get a bit tricky when you want to combine commands (in this case using the && operator. The usual way to handle this (or at least the way I like to) is to parse the grouped commands to a single bash -c command like this:

ls -1 | xargs -I{} bash -c 'head -c-1 "{}" > "{}.tmp" && mv "{}.tmp" "{}"'

Note, this command will process all files in the current directory (so be very careful), but obviously you can modify the beginning ls command to limit the files that will be processed. For example, to just process all C# source files, you could run this:

ls -1 *.cs | xargs -I{} bash -c 'head -c-1 "{}" > "{}.tmp" && mv "{}.tmp" "{}"'

Well, that's it for now ;)