patch Command in Linux: Complete Guide to Applying Patch Files, Diffs, and Parameters

patch command in Linux and it perimeters

Before I really understood patch, code review felt clumsier than it needed to be — I’d get a .diff file from a teammate and manually re-type the changes into my own copy of the file, which is both slow and a great way to introduce typos into someone else’s fix. Once I learned that diff and patch are meant to work together as a pair — one generates the change description, the other applies it — reviewing and applying changes became a completely different, much faster experience.

patch takes a “diff” (also called a patch file) describing the differences between two versions of a file, and applies those differences to a target file, transforming it from the old version into the new one. It’s the low-level mechanism underneath a huge amount of open-source collaboration, Linux kernel development, and package management.

What patch Does

patch [OPTIONS] [ORIGINALFILE [PATCHFILE]]

The typical usage pattern is:

patch target_file < patch_file

patch reads the patch file, figures out what changed, and applies it to target_file.

Where Patch Files Come From

Patch files are almost always generated by diff, most commonly with the -u (unified diff) flag, since unified diff format is what patch (and most tooling, including Git) expects by default:

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

I tested this directly:

$ printf "line1\nline2\nline3\n" > orig.txt
$ printf "line1\nline2modified\nline3\n" > new.txt
$ diff -u orig.txt new.txt > changes.patch
$ cat changes.patch
--- orig.txt	2026-07-31 01:37:09.315425609 +0000
+++ new.txt	2026-07-31 01:37:09.315425609 +0000
@@ -1,3 +1,3 @@
 line1
-line2
+line2modified
 line3

That’s a unified diff: the ---/+++ lines identify the original and new files, @@ -1,3 +1,3 @@ is a “hunk header” describing which line ranges are affected, and each content line is prefixed with a space (unchanged), - (removed), or + (added).

Applying a Patch

$ patch orig.txt < changes.patch
patching file orig.txt
$ cat orig.txt
line1
line2modified
line3

I confirmed this transforms orig.txt into exactly what new.txt contained — patch applied the hunk correctly, replacing line2 with line2modified while leaving line1 and line3 untouched.

Core Options and Parameters

--dry-run — Preview Without Modifying Anything

Before applying a patch to anything I care about, I always run this first:

$ patch --dry-run orig2.txt < changes2.patch
checking file orig2.txt

This tells you whether the patch would apply cleanly, without touching the actual file. If it reports “checking file” with no errors, you’re safe to apply for real.

-R — Reverse a Patch

If a patch has already been applied and you want to undo it, use -R:

$ patch orig2.txt < changes2.patch
patching file orig2.txt
$ cat orig2.txt
line1
line2modified
line3
$ patch -R orig2.txt < changes2.patch
patching file orig2.txt
$ cat orig2.txt
line1
line2
line3

I tested this end to end — applying the patch, confirming the change, then reversing it and confirming the file returned to its original state. This is genuinely useful for rolling back a bad hotfix without needing a separate backup copy, as long as you kept the original patch file.

-p NUMBER — Strip Leading Path Components

This is the option that confuses almost everyone the first time they use patch, and it’s worth understanding properly. Patches generated from a project’s root directory (very common with Git and multi-file diffs) include path prefixes like a/sub/file.txt and b/sub/file.txt:

$ diff -u projdir/sub/file.txt projdir_new/sub/file.txt
--- projdir/sub/file.txt
+++ projdir_new/sub/file.txt
@@ -1,3 +1,3 @@
 a
-b
+B
 c

If you’re standing inside projdir/ and want to apply this patch, the paths in the diff (projdir/sub/file.txt) don’t match your current location relative to the file (sub/file.txt). -p1 strips the first path component:

cd projdir
patch -p1 < ../changes.patch

-p0 (the default) expects the path exactly as written in the patch. -p1 strips one leading directory component (turning projdir/sub/file.txt into sub/file.txt), -p2 strips two, and so on. Git-generated patches (git diff) conventionally use a/ and b/ prefixes specifically so that -p1 reliably works when applying them from a repository root.

-i FILE — Explicitly Specify the Patch File

Equivalent to redirecting with <, but sometimes clearer in scripts:

patch -p1 -i changes.patch

-b — Keep a Backup of the Original File

patch -b target_file < changes.patch

This creates target_file.orig before applying changes, giving you a safety net beyond what -R already provides (particularly useful if you might lose track of the original patch file).

-o FILE — Write Output to a Different File

Rather than modifying the target in place, write the patched result elsewhere, leaving the original untouched:

patch -o patched_output.txt orig.txt < changes.patch

-N — Ignore Patches That Appear Already Applied

