What does @@ -1,7 +1,8 @@ mean? Reading a unified diff

How to read @@ in diff -u and git diff: start lines, line counts, empty ranges, the default three lines of context, and the function context Git adds, based on POSIX and official Git docs.

DevelopmentGit

Open a git diff in a terminal or a code review page and you’ll see lines like @@ -1,7 +1,8 @@ above the changes. Once you can read that line, you know where in the file a change happened and how many lines it added or removed. The format is called unified diff, and POSIX defines it as the output of diff -u.

Two header lines, then hunks

diff -u output starts with a two-line header: --- followed by the original file’s name and time, then +++ followed by the new file’s. Below that come hunks. Each hunk is one area where the two files differ.

Inside a hunk, the first character of each line tells you what it is:

  • a space: a line present in both files, or context
  • -: a line removed from the original file
  • +: a line added to the new file

Reading the @@ line

Take an eight-line file holding the letters a to h, one per line. Replace line 4, d, with two lines, D and d2, and compare the files with diff -u. This is the output from the diff that ships with macOS, which is based on FreeBSD. The two header lines containing file names and times have been left out.

@@ -1,7 +1,8 @@
 a
 b
 c
-d
+D
+d2
 e
 f
 g

-1,7 means “seven lines of the original file, starting at line 1.” +1,8 means “eight lines of the new file, starting at line 1.” The number before the comma is the starting line; the one after it is the line count.

You can check the counts yourself. For the original file, count the lines that start with a space or -. For the new file, count the lines that start with a space or +. Here, six context lines plus one removed line make 7. Six context lines plus two added lines make 8. The difference, 1, is how many lines this hunk adds.

The three lines of context before and after the change are the default. POSIX defines -u as giving three lines of unified context and -U n as giving n lines. Git also defaults to 3. You can change it with -U<n> or the diff.context setting.

When there’s only one number, or a 0

Under POSIX, a range of exactly one line may be written as just its starting line: 2 instead of 2,1. If a range is empty, its starting number is the line just before the range, or 0 if the empty range starts the file.

With no context at all (-U0), the rule is easy to see. Here is b deleted from the three lines a, b, c:

@@ -2 +1,0 @@
-b

In the original file, one line was removed at line 2, hence -2. The new file has no matching lines, so the count is 0, and the start is 1, the line just before where b used to be. Insert b between a and c instead and you get @@ -1,0 +2 @@. A ,0 means that file has no lines in this hunk.

What Git adds

git diff output differs slightly from traditional diff. According to Git’s documentation, it begins with a diff --git a/file b/file header, which may be followed by extended header lines such as index, new file mode, or rename from.

Text can also follow the second @@. Git’s documentation calls a line of the form @@ -k,l +n,m @@ TEXT a hunk header. By default, TEXT is a line beginning with a letter, an underscore, or a dollar sign. Setting something like *.py diff=python in .gitattributes switches to one of Git’s built-in language patterns.

Treat that text as a hint about location. Below is the output from Git 2.55.0 after changing return 0 to return None inside a function called average. The diff --git, ---, and +++ headers have been left out.

@@ -8,5 +8,5 @@ def total(items):
 def average(items):
     n = len(items)
     if n == 0:
-        return 0
+        return None
     return total(items) / n

The change is inside average, but def total(items): follows the @@. In this run, Git picked a line from before the hunk, not def average on line 8 where the hunk starts. For the exact location, rely on the numbers between the @@ markers.

If you’re comparing drafts and care more about which sentence changed than about line numbers, Motitory’s text diff checker may be easier to read. It doesn’t output the @@ format; it highlights the changed lines and characters instead.

Back to the blog