diff and patch Basics - Getting and Applying Diffs

diff and patch Basics - Getting and Applying Diffs

What You Will Learn

  • How to compare two files with diff and read the output
  • How to interpret unified diff format (diff -u)
  • How to apply a patch file with patch
  • How to revert a patch with patch -R

Quick Summary

  • View diff: diff -u old_file new_file
  • Save patch: diff -u old_file new_file > changes.patch
  • Apply patch: patch -p0 < changes.patch
  • Revert patch: patch -R -p0 < changes.patch

Prerequisites

  • OS: Ubuntu or any Linux distribution
  • diff and patch are installed by default on most Linux systems

What Is diff?

diff compares two files line by line and outputs what changed between them. It is used daily to review changes in source code, compare configuration files against backups, and generate patch files for distribution.

How to Use diff

Basic Comparison

$ diff old.txt new.txt

If there are no differences, nothing is printed. With differences, the default "normal" format output looks like this:

2c2
< old content
---
> new content

The normal format is hard to read and not suitable for patch. Use the unified format instead.

Unified Format (-u)

The -u option produces unified format output — the standard format for patch files.

$ diff -u old.txt new.txt

Example output:

--- old.txt	2026-06-01 10:00:00.000000000 +0000
+++ new.txt	2026-06-01 10:05:00.000000000 +0000
@@ -1,5 +1,5 @@
 line1 (unchanged)
-old line2
+new line2
 line3 (unchanged)

How to read it:

Symbol Meaning
--- Original (before) file
+++ Modified (after) file
@@ Hunk header with line numbers
- Removed line
+ Added line
(space) Context line (unchanged)

The hunk header @@ -1,5 +1,5 @@ means: starting at line 1, showing 5 lines from the old file / 5 lines from the new file. Three context lines before and after each change are included by default.

Recursive Directory Comparison (-r)

Use -r with -u to compare entire directory trees:

$ diff -ur dir_old/ dir_new/

Each changed file appears as a separate unified diff block in the output.

Useful diff Options

Option Description
-u Unified format (required for patch)
-r Recursive (compare directories)
-i Ignore case differences
-w Ignore all whitespace
-b Ignore changes in whitespace amount
--color Colorize output (GNU diff)

Saving the Diff to a Patch File

Redirect the diff output to a file:

# Single file
$ diff -u original.txt modified.txt > changes.patch

# Entire directory
$ diff -ur dir_old/ dir_new/ > project.patch

This file is what you pass to patch when applying changes.

What Is patch?

patch takes a diff file produced by diff and applies those changes to a target file or directory. It is how bug fixes and security patches are distributed in open-source software: the maintainer runs diff, ships the .patch file, and users run patch to apply it.

How to Use patch

Understanding the -p Strip Level

The -p option controls how many leading path components to strip from filenames inside the patch header.

--- a/src/config.txt    ← with -p1, interpreted as "src/config.txt"
+++ b/src/config.txt
Option Behavior
-p0 Use the full path as-is
-p1 Strip one leading component (removes a/ or b/)

Git-generated patches always include a/ and b/ prefixes, so -p1 is the most common choice for Git patches. For patches created with plain diff -u (no prefix), use -p0.

Dry Run First

Always preview before applying:

$ patch --dry-run -p0 < changes.patch

--dry-run checks whether the patch applies cleanly without modifying any files. If it reports no errors, proceed with the real apply.

Applying a Patch

For patches created with diff -u (plain, no a//b/ prefix):

$ patch -p0 < changes.patch

For Git-generated patches or patches with a//b/ prefixes:

$ patch -p1 < project.patch

If patch reports "Hunk FAILED", the patch does not match the current state of the target file — the file may have already been patched, or was modified independently. Use --dry-run to diagnose before attempting.

Rejecting Hunks

When a hunk fails, patch writes the rejected portions to a .rej file (e.g., config.txt.rej). Inspect it to apply the changes manually.

Reverting a Patch

The -R flag reverses a patch — applying it undoes the original change:

$ patch -R -p0 < changes.patch

# Git-format patch
$ patch -R -p1 < project.patch

Reverting requires that the context lines in the patch still match the target file. If the file has been changed further since the patch was applied, reverting may fail or produce incorrect results.

Practical Workflows

Sharing a Config File Change

# 1. Save the original
$ cp nginx.conf nginx.conf.orig

# 2. Edit the file
$ vim nginx.conf

# 3. Generate the patch
$ diff -u nginx.conf.orig nginx.conf > nginx-fix.patch

# 4. Recipient applies the patch
$ patch --dry-run -p0 < nginx-fix.patch
$ patch -p0 < nginx-fix.patch

Applying an Open-Source Patch

# Run from the project root directory
$ patch --dry-run -p1 < bugfix.patch
$ patch -p1 < bugfix.patch

# Undo if something goes wrong
$ patch -R -p1 < bugfix.patch

Generating a Git-Compatible Patch

When working in a Git repository, git diff produces output that patch -p1 can consume directly:

$ git diff > my-changes.patch
$ patch -p1 < my-changes.patch

For committed changes, git format-patch is more appropriate as it preserves commit metadata.

Summary

Command Purpose
diff -u old new Show unified diff
diff -ur dir1/ dir2/ Compare directories recursively
diff -u old new > fix.patch Generate a patch file
patch --dry-run -p0 < fix.patch Preview patch application
patch -p0 < fix.patch Apply patch (plain format)
patch -p1 < fix.patch Apply patch (Git format)
patch -R -p0 < fix.patch Revert a patch

Copy-Paste Templates

# Generate patch
diff -u original.txt modified.txt > changes.patch

# Preview
patch --dry-run -p0 < changes.patch

# Apply
patch -p0 < changes.patch

# Revert
patch -R -p0 < changes.patch

Next Reading