opscape

Text processing

patch

Apply a diff to files, turning the original versions into the changed ones.

patch [OPTION...] [ORIGFILE [PATCHFILE]]

10 of 10 examples run in Debian 13.7 on 2026-10-31 · manual: patch(1)

patch is the other half of diff: it reads a diff (usually unified, from diff -u or git diff) and makes those edits to your files. It reads the patch from standard input unless you give -i FILE, and it finds the files to change from the ---/+++ header lines.

It does not need the file to be exactly as it was. Each hunk carries a few lines of context, and patch looks for them near the stated line number: a hunk that moved is applied with an offset, one whose context changed slightly with fuzz, and one that cannot be placed is rejected into a .rej file while the rest still applies.

The option you will need most is -pN, which strips N leading directories from the paths in the headers. Patches made by git name files a/src/main.c and b/src/main.c, so you apply them from the top of the tree with -p1. Always try --dry-run first on anything that matters. The exit status is 0 when everything applied, 1 when some hunks failed, 2 on serious trouble.

Examples

Apply a patch to one file

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
patch < todo.patch
cat todo.txt
output
patching file todo.txt
buy milk
fix login bug
write integration tests
fix logout bug
deploy v2.4

patch reads the unified diff from stdin, takes the file name from the ---/+++ header, and edits todo.txt in place: the - line is removed and the + line added. It prints the name of each file it patches.

Check a patch before applying it

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
patch --dry-run < todo.patch
cat todo.txt
output
checking file todo.txt
buy milk
fix login bug
write tests
fix logout bug
deploy v2.4

--dry-run reports what would happen and changes nothing, as cat confirms. Use it to catch failed hunks before half a patch lands.

Strip path prefixes with -p1

fix.patch
--- a/src/main.c
+++ b/src/main.c
@@ -3,4 +3,4 @@
 int main(void) {
-    printf("hello, world\n");
+    printf("hello, demo\n");
     return 0;
 }
patch -p1 < fix.patch
grep printf src/main.c
output
patching file src/main.c
    printf("hello, demo\n");

The headers name a/src/main.c and b/src/main.c, the convention git uses. -p1 strips the first path component, so patch edits src/main.c relative to the current directory. Without it, patch would look for a directory called a.

Name the patch file and the target explicitly

greeting.patch
--- greet.sh
+++ greet.sh
@@ -1,2 +1,2 @@
 #!/bin/sh
-echo "Hello, ${1:-world}!"
+echo "Hi there, ${1:-world}."
patch scripts/greet.sh -i greeting.patch
scripts/greet.sh
output
patching file scripts/greet.sh
Hi there, world.

Naming the file to patch overrides the names in the header, and -i reads the patch from a file instead of stdin. Here the header says greet.sh but the file lives in scripts/. Naming the target also avoids surprises when the --- and +++ names differ and both files exist: patch then picks by its own rules (fewest path components first), which may not be the file you meant.

Keep a backup of the original

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
patch -b < todo.patch
diff todo.txt.orig todo.txt
outputexit 1
patching file todo.txt
3c3
< write tests
---
> write integration tests

-b saves each original file with a .orig suffix before changing it. The final diff shows the change and exits with 1 because the files differ.

Undo a patch with -R

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
patch < todo.patch
patch -R < todo.patch
sed -n 3p todo.txt
output
patching file todo.txt
patching file todo.txt
write tests

-R applies the patch backwards, turning + lines into removals and - lines into additions. Line 3 is back to its original text.

Skip a patch that is already applied with -N

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
patch < todo.patch
patch -N < todo.patch
outputexit 1
patching file todo.txt
patching file todo.txt
Reversed (or previously applied) patch detected!  Skipping patch.
1 out of 1 hunk ignored -- saving rejects to file todo.txt.rej

Applied a second time, the hunk looks reversed. Without -N, patch stops to ask whether to undo it instead. -N answers no without asking: the hunk is skipped, saved to todo.txt.rej, and the exit status is 1, so a script can tell it did nothing.

A hunk that moved still applies

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
sed -i '1i book flights\nrenew passport' todo.txt
patch < todo.patch
output
patching file todo.txt
Hunk #1 succeeded at 3 (offset 2 lines).

Two lines were inserted at the top after the patch was made, so the hunk's context is now two lines further down. patch finds it anyway and reports the offset.

A hunk that does not fit is rejected

todo.patch
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4
sed -i 's/write tests/write unit tests/' todo.txt
patch < todo.patch
echo "exit status: $?"
cat todo.txt.rej
output
patching file todo.txt
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file todo.txt.rej
exit status: 1
--- todo.txt
+++ todo.txt
@@ -1,5 +1,5 @@
 buy milk
 fix login bug
-write tests
+write integration tests
 fix logout bug
 deploy v2.4

The line the patch wants to remove no longer exists, so the hunk fails. patch writes it to todo.txt.rej, leaves the file unchanged and exits with 1. Resolve rejects by hand, then delete the .rej file.

Apply a patch to a whole tree

Setup run before this example
cp -r src /tmp/src.orig
sed -i 's/hello, world/hello, demo/' src/main.c
printf 'print("new")\n' > src/new.py
cd /home/dev/demo && diff -ruN /tmp/src.orig src | sed 's|/tmp/src.orig|a/src|; s|^+++ src|+++ b/src|' > /tmp/tree.patch
rm -r src && cp -r /tmp/src.orig src
patch -p1 < /tmp/tree.patch
cat src/new.py
output
patching file src/main.c
patching file src/new.py
print("new")

A recursive diff -ruN produces one patch for many files, including new ones. patch applies each section in turn and creates src/new.py from nothing.

Common pitfalls

  • Wrong -p level is the classic failure: patch asks "File to patch:" or reports the file missing. Look at the +++ header and count how many leading directories to strip from where you stand.

  • Patches with CRLF line endings, or files with them, make every hunk fail. Use --binary on both sides consistently, or convert line endings first.

  • A partly applied patch leaves .rej and possibly .orig files lying around. Run with --dry-run first, and clean up with find . -name '*.rej' -o -name '*.orig' after.

  • Patches copied from a web page or email often lose trailing whitespace on context lines and fail to apply. Use the raw file, or -l to ignore whitespace differences.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • macOS and the BSDs ship a BSD patch; -p, -R, -N, -i, -b and --dry-run (BSD also spells it -C/--check) behave the same for everyday use.

Options worth knowing

A selection, described in our own words. For the full list, see patch(1) or patch --help.

OptionWhat it does
-p, --strip NUMStrip NUM leading path components from file names in the patch.
-i, --input PATCHFILERead the patch from PATCHFILE instead of stdin.
-R, --reverseApply the patch in reverse, undoing it.
-N, --forwardIgnore patches that seem reversed or already applied.
--dry-runPrint what would happen without changing any files.
-b, --backupSave each original file with a .orig suffix.
-d, --directory DIRChange to DIR before doing anything.
-o, --output FILEWrite the patched result to FILE instead of in place.
-r, --reject-file FILEWrite rejected hunks to FILE.
-F, --fuzz NUMAllow up to NUM lines of mismatched context.
-l, --ignore-whitespaceIgnore whitespace differences when matching context.
-s, --silentWork silently unless an error occurs.
-t, --batchNever ask questions; skip patches whose files are missing.
-f, --forceNever ask questions and assume the patch is not reversed.
-E, --remove-empty-filesRemove files that are empty after patching.

Related