GitCreating & Applying Patches

Git Patches

A Git patch is a plain-text file that describes one or more sets of changes. It combines a unified diff (the actual code changes) with metadata — commit message, author, date — formatted so it can be emailed, reviewed on paper, or applied to a repository that has no network connection to the original. Patches are the oldest collaboration primitive in distributed version control and remain a first-class workflow in projects like the Linux kernel.

Two patch formats in Git

Format

Command

Includes commit metadata?

Apply with

Unified diff

git diff > changes.patch

No — just the raw diff

git apply

Mailbox (mbox)

git format-patch

Yes — author, date, subject, message

git am

Creating a patch with git format-patch

git format-patch generates one .patch file per commit. Each file is formatted as an email-ready mbox message containing the full commit metadata plus the diff.

Bash
# Generate a patch for the most recent commit
git format-patch -1 HEAD

# Generate a patch for a specific commit
git format-patch -1 a1b2c3d

# Generate patches for all commits on current branch not in main
git format-patch main

# Generate patches for a range
git format-patch main..feature/new-auth

# Output to a specific directory
git format-patch main -o /tmp/patches/

Files created

Text
0001-feat-add-OAuth2-login-flow.patch
0002-fix-handle-empty-user-list.patch
0003-docs-update-API-reference.patch

The files are numbered in order and named from the commit subject — perfect for attaching to an email thread in order.

Anatomy of a format-patch file

0001-feat-add-OAuth2-login-flow.patch

Text
From a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 Mon Sep 17 00:00:00 2001
From: Alice <alice@example.com>
Date: Tue, 20 May 2026 14:32:00 +0000
Subject: [PATCH 1/3] feat: add OAuth2 login flow

Add support for Google and GitHub OAuth2 providers.
Tokens are stored in httpOnly cookies.

---
 src/auth/oauth.ts | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 src/auth/oauth.ts