Useful in idempotent automation scripts where a patch might get applied more than once accidentally — -N causes patch to skip hunks that already match the target state instead of erroring out.

-f — Force Mode (Assume Answers, Don’t Prompt)

By default, if patch encounters ambiguity (like a patch that doesn’t apply cleanly), it may interactively prompt you. -f suppresses those prompts, which is necessary for non-interactive/automated use, but should be paired with checking the exit code and .rej files carefully since it will proceed even when uncertain.

Understanding Patch Failures and .rej Files

When a patch doesn’t apply cleanly — because the target file has diverged from what the patch expects — patch will report failed hunks and, by default, write the unappliable hunks to a .rej (reject) file:

patching file config.conf
Hunk #2 FAILED at 45.
1 out of 2 hunks FAILED -- saving rejects to file config.conf.rej

When this happens, I open the .rej file, look at exactly which hunk failed, and manually reconcile the intended change with the current state of the file — this is a normal part of the workflow when patches are generated against an older version of a file than the one you’re actually patching.

How patch Works Internally

patch parses the patch file to identify one or more “hunks” — contiguous blocks of context lines plus additions/removals, each anchored to an approximate line number in the original file. Rather than blindly trusting the line numbers, patch uses the surrounding context lines (the unchanged lines included in the diff around each change) to locate exactly where in the target file the hunk should be applied, even if the file has shifted slightly (e.g., lines were added elsewhere earlier in the file). This context-based fuzzy matching is what makes patches resilient to minor drift between the file the patch was generated against and the file it’s being applied to — within limits, controlled by the --fuzz option.

Practical, Real-World Examples

1. Applying a Bug Fix Sent by a Colleague

patch -p1 --dry-run < bugfix.patch
patch -p1 < bugfix.patch

2. Applying a Kernel or Software Source Patch

Classic Linux kernel workflow:

cd linux-source-tree/
patch -p1 < some-feature.patch

3. Reviewing a Diff Before Committing to Applying It

less changes.patch          # read through the hunks first
patch --dry-run -p1 < changes.patch
patch -p1 < changes.patch

4. Rolling Back a Bad Patch in Production

patch -R -p1 < last_deploy.patch

5. Generating and Applying a Patch for a Whole Directory Tree

diff -ruN old_version/ new_version/ > full_update.patch
patch -p1 -d target_directory < full_update.patch

-d tells patch to cd into the given directory before applying, which is convenient when scripting patch application against a directory that isn’t your current working directory.

patch in Shell Scripting and Automation

A pattern I’ve used in configuration management for legacy systems that predate proper templating tools:

#!/bin/bash
CONFIG="/etc/legacyapp/app.conf"
PATCH="/opt/deploy/patches/app.conf.patch"

if patch --dry-run -p0 "$CONFIG" < "$PATCH" > /dev/null 2>&1; then
  patch -p0 -b "$CONFIG" < "$PATCH"
  echo "Patch applied successfully"
else
  echo "Patch does not apply cleanly — manual intervention required" >&2
  exit 1
fi

This checks with --dry-run first, only applies if it would succeed cleanly, and keeps a backup (-b) in case a rollback is needed later.

Comparing patch to Related Tools

TaskBest Tool
Generating a diff between two filesdiff
Applying a diff to a filepatch
Version-controlled diff/patch workflowgit diff / git apply
Binary file differencescmp, bsdiff
Merging with conflict resolutiongit merge, diff3

In modern Git-based workflows, git apply has largely replaced raw patch for applying diffs within a repository, since it understands Git-specific metadata (file modes, renames) better. That said, patch remains essential for non-Git contexts — applying vendor-supplied patches, kernel source patches, or any diff generated outside version control.

Troubleshooting Common patch Issues

“can’t find file to patch” — almost always a -p level mismatch; try adjusting -p0, -p1, -p2 until the paths resolve correctly relative to your current directory.

Hunks failing even though the change looks simple — the target file has diverged more than the patch’s context lines can tolerate. Check --fuzz=N to allow more mismatched context lines, though this increases the risk of applying to the wrong location — always verify manually afterward.

Patch applies “successfully” but the change is wrong — verify you’re not accidentally applying a patch in the wrong direction; check whether -R should have been used, or whether the patch file itself was generated in reverse (diff -u new.txt old.txt instead of diff -u old.txt new.txt).

Line ending mismatches (CRLF vs LF) causing every hunk to fail — normalize line endings on both the patch and target file before applying, e.g., with dos2unix or sed -i 's/\r$//'.

Performance Optimization

