Newlines and Version Control

Imagine you have just finished implementing a new feature in a project and before committing the work to the repo, want to see a summary of the changes. Running git diff reveals the expected changes, but you also see the strange message:

No newline at end of file.

OK, so there is no newline at end of file… What is the big deal?

Aside: I would recommend reading The Great Newline Schism by Jeff Atwood to clear up any misunderstanding about carriage return and line feed before continuing…

For the purpose of clarity, we'll be using \n for newlines characters - it is a common abstraction in many programming languages for newlines.

No newline at end of file

The message indicates that you do not have a newline at the end of the file. You may be asking:

So what is the significance?

Let's do a little experimenting on the command line to see how git handles things:

Newline Experiment

Part 1 - Adding content without a newline

  1. We will start by creating a new directory for our experiment.

  2. In this new directory we will create an empty git repository by typing git init.

  3. Now we will create an empty file called foo.txt using touch foo.txt.

  4. Staging the empty file in the git repository will allow us to compare upcoming changes against it. Stage the file using git add foo.txt.

  5. Add "Hello" to the file - printf 'Hello' >> foo.txt. We are explicitly adding the text without a new line at the end. echo adds a newline by default. printf does not add a newline by default.

  6. Now that we have added some content we can see what the diff generates. Running git diff will output the following:

diff --git a/foo.txt b/foo.txt
index e69de29..5ab2f8a 100644
--- a/foo.txt
+++ b/foo.txt
@@ -0,0 +1 @@
+Hello
\ No newline at end of file

Notice the \ No newline at end of file message below the "Hello".

The diff's output is rendered with a newline at the end of the "Hello" to make it easier to consume programmatically.
The No newline at end of file message is appended to indicate that the newline is actually missing from that file.

A newline character is always at the end of the line.

Newlines are a control character and therefore don't have a visible character. Certain editors may display a character to show that there is a newline, others will show the next line number and some will not show anything.

OK, let's get back to this experiment

Part 2 - Adding content after a missing newline

  1. Let's stage the file again so we can see the effect of adding more lines - git add foo.txt

  2. This time we will add "World" on a newline - printf '\nWorld\n' >> foo.txt. Note how we have added \n before and after "World".

  3. What is the diff now? - git diff

diff --git a/foo.txt b/foo.txt
index 5ab2f8a..f9264f7 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1,2 @@
-Hello
\ No newline at end of file
+Hello
+World

The diff reveals the following:

What about adding another line?

OK, lets see what happens when we add another line.

Part 3 - Adding content after a newline

  1. We will stage the current changes so we can get a clean diff - git add foo.txt

  2. Let us add "!" on the third line - echo '!' >> foo.txt. We use echo here to get the benefit of appending a newline character by default.

  3. Now we can have a look at the diff

diff --git a/foo.txt b/foo.txt
index f9264f7..a9a7f7a 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1,2 +1,3 @@
 Hello
 World
+!

The diff above shows that the second line was not modified. We have successfully appended to the file without other modifications. We did not need to append a newline to the second line to create the third line.

That is a much cleaner diff. The intent is clearly conveyed.

OK, there is just one thing missing…

Part 4 - Preparing a patch

  1. Stage the most recent changes again - git add foo.txt

  2. We will now apply a patch using an existing diff. Create a new file called add-beautiful.diff and add following snippet to it.

diff --git a/foo.txt b/foo.txt
index a9a7f7a..9a4c951 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1,3 +1,4 @@
 Hello
+Beautiful
 World
 !

Note: We can generate our own patch by using the output generated by git diff and send the output to a file - git diff >> <filename>.

Part 5 - Apply a patch

  1. Now we can apply the patch which will update our foo.txt. To do this we use git apply add-beautiful.diff

  2. If we now do diff - git diff

diff --git a/foo.txt b/foo.txt
index a9a7f7a..9a4c951 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1,3 +1,4 @@
 Hello
+Beautiful
 World
 !

Notice how the output is the same as was the patch we applied.

This is not a coincidence. Remember that a diff is a format that can easily be consumed programmatically.

My take on it

"No newline at end of file", like other whitespace changes, add unnecessary noise to commits and diffs. It should be avoided and is fairly easy to avoid.

Managing different configurations across different projects can be tedious. The solution that I've found most helpful is adding a .editorconfig file to a project and installing the EditorConfig plugins corresponding to my editors.

Conclusion

Commands that we have covered during this post:

So hopefully you now have a better understanding of why git keeps pointing out the "No newline at end of file".

Now go out and enjoy the beautiful world!