diff --git a/src/auth/oauth.ts b/src/auth/oauth.ts
new file mode 100644
index 0000000..1a2b3c4
--- /dev/null
+++ b/src/auth/oauth.ts
@@ -0,0 +1,54 @@
+import { createOAuthHandler } from './base'
+
+export const googleOAuth = createOAuthHandler({
...
--
2.44.0

The --- divider separates the commit message body from the diff stats. The -- at the end marks the end of the email and shows the Git version that generated the patch.

Useful format-patch flags

Flag

Effect

-N (e.g., -3)

Generate patches for the last N commits.

-o <dir>

Write patch files to the specified output directory.

--stdout

Print all patches to stdout (one combined mbox) instead of separate files.

--subject-prefix="PATCH v2"

Change the subject prefix from the default [PATCH].

--cover-letter

Generate a 0000-cover-letter.patch for a multi-patch series introduction.

--numbered

Always include [N/M] numbering even for a single patch.

--base=<commit>

Record the base commit for easier cherry-pick on the recipient side.

-v2 / --reroll-count=2

Add v2 prefix for a revised patch series.

Cover letters for patch series

For a series of patches (more than one commit), always include a cover letter that explains the overall intent of the series.

Bash
git format-patch main --cover-letter -o /tmp/patches/

Generated files with cover letter

Text
0000-cover-letter.patch    ← fill in Subject: and body
0001-feat-add-OAuth2-login-flow.patch
0002-fix-handle-empty-user-list.patch

0000-cover-letter.patch (fill in manually)

Text
From: Alice <alice@example.com>
Subject: [PATCH 0/2] Add OAuth2 authentication

*** SUBJECT HERE ***

*** BLURB HERE ***

Alice (2):
  feat: add OAuth2 login flow
  fix: handle empty user list

 src/auth/oauth.ts    | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/auth/session.ts  |  8 ++++----
 2 files changed, 58 insertions(+), 4 deletions(-)
Applying patches with git am

git am ("apply mailbox") applies one or more format-patch files, preserving the original author name, email, date, and commit message. It creates real commits, not just working tree changes.

Bash
# Apply a single patch
git am 0001-feat-add-OAuth2-login-flow.patch

# Apply all patches in a directory (in order)
git am /tmp/patches/*.patch

# Apply from stdin
cat *.patch | git am

# Apply and add a Signed-off-by trailer
git am --signoff 0001-feat-add-OAuth2-login-flow.patch

Successful apply output

Text
Applying: feat: add OAuth2 login flow
Applying: fix: handle empty user list
Handling conflicts in git am

Failed apply output

Text
Applying: feat: add OAuth2 login flow
error: patch failed: src/auth/oauth.ts:12
error: src/auth/oauth.ts: patch does not apply
Patch failed at 0001 feat: add OAuth2 login flow
hint: Use 'git am --show-current-patch' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip".
hint: To restore the original branch and stop patching, run "git am --abort".

Bash
# See which patch failed
git am --show-current-patch

# Option 1 — Resolve manually
git apply --reject 0001-feat-add-OAuth2-login-flow.patch
# Edit the .rej file contents into the target file
vim src/auth/oauth.ts
git add src/auth/oauth.ts
git am --continue

# Option 2 — Skip this patch and move to the next
git am --skip

# Option 3 — Abort and restore the branch
git am --abort

# Use 3-way merge to resolve conflicts automatically where possible
git am -3 0001-feat-add-OAuth2-login-flow.patch
git apply vs git am

Aspect

git apply

git am

Input format

Unified diff (from git diff or diff -u)

Mbox format (from git format-patch)

Creates a commit

No — stages or applies to working tree only

Yes — creates a commit with original metadata

Preserves author/date

No

Yes

Conflict handling

--reject dumps .rej files

--continue/--skip/--abort workflow

Use case

Quick local patches, testing diffs

Applying submitted patches to a project

Creating and applying unified diffs

Bash
# Create a diff of all unstaged changes
git diff > my-changes.patch

# Create a diff of all staged changes
git diff --cached > staged.patch

# Create a diff of a specific commit range
git diff v1.0..v1.1 > v1.0-to-v1.1.patch

# Apply a raw diff to the working tree (no commit)
git apply my-changes.patch

# Apply and immediately stage
git apply --cached my-changes.patch

# Check if a patch will apply cleanly without actually applying
git apply --check my-changes.patch

# Apply even if whitespace differences exist
git apply --whitespace=fix my-changes.patch
Inspecting patches before applying

Bash
# See what a patch will do without applying it
git apply --stat 0001-feat-add-OAuth2-login-flow.patch
git apply --check 0001-feat-add-OAuth2-login-flow.patch

# Read the commit metadata from a format-patch file
git mailinfo /tmp/msg /tmp/diff < 0001-feat-add-OAuth2-login-flow.patch
cat /tmp/msg   # author, date, subject
cat /tmp/diff  # the actual diff
The Linux kernel patch workflow

The Linux kernel does not use GitHub pull requests. Contributions are sent as patches via email to mailing lists, reviewed by maintainers, and applied with git am. This workflow scales to thousands of contributors without centralised infrastructure.

  1. Fork the kernel tree and create your changes on a branch.

  2. Run git format-patch --cover-letter -v1 -o patches/ linus/master..HEAD.

  3. Edit the cover letter to describe the series.

  4. Send the patches to the appropriate subsystem mailing list with git send-email.

  5. Maintainers review the patches inline in email.

  6. After revisions, send v2 patches: git format-patch --subject-prefix="PATCH v2".

  7. A maintainer applies accepted patches: git am patches/*.patch.

  8. The maintainer's tree is pulled up the tree toward Linus's tree.

git send-email overview

git send-email sends format-patch files directly from the command line as emails. It is the standard tool for kernel-style email-based patch submission.

Bash
# Install: on Ubuntu/Debian
sudo apt-get install git-email

# Configure your SMTP server once
git config --global sendemail.smtpserver smtp.gmail.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
git config --global sendemail.smtpuser your@gmail.com

Bash
# Generate patches
git format-patch -3 --cover-letter -o /tmp/patches/

# Dry run — see what would be sent without actually sending
git send-email --dry-run /tmp/patches/

# Send to a mailing list
git send-email   --to="linux-kernel@vger.kernel.org"   --cc="maintainer@example.com"   /tmp/patches/

send-email interactive prompts

Text
Who should the emails appear to be from? [Alice <alice@example.com>]
Who should the emails be sent to? linux-kernel@vger.kernel.org
Message-ID to be used as In-Reply-To for the first email?

(sending cover letter)
OK. Log says:
Sendmail: /usr/sbin/sendmail -oi -f alice@example.com ...
From: Alice <alice@example.com>
To: linux-kernel@vger.kernel.org
Subject: [PATCH 0/3] feat: add power management hooks
...
When to use patches
  • No shared remote: applying changes to a server with no network access, by copying a .patch file via SSH or USB.

  • Email-based review: sending a change for review without requiring the reviewer to clone your repo or use a hosted platform.

  • Offline collaboration: exchanging changes on air-gapped systems.

  • Preserving authorship: applying someone else's change and keeping their name as the author (git am vs git apply).

  • Backporting: generating a patch from one release branch and applying it cleanly to an older one.

  • Archiving a specific change: a self-contained .patch file that can be stored with a bug report.

Common errors

Error

Cause

Fix

error: patch failed: file.ts:N

The file has changed since the patch was generated — context lines do not match.

Use git am -3 for 3-way merge, or git apply --reject to produce .rej files you can resolve manually.

Patch is empty

The commit you tried to format has no diff (empty commit).

Use --allow-empty or skip that commit.

fatal: repository has no commits yet

Trying to apply a patch to an unborn branch.

Create an initial commit first.

error: corrupt patch at line N

The patch file was mangled (e.g., email client wrapped lines or converted line endings).

Use git am --keep-cr for Windows line endings, or re-generate the patch.

Warning
Avoid editing patch files manually unless you know the mbox format well. Even a single stray space or an off-by-one in the hunk header (`@@ -N,M +N,M @@`) will cause the patch to fail to apply. If you need to edit a patch, re-generate it from the source commit after making changes.
Tip
Use `git format-patch --base=auto` to automatically record the base commit (the common ancestor between the patch series and the target branch). Recipients can then use `git am --base` to verify the apply point and get a cleaner cherry-pick experience.