Most patches exchanged in code review, bug reports, and email use the unified diff format. git diff, diff -u, and GitHub's "raw diff" all use it. It looks full of symbols at first, but the rules are simple.
Overall shape
@@ -1,5 +1,6 @@ def greet(name):- print("Hello " + name)+ message = f"Hello, {name}!"+ print(message) def main(): greet("world")
From the top come the git extended headers, the file headers (---, +++), and then hunks (blocks starting with @@). One file can have several hunks, and one patch can contain several files.
File headers: --- and +++
--- a/greet.pyis the file before the change, and+++ b/greet.pyis the file after the change.- git prefixes paths with
a/andb/. That is why you strip the first path component with-p1when applying the patch with thepatchcommand. - A newly created file is shown as
--- /dev/null, and a deleted file as+++ /dev/null. - GNU diff sometimes appends the modification time after the file name.
The hunk header @@ -a,b +c,d @@
@@ -1,5 +1,6 @@ means "this shows 5 lines starting at line 1 of the old file, and 6 lines starting at line 1 of the new file".
- The first number is the starting line number, and the number after the comma is how many lines the hunk covers (including context lines).
- If the count is 1, it is omitted together with the comma.
@@ -7 +7 @@is a one-line hunk at line 7. - A count of 0 means there are no lines on that side.
@@ -3,0 +4,2 @@means two new lines were inserted after line 3 of the original. Comparing against an empty file gives-0,0. - Text such as a function name may follow the second
@@(@@ -40,7 +40,8 @@ def main():). It is reference information telling you which function the hunk is in, and it is ignored when the patch is applied.
If you count them yourself, the example above has 4 context lines (lines starting with a space): def greet(name):, a blank line, def main():, and greet("world"). The old line count is 4 context lines plus 1 - line = 5, and the new line count is 4 context lines plus 2 + lines = 6, so the header is -1,5 +1,6.
The three kinds of lines in the body
| First character | Meaning |
|---|---|
| Space | A context line present on both sides |
- | A line only in the old version (deleted) |
+ | A line only in the new version (added) |
The default context is 3 lines before and after each change (you can change it, e.g. git diff -U5). Context lines are used to find the right place when applying the patch. Even if line numbers are slightly off, patch shifts the position (offset) and applies it as long as the context matches.
An edit to a single line appears as a - line followed by a + line. The unified format has no separate symbol for "modified", so the trick is to read a run of - lines against the run of + lines below it.
\ No newline at end of file
@@ -1,2 +1,2 @@ host=example.com-port=8080\ No newline at end of file+port=8080
This marks that the line right above is the last line of the file and has no newline character at the end. In the example above, the content is the same, and only the new file gained a final newline. For more, see Line endings and invisible differences.
git extended headers
The lines after diff --git a/path b/path are extra information specific to git.
index 3b18e51..a6f4c2d 100644— the abbreviated hashes of the file contents before and after, and the file mode (100644 is a regular file, 100755 an executable)new file mode 100644/deleted file mode 100644— file created or deletedold mode 100644/new mode 100755— only the execute permission changed (may appear without any content hunk)similarity index 90%,rename from old-path,rename to new-path— a rename (the contents are 90% the same)Binary files a/logo.png and b/logo.png differ— for binary files, just this one line instead of the contents
Tips for reviewing
- Look at the difference in line counts in the hunk header first to gauge the size of a change.
-10,7 +10,30means it grew by 23 lines. - To filter out hunks where only whitespace changed, use
git diff -w. This is especially useful for commits that mix indentation cleanup with real changes (Whitespace options). - To see what changed within a line, use
git diff --word-diff, or paste the diff into a tool that supports in-line highlighting, like this site. - Before applying a patch, check that it applies cleanly with
git apply --check file.patch. Outside a git repository, usepatch -p1 --dry-run < file.patch.
Try it in this tool
The unified view of the Text Diff Checker shows a run of - lines followed by a run of + lines, in the same order as this format, and highlights the changed words within lines once more. The patch button in the result bar copies standard unified diff text, and the save button next to it downloads a diff.patch file. A patch created with all ignore options turned off can be applied to the original with patch -p1 or git apply.