The Patch Workflow: format-patch, send-email, checkpatch
Master the complete patch lifecycle — from a clean commit to a well-formatted, correctly-threaded email series that maintainers will actually read and merge.
Overview
Contributing to the Linux kernel is not just about writing good code — it is about communicating that code through a very specific, deliberately low-tech channel: plain-text email. The kernel community has refined this workflow over three decades. It is not arbitrary; every convention exists because it solves a real problem at scale, where thousands of patches flow through hundreds of mailing lists every week, read in text-mode terminals by maintainers who may be traveling, offline, or triaging on a phone.
This module walks you through the complete patch lifecycle. You will start at the moment after you have written a fix or feature, and end at the moment your patch lands in a maintainer's queue. Along the way you will learn how to make a commit that tells the full story, how git format-patch turns that commit into a portable artifact, how checkpatch.pl catches dozens of style problems before a human reviewer ever sees them, and how git send-email delivers the result to exactly the right list and people with correct threading so that review replies attach themselves to your patch in everyone's mail client.
You will also learn the conventions that are entirely invisible unless someone explains them: what a cover letter is and when to write one, why the subject line uses [PATCH v2 2/3] notation, what Signed-off-by legally means, how Reviewed-by and Acked-by differ, and why the kernel uses --- to separate the commit message from a "throw-away" diff stat that does not appear in the permanent history.
This is the layer between "I wrote working code" and "my code is in Linus's tree." Understanding it deeply is what separates a one-time submitter from a recurring contributor.
Every kernel contribution, no matter how technically brilliant, is rejected if it does not follow the patch submission workflow. Maintainers receive hundreds of patches per week. A patch that is not in plain text, that has a malformed subject line, that lacks a Signed-off-by, or that is sent to the wrong list will be silently ignored — not out of rudeness, but because the review infrastructure literally cannot process it. Learning this workflow is not bureaucracy; it is fluency in the language maintainers speak.
Beyond the mechanical process, internalizing the conventions makes you a better engineer. Writing a commit message that explains the why forces you to articulate your reasoning. Running checkpatch.pl before submitting trains you to write code that looks like kernel code. Threading a patch series correctly shows reviewers how to read the pieces in relation to each other. These habits — precision in communication, self-review discipline, structured presentation — transfer to every engineering context you will ever work in.
Turn your commit into a plain-text patch with a Signed-off-by line.
Your patch's journey from your laptop to Linus's tree. Click a step.
The Kernel Patch Lifecycle
A patch starts as a git commit and travels through formatting, style checking, maintainer identification, email delivery, public archiving, and review before landing in the mainline kernel tree. Revision loops are normal — most patches go through at least v2.
The concepts
01What Makes a Clean Commit
Before any tooling is involved, a contribution starts with a git commit that earns its place in the permanent record. The Linux kernel has strict expectations for what a commit should contain, and violating them is the most common reason first-time patches are rejected.
One logical change per commit. A commit should do exactly one thing. "Fix the race condition in the block driver" is a commit. "Fix the race condition AND clean up whitespace AND add a missing unlock" is three commits. This matters because git bisect needs to identify which commit introduced a regression — a commit that does two things defeats bisection. It also matters for review: a reviewer can reason about one change at a time.
The commit message structure. The first line is the subject — 72 characters or fewer, imperative mood ("Add", "Fix", "Remove", not "Added" or "Fixes"). Then a blank line. Then the body, which explains why the change is necessary, what was broken or missing, how the fix works at a conceptual level, and (if appropriate) what testing was done. The body is where maintainers decide whether to trust you.
Trailers. After the body and a blank line come structured tags. Signed-off-by: Your Name <email> is mandatory — it is your legal certification under the Developer's Certificate of Origin (DCO) that you have the right to submit this code. Fixes: <hash> ("<subject>") tells the stable team which commit introduced a regression. Reported-by:, Suggested-by:, Tested-by: give credit appropriately.
A concrete example of a well-formed commit message:
net: fix use-after-free in tcp_cleanup_rbuf
When a socket is closed while tcp_cleanup_rbuf() is executing, the
socket's receive buffer can be freed before the function returns,
leading to a use-after-free. Add a reference count hold across the
critical section.
Reproduced with syzkaller on v6.8-rc3. The KASAN report points to
line 543 of net/ipv4/tcp.c.
Fixes: a1b2c3d4e5f6 ("net: add tcp receive buffer cleanup")
Reported-by: Fuzzer Bot <fuzzer@example.com>
Signed-off-by: Jane Developer <jane@example.com>
Notice what is not in the subject: the file name, the subsystem path, the line number. What is in the subject: a subsystem prefix (net:), an imperative verb (fix), and a concise description. The body tells the story. The trailers provide metadata.
# Correct: one logical change, imperative mood, subsystem prefix
git commit -m "mm: fix off-by-one in vmalloc_to_page bounds check"
# Wrong: past tense, no subsystem, vague
git commit -m "fixed some memory stuff"
02git format-patch: turning commits into portable artifacts
Once your commit exists, git format-patch turns it into a .patch file — a plain-text file that contains your commit metadata (author, date, message) AND the diff, formatted in a way that git am can re-apply exactly. This format predates GitHub by many years and works perfectly over email.
Basic usage. git format-patch HEAD~1 produces one .patch file for the most recent commit. git format-patch HEAD~3 produces three .patch files — one per commit, numbered 0001-, 0002-, 0003-.
The anatomy of a .patch file. Every patch file begins with a pseudo-email header:
From abc123def456 Mon Sep 17 00:00:00 2001
From: Jane Developer <jane@example.com>
Date: Mon, 10 Jun 2024 14:23:01 +0530
Subject: [PATCH] net: fix use-after-free in tcp_cleanup_rbuf
Then the commit message body. Then ---. Then a diffstat (summary of files changed, lines added/removed). Then the actual diff. The --- separator is important: everything below it is thrown away when the patch is applied with git am. This means you can put review notes, diffstat summaries, or "this applies on top of X" notes after --- without polluting the permanent commit message.
The --base flag. git format-patch --base=HEAD~3 HEAD~3..HEAD records which commit your series is based on. This helps maintainers know which tree to apply your patches to.
Naming and output directory. git format-patch -o /tmp/patches/ HEAD~3 writes all three patches into /tmp/patches/, keeping your working directory clean.
Version bumps. When you revise after review, use -v2, -v3, etc.: git format-patch -v2 HEAD~1. This makes the subject line read [PATCH v2] automatically, signaling to reviewers that this is a revised version.
# Single patch for the last commit
git format-patch HEAD~1
# Output: 0001-net-fix-use-after-free-in-tcp_cleanup_rbuf.patch
# Patch series, output to a temp directory
git format-patch -o /tmp/mypatches/ HEAD~3
# Output:
# /tmp/mypatches/0001-mm-fix-vmalloc-bounds-check.patch
# /tmp/mypatches/0002-mm-add-vmalloc-selftest.patch
# /tmp/mypatches/0003-mm-update-vmalloc-documentation.patch
# Revision 2, with base recorded
git format-patch -v2 --base=linus/master HEAD~3
03checkpatch.pl: the kernel's automated style enforcer
scripts/checkpatch.pl is a Perl script in the kernel source tree that checks a patch file (or a git commit range, or a C file) for coding style violations, common bugs, and anti-patterns. It is not optional — submitting a patch with checkpatch warnings is a nearly guaranteed way to get a review comment asking you to fix them before anyone looks at the substance.
What it checks. Checkpatch looks for: trailing whitespace, lines longer than 80 characters (hard limit: 100), missing SPDX-License-Identifier, missing Signed-off-by, incorrect spacing around operators, using printk instead of pr_err / dev_err, declaring variables mid-block (old C99 style discouraged in older kernel code), sizeof(type) where sizeof(*ptr) is preferred, use of __attribute__ where kernel macros exist, and many more. It also detects common logical mistakes like missing break in switch statements.
Running it. You can run it on a patch file, on a commit range, or on a source file:
./scripts/checkpatch.pl 0001-my-patch.patch
./scripts/checkpatch.pl --git HEAD~3..HEAD
./scripts/checkpatch.pl --file drivers/net/ethernet/intel/e1000e/netdev.c
Reading the output. Checkpatch reports three severity levels: ERROR (must fix), WARNING (should fix), CHECK (consider fixing). Treat ERROR and WARNING as blockers. CHECK items are often style preferences where you have more latitude.
Strict mode and extra checks. --strict enables all CHECK items. --no-tree lets you run it outside the kernel source tree. Most subsystem maintainers expect zero ERROR and zero WARNING before you submit.
What checkpatch does NOT catch. Checkpatch is a syntactic and style tool. It does not understand semantics: it will not catch a race condition, a missing memory barrier, or a wrong algorithm. It is a floor, not a ceiling. After checkpatch passes, your code still needs to be correct.
# Run checkpatch on a formatted patch (most common workflow)
cd /path/to/linux
./scripts/checkpatch.pl /tmp/mypatches/0001-mm-fix-vmalloc-bounds-check.patch
# Sample output (bad patch):
# ERROR: trailing whitespace
# #42: FILE: mm/vmalloc.c:312:
# + if (addr > end)
#
# WARNING: line over 80 characters
# #67: FILE: mm/vmalloc.c:337:
# + if (unlikely(addr < VMALLOC_START || addr > VMALLOC_END || addr == NULL)) {
#
# total: 1 errors, 1 warnings, 0 checks, 23 lines checked
# Clean patch output:
# total: 0 errors, 0 warnings, 0 checks, 23 lines checked
# Your patch looks OK.
04get_maintainer.pl: finding who to send to
The Linux kernel has no central email address for patches. Each subsystem has one or more maintainers and one or more mailing lists, recorded in the MAINTAINERS file at the root of the tree. Sending to the wrong list is a common beginner mistake that results in silence — not an error, just no response.
scripts/get_maintainer.pl reads the MAINTAINERS file and maps files touched by your patch to the right people and lists. It is essential.
Usage. Pass it the patch file:
./scripts/get_maintainer.pl 0001-mm-fix-vmalloc-bounds-check.patch
It outputs a list of emails with roles:
(maintainer)— the person who reviews and merges patches for this subsystem(reviewer)— designated reviewers(mailing list)— the list the patch should go to(open list)— broader lists (oftenlinux-kernel@vger.kernel.org)
The --nogit and --norolestats flags simplify output when you are scripting. For manual use, the default output is fine.
Always CC linux-kernel@vger.kernel.org. LKML (Linux Kernel Mailing List) is the archive of record. Even when your patch is specific to a subsystem, LKML should be CC'd so that it appears in the public archive and can be found by anyone searching kernel history.
Never send to a person's private email unless they have explicitly asked for it. The mailing list is the right channel; a private email cannot be reviewed by the community.
cd /path/to/linux
./scripts/get_maintainer.pl /tmp/mypatches/0001-mm-fix-vmalloc-bounds-check.patch
# Sample output:
# Andrew Morton <akpm@linux-foundation.org> (maintainer:MEMORY MANAGEMENT)
# linux-mm@kvack.org (open list:MEMORY MANAGEMENT)
# linux-kernel@vger.kernel.org (open list)
# Use --separator , to get a comma-separated list for --to/--cc:
./scripts/get_maintainer.pl --separator , \
/tmp/mypatches/0001-mm-fix-vmalloc-bounds-check.patch
05git send-email: delivering patches correctly
git send-email is the canonical tool for sending kernel patches. It sends each patch file as its own email, with correct threading so they appear as a conversation in mail clients and archives. Using a regular mail client (Gmail, Thunderbird, Outlook) almost always corrupts patches because those clients do word-wrapping, line-ending conversion, HTML encoding, or base64 encoding — all of which break git am.
Initial setup. You need to configure git to know your SMTP server. For Gmail:
[sendemail]
smtpServer = smtp.gmail.com
smtpServerPort = 587
smtpEncryption = tls
smtpUser = you@gmail.com
For Gmail you also need an App Password (not your main password) because Google blocks direct SMTP login without 2FA app passwords.
Basic invocation — single patch. The --to flag sets the primary recipient (usually the maintainer or mailing list), --cc adds carbon copies:
git send-email \
--to="linux-mm@kvack.org" \
--cc="akpm@linux-foundation.org" \
--cc="linux-kernel@vger.kernel.org" \
0001-mm-fix-vmalloc-bounds-check.patch
Dry run. Always use --dry-run first to see exactly what would be sent without actually sending it. This is your safety net.
Interactive confirmation. By default, git send-email pauses before each email and shows you who it is being sent to. Type y to send or q to quit.
--confirm=always vs --confirm=never. For a series, use --confirm=always during learning so you see each message before it goes.
Subject line rewriting. git send-email uses the subject from the patch file. You should not have to manually edit subjects if you used git format-patch correctly with -v2 etc.
# Step 1: dry run — see what would be sent
git send-email \
--dry-run \
--to="linux-mm@kvack.org" \
--cc="akpm@linux-foundation.org" \
--cc="linux-kernel@vger.kernel.org" \
/tmp/mypatches/0001-mm-fix-vmalloc-bounds-check.patch
# Step 2: actually send
git send-email \
--to="linux-mm@kvack.org" \
--cc="akpm@linux-foundation.org" \
--cc="linux-kernel@vger.kernel.org" \
/tmp/mypatches/0001-mm-fix-vmalloc-bounds-check.patch
06Patch Series, Cover Letters, and Threading
A patch series is a set of logically related commits submitted together. For example: a bug fix, a selftest for that fix, and a documentation update. These three patches are sent as three separate emails, but they must be threaded — that is, emails 2 and 3 must be replies to email 1, and email 1 (the cover letter) introduces the whole series.
The cover letter. For a series of more than one patch, you write a cover letter: git format-patch --cover-letter -o /tmp/mypatches/ HEAD~3. This creates 0000-cover-letter.patch. Edit this file — the [PATCH 0/3] email — to explain the overall goal, any context reviewers need, and what changed since the previous version (if this is v2+). The cover letter is the only patch that is NOT applied; it is purely for human readers.
Subject line convention. For a series of 3 patches at version 2:
[PATCH v2 0/3] mm: vmalloc bounds checking fixes <- cover letter
[PATCH v2 1/3] mm: fix off-by-one in vmalloc_to_page
[PATCH v2 2/3] mm: add selftest for vmalloc bounds
[PATCH v2 3/3] mm: document vmalloc address space layout
The format-patch tool generates the numbering automatically; -v2 adds the version.
Threading. When you send the series with git send-email /tmp/mypatches/, it automatically makes patches 1/3, 2/3, 3/3 in-reply-to the cover letter. This means every mail client and every archive (Lore, MARC, Patchwork) shows them as one thread. Reviewers can reply inline to patch 2/3 without losing track of the series context.
--in-reply-to for manual threading. If you need to reply to a specific existing message (for example, to thread your v2 under your v1 thread), use:
git send-email --in-reply-to="<message-id@mail.kernel.org>" ...
The Message-ID is visible in the raw email headers on lore.kernel.org.
Interdiff and change logs. In your v2 cover letter, add a Changes in v2: section listing what you changed based on review feedback. For individual patches, you can add a change log after the --- separator (so it does not appear in the permanent commit message). Reviewers deeply appreciate this — it shows you listened.
# Generate a 3-patch series with a cover letter
git format-patch -v2 --cover-letter -o /tmp/mypatches/ HEAD~3
# Creates:
# 0000-cover-letter.patch <- edit this!
# 0001-mm-fix-off-by-one-in-vmalloc_to_page.patch
# 0002-mm-add-selftest-for-vmalloc-bounds.patch
# 0003-mm-document-vmalloc-address-space-layout.patch
# Send the whole series (cover letter first, rest as replies)
git send-email \
--to="linux-mm@kvack.org" \
--cc="akpm@linux-foundation.org" \
--cc="linux-kernel@vger.kernel.org" \
/tmp/mypatches/
07Plain-Text Email and Why HTML Email Breaks Everything
The kernel community's insistence on plain-text email is one of the most frequently misunderstood rules. It seems like an anachronism until you understand what git am does.
git am applies a patch by parsing the raw email: it reads the From:, Date:, and Subject: headers, extracts the commit message, finds the --- separator, skips the diffstat, and applies the diff. The diff must be character-perfect — every space, tab, +, and - must be exactly as git format-patch produced them.
How HTML email breaks diffs. HTML email clients wrap lines at 72-76 characters and insert <br> or soft hyphens. They convert -- to an em-dash. They may base64-encode the entire body or turn it into a MIME multipart with a text/html part that contains the diff inside HTML tags. Any of these transformations make git am fail silently or with a confusing error. The patch cannot be applied.
How to send plain-text email. The safest method is always git send-email. If you must use a mail client for discussion emails (not patches), configure it for plain text:
- Thunderbird: Account Settings > Composition & Addressing > uncheck "Compose messages in HTML format"
- mutt/neomutt: default plain text
- Gmail web: click the formatting button in compose and select "Plain text mode"
Inline replies, not top-posting. When responding to review, quote the relevant lines, put your response below them (inline or bottom-posted), and trim the parts you are not responding to. Top-posting (replying above the quoted text) is strongly discouraged because it makes threads hard to read linearly.
No attachments. Never send patches as attachments. Always inline them. An inline patch can be copy-pasted directly into a terminal with git am. An attachment requires extra steps and signals inexperience.
# Test whether your mail client would corrupt the patch:
# After sending, view the raw email on lore.kernel.org
# and run:
curl -s 'https://lore.kernel.org/<thread-url>/raw' | git am
# If git am fails, your mail client corrupted it.
# Switch to git send-email.
08Trailer Tags: Signed-off-by, Reviewed-by, Acked-by and Friends
The lines at the bottom of a commit message — called trailers — are structured metadata with precise meanings. Confusing them signals a lack of familiarity with the process.
Signed-off-by (mandatory). This is your agreement with the Developer's Certificate of Origin (DCO). By adding Signed-off-by: Name <email>, you certify that: (a) you wrote the code yourself, (b) you have permission from its original author, or (c) it was provided under a compatible open-source license. It is a legally meaningful statement. Every commit must have at least your own Signed-off-by. If a maintainer applies your patch, their Signed-off-by is added to the chain, showing the path of responsibility.
Reviewed-by. A reviewer adds this to say "I have carefully read this patch, I believe it is correct, and it looks good to me." It does not carry the same legal weight as Signed-off-by but is a strong signal of quality. Maintainers value it — a patch with multiple Reviewed-by tags is much more likely to be merged quickly.
Acked-by. A subsystem maintainer or stakeholder adds this to say "I am OK with this going in, even if I have not done a full line-by-line review." It is weaker than Reviewed-by but meaningful — it means the right person has seen it and does not object.
Tested-by. Added by someone who has tested the patch on real hardware or a specific configuration. This is especially valued for driver patches and hardware-specific fixes.
Reported-by. Credits the person who filed the bug report. If someone filed a bug in Bugzilla or on the mailing list, they deserve credit. This also creates a searchable audit trail.
Fixes:. Format: Fixes: <12-char-hash> ("<subject of broken commit>"). This tells the stable tree maintainers that your patch fixes a regression introduced by a specific commit. It also tells git bisect users which commit to bisect to. The parenthesized subject is required — it makes the reference human-readable.
Cc: stable@vger.kernel.org. Adding this tag (literally in the commit message) flags the patch for backporting to stable kernel branches. Only use it for clear bug fixes with no API changes.
# A complete, well-tagged commit message
git commit
# Opens your editor with:
# drivers/gpu/drm/amd: fix null pointer deref in amdgpu_cs_parser_init
#
# When the job context is destroyed before amdgpu_cs_parser_init
# completes, the ctx pointer can be freed, leading to a null pointer
# dereference on the next access.
#
# Add a reference count hold before the critical section and drop it
# on all exit paths.
#
# Fixes: deadbeef1234 ("drm/amdgpu: add job context initialization")
# Cc: stable@vger.kernel.org
# Reported-by: Syzbot <syzkaller@googlegroups.com>
# Tested-by: Alex Reviewer <alex@example.com>
# Reviewed-by: Christian Koenig <christian.koenig@amd.com>
# Signed-off-by: Jane Developer <jane@example.com>
09The --- Separator and Diffstat in Patch Files
The --- line in a .patch file is one of the most useful but least explained conventions. It is a hard boundary with a precise meaning: everything above --- becomes the permanent commit message when git am applies the patch; everything below --- is discarded.
What goes below ---? The standard git format-patch output automatically places a diffstat below ---:
---
mm/vmalloc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
This diffstat is convenient for reviewers glancing at the scope of the change but is not preserved in history.
What else can you put below ---? Anything you want reviewers to see but do not want in the permanent record. Common uses:
- "This patch applies on top of commit
abc123from Andrew's tree" - "Tested on: x86_64 with CONFIG_DEBUG_VM=y, ARM64 with defconfig"
- Version change notes for v2/v3 of individual patches (as opposed to the cover letter)
- A link to a related discussion thread
Why this matters. New contributors sometimes put change notes in the commit message body (above ---) because they do not know about the separator. This results in ugly permanent commit messages that say things like v2: added missing unlock in the history forever. Maintainers will ask you to clean this up. Put version change notes below ---.
Viewing the separator in the patch file. After git format-patch, open the .patch file in a text editor and scroll to find the --- line. Everything between it and the first diff --git line is your "reviewer notes" zone.
# Example: view the --- separator in a generated patch
cat /tmp/mypatches/0001-mm-fix-off-by-one.patch
# Output excerpt:
# Subject: [PATCH v2 1/3] mm: fix off-by-one in vmalloc_to_page
#
# When addr equals exactly VMALLOC_END, the bounds check fails to
# catch an out-of-range access. Correct the comparison operator.
#
# Fixes: abc123def456 ("mm: add vmalloc_to_page")
# Signed-off-by: Jane Developer <jane@example.com>
# ---
# Changes in v2:
# - Use >= instead of > per Andrew's suggestion
# - Add comment explaining the boundary condition
#
# mm/vmalloc.c | 2 +-
# 1 file changed, 1 insertion(+), 1 deletion(-)
#
# diff --git a/mm/vmalloc.c b/mm/vmalloc.c
# ...
10Lore and Patchwork: the patch archive and tracking system
After you send a patch, it does not disappear into a black hole — it lands in public archives and a tracking system that maintainers and contributors use to manage the review lifecycle.
lore.kernel.org. This is the official, permanent, searchable archive of all kernel mailing lists. Every email sent to a kernel mailing list is archived here within minutes. You can browse threads, link to specific messages, view raw email (click raw to get the patch in exactly the form git am expects), and reply. When reviewers say "link to the thread on lore", this is where they mean.
Patchwork (patchwork.kernel.org / patch.kernel.org). Patchwork is a web application that automatically parses emails on kernel mailing lists and identifies patches. It tracks the state of each patch: New, Under Review, Accepted, Rejected, Superseded. Maintainers use it to manage their queue. When a maintainer applies your patch, they mark it Accepted. When you send v2, the v1 is automatically marked Superseded.
b4. b4 is a newer command-line tool (available via pip) that makes working with lore/patchwork much easier. b4 am <lore-url> downloads a patch series from lore and prepares it for git am in one command. b4 prep helps you prepare and track your patch series. Many maintainers and contributors use b4 as a modern replacement for the manual git format-patch + git send-email workflow, though the underlying concepts are identical.
Checking your submission. After sending, wait a few minutes and then search lore.kernel.org for your email subject. Verify it arrived intact, the formatting looks correct, and the thread structure is right. This is your QA step.
# Install b4 (modern patch management tool)
pip install b4
# Download and apply a patch series from lore
b4 am https://lore.kernel.org/linux-mm/20240610142301.12345-1-jane@example.com/
# Prepare a new patch series with b4 (tracks revisions)
b4 prep -n "fix-vmalloc-bounds" -b linus/master
# After making commits:
b4 send
11Responding to Review: the feedback loop
Sending the patch is the beginning of the process, not the end. The review loop is where contributions live or die. Understanding how to respond correctly is as important as writing the patch itself.
Acknowledge every comment. Even if you disagree, acknowledge that you read and understood the feedback. Silence is the worst response — it signals that you are not engaged.
Inline replies. Quote the specific line of review feedback you are addressing, put your reply directly below it, and trim everything else. This is the standard email reply style in the kernel community (sometimes called "interleaved" or "inline" replies). Avoid top-posting (replying above all the quoted text).
When you agree. Make the change, note it in the v2 change log (below ---), and thank the reviewer. You do not need to respond to the original email explaining every change — the v2 patch is the response.
When you disagree. Explain your reasoning clearly and respectfully. Maintainers are not always right. But you need to make the technical case, not appeal to authority or emotion. If a senior maintainer disagrees and you have explained your position twice, it is usually wise to defer unless you have a very strong technical argument.
Sending v2. After incorporating review feedback: amend your commits, re-run checkpatch.pl, re-run get_maintainer.pl (in case the diff changed), re-run git format-patch -v2, and re-run git send-email. Include --in-reply-to with the Message-ID of the v1 cover letter or patch so reviewers can find the history.
The change log. In the cover letter (for a series) or in the patch body below --- (for a single patch), add:
Changes in v2:
- Fixed the off-by-one error (reported by Andrew Morton)
- Added selftest as requested by the mm maintainer
- Corrected commit message grammar
This is a courtesy that reviewers genuinely appreciate.
# Workflow for v2 after review:
# 1. Amend the commit(s) based on feedback
git rebase -i HEAD~3 # or git commit --amend for a single patch
# 2. Re-check style
./scripts/checkpatch.pl --git HEAD~3..HEAD
# 3. Generate v2 patches
git format-patch -v2 --cover-letter -o /tmp/v2patches/ HEAD~3
# 4. Edit cover letter to add "Changes in v2" section
vim /tmp/v2patches/0000-cover-letter.patch
# 5. Send v2 threaded under v1
# Find v1 message ID from lore.kernel.org
git send-email \
--in-reply-to="<20240610142301.12345-0-jane@example.com>" \
--to="linux-mm@kvack.org" \
--cc="akpm@linux-foundation.org" \
/tmp/v2patches/
Key insights
- ✦A patch is communication first, code second — the commit message explaining *why* matters more than the diff to a first-time reviewer.
- ✦The `---` separator in a patch file is a hard boundary: above it becomes permanent history, below it is reviewer-only scratch space. Knowing this prevents the most common commit message pollution mistake.
- ✦checkpatch.pl is your pre-flight checklist, not your linter — a patch with no checkpatch warnings is the minimum bar, not evidence of correctness.
- ✦git send-email exists specifically because all other mail clients corrupt patches through line-wrapping, HTML encoding, or charset conversion. There is no workaround; use the right tool.
- ✦Threading is not cosmetic — a correctly threaded series allows maintainers, reviewers, and archives to understand the relationship between patches and attach review comments to the right piece.
- ✦Signed-off-by is a legal statement under the DCO, not just a name tag. Every commit needs yours; the chain of Signed-off-by lines traces responsibility through the tree.
- ✦The patch workflow is designed for asynchronous, distributed review by people in every timezone. Every convention (plain text, threading, inline replies, change logs) optimizes for exactly that environment.
Common pitfalls
- ⚠Sending with Gmail web or Outlook: these clients wrap lines and corrupt diffs. Always use git send-email.
- ⚠Putting change-log notes ("v2: fixed the thing") inside the commit message body above `---` — they end up in the permanent git history permanently cluttering commit messages.
- ⚠Forgetting get_maintainer.pl and guessing the right mailing list — patches sent to the wrong list are silently ignored, not bounced.
- ⚠Sending a v2 without an `--in-reply-to` link to v1 — reviewers lose the thread history and cannot see what changed.
- ⚠A commit subject line in past tense ("Fixed the bug") or without a subsystem prefix — both signal inexperience and invite a style review comment before substance.
- ⚠Using `git send-email` with `--confirm=never` before you are comfortable — you may accidentally send a test patch to a real mailing list.
- ⚠Silently ignoring review comments — even a short "I'll fix this in v2" reply shows you are engaged and earns goodwill.
- ⚠Adding `Cc: stable@vger.kernel.org` to every patch — this tag is for genuine regressions only; abusing it wastes the stable team's time and signals a lack of judgment.
Practice
- 01easy
Clone a subsystem tree (e.g., `git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git --depth=1`), find a file with a comment that has a typo (search with `grep -r 'teh ' drivers/` or similar), fix it, write a proper commit message with `Signed-off-by`, run `checkpatch.pl` to zero warnings, and run `get_maintainer.pl` to find the right list. Do NOT actually send — just complete every step up to `git send-email --dry-run`.
- 02medium
Write a three-patch series: (1) a trivial bug fix (e.g., add a missing `break` in a switch statement in a staging driver), (2) a selftest or comment explaining the fix, (3) a MAINTAINERS or Kconfig help text update. Use `git format-patch --cover-letter -v1 -o /tmp/series/ HEAD~3`. Write a real cover letter. Verify threading by inspecting the `In-Reply-To` header in patches 1-3 with `grep 'In-Reply-To' /tmp/series/*.patch`.
- 03medium
Set up `git send-email` with a real SMTP server (Gmail with an App Password, or a local postfix/sendmail setup). Send your single-patch series to yourself (use your own email as both `--to` and `--cc`). Receive the email, save it as a `.eml` file, run `git am` on it in a fresh clone, and verify the commit message and authorship are perfectly preserved.
- 04easy
Find an actual open patch on lore.kernel.org (search for `[PATCH] staging:` on https://lore.kernel.org/linux-staging/) that has received at least one review comment. Read the full thread. Write a detailed analysis: What did the reviewer object to? Was the response adequate? Was the patch eventually accepted or abandoned? This is pure reading practice — understanding how real review conversations work.
- 05hard
Install the `b4` tool (`pip install b4`). Find a patch series on lore.kernel.org with at least 3 patches. Use `b4 am <url>` to download and apply it to a fresh kernel clone. Then simulate responding to a review by creating a v2 with one commit amended, and use `b4 send --dry-run` to see what it would send. Compare the process with the manual `git format-patch` + `git send-email` workflow.
Resources
The official, authoritative kernel documentation on the entire patch submission process — required reading, bookmark it permanently.
The document checkpatch.pl enforces — read it once to understand *why* each rule exists.
Full reference for every flag — especially useful for SMTP setup and threading options.
The permanent public archive of all kernel mailing lists; where you verify your patches landed intact.
Chapter on the development process and patch submission workflow — written by a core kernel developer.
The modern tool that wraps lore + format-patch + send-email into a streamlined workflow used by many kernel contributors.
Practical first-contributor perspective on the philosophy and mechanics of kernel patches.
The big-picture development process document that explains where patches go and how decisions are made.
Web interface for tracking the state of patches on kernel mailing lists — use this to check if your patch was applied.
Checkpoints — you can now…
- ✓You can write a kernel commit message with a correct subsystem prefix, imperative subject, explanatory body, and proper Signed-off-by trailer.
- ✓You can run git format-patch to produce numbered .patch files for a single commit and a multi-patch series, including a cover letter.
- ✓You can run checkpatch.pl on a patch file and interpret ERROR, WARNING, and CHECK output, fixing all errors and warnings before submitting.
- ✓You can run get_maintainer.pl to find the correct mailing list and maintainer for any file in the kernel tree.
- ✓You can configure git send-email with SMTP credentials and use --dry-run to preview exactly what would be sent before sending.
- ✓You can send a correctly-threaded patch series where all patches reply to the cover letter, and verify the threading on lore.kernel.org.
- ✓You can write a v2 cover letter with a Changes section, generate version-bumped patches with -v2, and thread them under the original v1 thread using --in-reply-to.
- ✓You understand the --- separator and can place reviewer-only notes (change logs, test instructions) below it without polluting the permanent commit message.