patch is lightweight and rarely a performance bottleneck even on large source trees, since it processes each file independently and only reads the specific files referenced in the patch. For applying patches across many files in a large tree, the overhead is dominated by file I/O and process startup, not by patch‘s internal diff-matching logic.

Security Implications

Never apply a patch from an untrusted source without reviewing it first — a malicious patch can silently introduce backdoors, alter build scripts, or overwrite files well outside the apparent scope of the “fix” it claims to provide, especially when combined with directory traversal in file paths. Always read the patch content (or diff it against what you expect) before running patch -p1 < untrusted.patch, and never run patch as root against files or paths you haven’t fully reviewed.

Compatibility Across Distributions

GNU patch (tested here at version 2.7.6) ships by default on virtually every major Linux distribution — Ubuntu, Debian, Fedora, RHEL/CentOS, Arch, openSUSE — since it’s a foundational tool for source-based package building. BSD and macOS also ship a patch utility, though historically an older BSD-derived version with slightly different option support; GNU-specific flags like --dry-run are supported in modern versions but it’s worth checking patch --version if scripts need to run across both environments.

Unified Diff Format in More Depth

It’s worth understanding the unified diff format itself a bit more thoroughly, since being able to read a patch file directly — without applying it first — is a genuinely useful skill for reviewing changes before you trust them. Beyond the ---/+++ file headers and the @@ -a,b +c,d @@ hunk header already covered, the numbers in that hunk header carry specific meaning: -a,b means the hunk starts at line a in the original file and spans b lines from that file; +c,d means the corresponding section starts at line c in the new file and spans d lines. When a hunk is purely an insertion with no corresponding original lines, b will be 0; when it’s a pure deletion with nothing added, d will be 0.

Multiple hunks can appear in a single patch for one file, each with its own @@ ... @@ header, and a single patch file can contain hunks for many different files in sequence, each introduced by its own ---/+++ pair. This is exactly what a git diff spanning multiple modified files produces, and it’s why patch -p1 applied to a multi-file Git diff correctly walks through and patches every affected file in one invocation, rather than needing to be run once per file.

Context Lines and the “Fuzz” Concept

The unchanged lines surrounding each change (the lines with a leading space rather than - or +) are called context lines, and they exist for a very deliberate reason beyond just human readability: they let patch verify it’s applying the change to the correct location, even if line numbers have shifted slightly since the patch was generated. By default, GNU patch requires context lines to match exactly at the expected location, but if they don’t match at the exact line number, it will search nearby lines for a matching context before giving up — this search tolerance is controlled by the --fuzz=N option, where N specifies how many lines of context mismatch to tolerate before considering a hunk a failure.

A higher fuzz value makes patch more forgiving of drift between the file the patch was generated against and the file it’s being applied to, but it also increases the risk of the patch landing in a superficially similar but actually wrong location in the file — for anything beyond casual, low-stakes patching, keeping the default fuzz tolerance and manually reviewing any resulting .rej files is the safer approach rather than cranking up --fuzz to force an otherwise-failing patch to apply.

Generating Better Patches for Easier Application

If you’re the one generating patches (rather than just applying ones you’ve received), a few diff habits make the resulting patch more robust and easier for others to apply correctly:

diff -u -r --exclude='.git' old_project/ new_project/ > project_changes.patch

Using -r for recursive directory comparison, -u for the unified format patch expects, and --exclude to skip version-control metadata directories keeps the resulting patch focused only on meaningful content changes. When working within a Git repository specifically, git diff (or git format-patch for a series of commits formatted as individually applicable patches) is generally preferable to raw diff, since it produces patches with the a//b/ path convention that pairs naturally with -p1, along with additional metadata that tools like git apply can use for more precise application than plain patch alone provides.

Summary

patch and diff form a foundational pair in the Linux toolchain: diff describes a change, patch applies it. Understanding -p levels, using --dry-run before committing to a real change, and knowing how to read and reverse-apply (-R) a patch covers the vast majority of situations you’ll encounter, whether you’re reviewing a colleague’s fix, applying a vendor patch, or rolling back a bad change in production.

References

  • GNU patch Manual: https://www.gnu.org/software/patch/manual/patch.html
  • GNU diffutils Manual: https://www.gnu.org/software/diffutils/manual/diffutils.html
  • man patch / man diff (local manual pages)
Total
0
Shares

Leave a Reply

Previous Post
paste command in Linux and it perimeters

paste Command in Linux: Complete Guide to Merging File Lines and Parameters

Next Post
sed command in Linux and it perimeters

sed Command in Linux: Complete Guide to Stream Editing, Text Manipulation, and Parameters

Related Posts