What a patch file actually is
A patch file is a text file containing a unified diff — the same format you see in git diffoutput — stored as a file. That's it. The patch command reads this file and applies the changes described in it to the target files. The format is both human-readable and machine-applicable.
The unified diff format was developed in the early 1980s as part of GNU diffutils. It predates Git by over two decades. When Linus Torvalds created Git in 2005, he designed the commit format to be patch-compatible — commits can be exported as patch files and applied elsewhere.
# A patch file looks like this:
From abc1234def5678 Mon Sep 17 00:00:00 2001
From: Alice Developer <alice@example.com>
Date: Fri, 28 May 2026 14:30:00 +0000
Subject: [PATCH] Fix null pointer in user validation
---
src/validation.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/validation.ts b/src/validation.ts
index 3a1b2c4..7d8e9f0 100644
--- a/src/validation.ts
+++ b/src/validation.ts
@@ -15,7 +15,10 @@ export function validateUser(user: User) {
- if (user.name.length > 0) {
+ if (user.name && user.name.length > 0) {
return true;
}
+ return false;
}Notice that the patch file includes metadata: author, date, subject line. This makes patch files self-describing — they carry the context of the change, not just the change itself.
Creating patches with git format-patch
git format-patch exports commits as patch files, one file per commit. This is different from git diff because format-patch includes commit metadata (author, date, message) and produces files ready for transmission and application elsewhere.
# Export the last commit as a patch file: git format-patch -1 HEAD # Export the last 3 commits: git format-patch -3 HEAD # Export all commits not in main: git format-patch main # Export commits in a range: git format-patch abc1234..def5678 # Output to a specific directory: git format-patch -3 HEAD -o /tmp/patches/ # Create a single patch file for a range (not one per commit): git format-patch main --stdout > feature.patch
The output files are named with a sequence number prefix: 0001-fix-null-pointer.patch, 0002-add-tests.patch, etc. This makes it easy to apply them in order.
Applying patches with git am and git apply
Git provides two commands for applying patches, with different semantics:
# git apply: applies the patch as working directory changes # (does NOT create a commit, just modifies files) git apply feature.patch git apply /tmp/patches/*.patch # apply multiple # git am: applies and COMMITS each patch with original metadata # (preserves author, date, commit message) git am feature.patch git am /tmp/patches/*.patch # If a patch doesn't apply cleanly: git am --3way feature.patch # use 3-way merge for conflicts # After resolving a conflict: git am --continue # Or abort the entire am session: git am --abort
The key distinction: git apply is for applying arbitrary diffs to your working tree; git am (apply mailbox) is for applying patches that came from format-patch with full commit metadata intact. Use git am when you want to preserve authorship for open-source contributions.
The patch command: applying diffs without Git
The traditional Unix patch command predates Git and works directly on files without a Git repository. It reads unified diff output and applies it:
# Apply a diff file to the current directory: patch -p1 < feature.patch # The -p1 flag strips the leading "a/" and "b/" path prefixes # that git diff adds. -p0 would keep them; -p2 strips two components. # Test if a patch will apply without actually applying it: patch --dry-run -p1 < feature.patch # Apply in reverse (undo a patch): patch -R -p1 < feature.patch # Generate a patch with diff: diff -u original.py modified.py > changes.patch
The patchcommand is invaluable for applying changes to source distributions, customizing open-source packages you're vendoring, or sharing changes with someone who doesn't use Git.
Real use cases: when patches shine
Sharing changes without a shared remote. Need to share a fix with a contractor who doesn't have access to your private repo? git format-patch the relevant commits, email the .patch files, they apply with git am. No credentials, no repo access needed.
Backporting fixes across major versions. You have a security fix on main that also needs to apply to v2.x and v1.x branches. Create a patch from the fix commit, apply it to each branch with git am --3way. Cleaner than cherry-picking if the branches have significant divergence.
Open source contribution via email. The Linux kernel development process still uses email-based patches as the primary contribution mechanism. Patches are sent to mailing lists, reviewed in email threads, and applied by maintainers with git am. Understanding this workflow is essential for contributing to the kernel or other projects that use it (like mutt, various embedded Linux projects).
Cherry-picking across repositories. You have two related repos and a bug fix in one that applies to the other. Export as a patch, apply in the target repo. The commit history and authorship travel with it.
Patches as a deeper mental model of version control
Understanding patch files gives you a clearer mental model of what a Git commit actually is. A commit is not a snapshot with a diff — it's a snapshot. But the patch format reveals the "what changed" view that is often more useful for understanding history.
When you understand that a commit can be serialized to a text file and applied elsewhere, you start to see version control as more flexible. Branches are not rigid structures — they're sequences of changes that can be serialized, transmitted, and applied anywhere. The patch format makes this concrete.
Most developers who have been writing code for years have never deliberately created or applied a patch file. That's a blind spot. Spend 20 minutes with git format-patch and git am on a test repo. The insight about how Git actually works at the transport layer is worth the time.
Try it yourself
Diff Checker — compare two texts online →