Patch Craft
A patch is not a diff. It is a diff plus an argument, addressed to a specific person, that has to survive being read in three years by someone bisecting a regression.
This chapter covers what makes one patch one patch, what a commit message is for, which tags to use, and why a series has to bisect.
The specification is in your tree and you should read it before this chapter:
$EDITOR ~/kernel/linux/Documentation/process/submitting-patches.rst
$EDITOR ~/kernel/linux/Documentation/process/submit-checklist.rst
One Logical Change
The rule is not "small patches". It is one logical change per patch, which sometimes means a 1,000-line patch and sometimes means splitting a 20-line change into three.
ONE PATCH SPLIT IT
───────── ────────
Rename a function and fix all A rename PLUS a behavior change
400 call sites (two reviews, two revert units)
Add a driver, all of it A driver plus an unrelated
(it is one thing that did not cleanup in a header you noticed
exist before)
Fix one bug Fix two bugs
(one may be backported, the
other may be wrong)
A refactor that enables a fix, Both in one patch — now the
as patch 1 of 2 refactor cannot be reverted
independently
The two questions that settle almost every case:
- Could one half be reverted without the other? If yes, they are two patches.
- Would a reviewer want to say "yes" to one and "no" to the other? If yes, they are two patches.
Warning: The most common split request is "do not mix a cleanup with a fix." If you fix a bug and also tidy the whitespace around it, the fix cannot be cleanly backported to stable, and the reviewer has to separate signal from noise to check your one real change. Send the fix; send the cleanup after it, or not at all.
The Commit Message
The message is read far more often than the diff, by people who do not have your context: a maintainer deciding whether to apply it, a stable maintainer deciding whether to backport it, and someone in 2031 who bisected a regression to your commit and needs to know what you were doing.
The shape
subsystem: imperative summary under about 70 characters
Describe the PROBLEM first, in the present tense. What is wrong today,
under what conditions, and what does the user observe? A reader who has
never seen this code must be able to tell whether they have this bug.
Then explain the FIX, and why it is the right one. If you considered
another approach and rejected it, say so — that saves a review round trip
because the reviewer was about to suggest it.
Then say HOW YOU KNOW. Which test, on what hardware or config, with what
result. "Tested on x86_64 and arm64 under QEMU with KASAN enabled" is
worth more than three paragraphs of reasoning.
Wrap the body at about 72-75 columns. Do not wrap pasted oops output,
log lines, or anything a reader might want to grep for.
Fixes: abc123def456 ("subsystem: the original commit's subject")
Reported-by: Some Person <some@person.example>
Closes: https://lore.kernel.org/all/<message-id>/
Signed-off-by: Your Name <you@example.com>
The subject line
# Get the prefix convention from the file's own history. Do not invent one.
git log --oneline -20 -- drivers/gpu/drm/vkms/
| Rule | Why |
|---|---|
subsystem: prefix, taken from git log on that path | It is how maintainers scan a hundred subjects |
| Imperative mood: "Fix", "Add", "Remove" — not "Fixed", "Adds", "Fixing" | Reads as an instruction to the codebase. git log --oneline becomes a list of operations. |
| Under ~70 characters including the prefix | So git log --oneline fits in a terminal |
| No trailing period | Convention |
| Specific | "Fix bug" tells a bisecting reader nothing |
BAD GOOD
─── ────
Fixed a bug vkms: Fix NULL deref on unbind
this patch adds support for X drm/vkms: Add plane rotation support
mm: changes mm/page_alloc: Avoid overflow in ...
drm/vkms: fix. drm/vkms: Free the CRTC state on error
The body
Three prohibitions that maintainers enforce, especially the tip tree:
| Do not write | Write instead |
|---|---|
| "This patch fixes…" | "Fix…" — the message is about the patch; saying so is noise |
| "I changed X to Y" | Why X was wrong. The diff already shows what changed. |
| Nothing at all | Even a one-line fix needs a "why". Especially a one-line fix. |
Tip: The test for a commit message is: can a reader tell whether they are affected by this bug, without applying the patch? If your message is "Fix a race in the reset path", they cannot. If it is "A concurrent reset and submit can free the context while the submit path still holds a pointer to it, which KASAN reports as a use-after-free in
foo_submit()", they can.
Tags and Trailers
Trailers live at the bottom of the commit message, one per line, no blank lines between them. They are machine-parsed; the format is exact.
| Trailer | Means | Who adds it |
|---|---|---|
Signed-off-by: | The DCO: you wrote this or have the right to submit it | You. Then everyone who passes it along. |
Co-developed-by: | Someone else co-wrote it | You — and it must be immediately followed by that person's Signed-off-by: |
Reported-by: | Who found the bug | You, with their permission |
Closes: | The report this closes (a lore or bug-tracker URL) | You. Expected alongside Reported-by:. |
Suggested-by: | Whose idea the fix was | You, with permission |
Reviewed-by: | "I read this and believe it is correct" | The reviewer, in reply. You collect it into v2. |
Acked-by: | "I am fine with this touching my area" — weaker than Reviewed-by | The maintainer of an area you touched |
Tested-by: | "I ran it and it works" | Whoever tested it |
Fixes: | Which commit introduced the bug | You |
Cc: stable@vger.kernel.org | Request a backport | You, when the rules apply |
Link: | The list discussion this came from | You, or the maintainer on apply |
Never invent a Reviewed-by: or Acked-by:. They are attestations by named people. Add them
only when that person wrote them in a reply, and carry them forward to the next version only if your
changes are trivial — if you substantially reworked the patch, the review no longer applies and you
should drop the tag and say so.
Fixes:
This is the single most valuable tag on a bug fix, because it is what lets stable maintainers and distributions work out which releases need it.
# The canonical format: 12+ hex digits and the subject in parens.
git log -1 --pretty=fixes <sha>
# → Fixes: 1234567890ab ("subsystem: the original subject")
# Set this once so everything you produce uses 12 characters:
git config --global core.abbrev 12
Finding the guilty commit:
git log -S'the_broken_thing' --oneline -- path/to/file.c
git blame -L '/the_broken_line/,+1' path/to/file.c
git bisect ... # when it is not obvious — see Engineering
checkpatch.pl validates the format, and gets it wrong often enough that reading its complaint is
worthwhile rather than reflexive.
Cc: stable
$EDITOR ~/kernel/linux/Documentation/process/stable-kernel-rules.rst
The rules, compressed: it must fix a real bug users hit, be already accepted upstream (or on its way), be small (roughly under 100 lines), and not add features. Then:
Cc: stable@vger.kernel.org
or, when only some releases are affected:
Cc: stable@vger.kernel.org # 6.1.x
Note: Do not put
stable@vger.kernel.orgin yourgit send-emailrecipients for a patch that is not upstream yet. The tag in the commit message is the mechanism; the stable maintainers pick it up after it lands in Linus's tree. Mailing them a not-yet-merged patch just creates work. A correctFixes:tag often gets a patch picked up by AUTOSEL even withoutCc: stable.
Everything Below ---
git strips everything between the --- line and the diff. That is where you put things reviewers
need and history does not.
Signed-off-by: Your Name <you@example.com>
---
Changes in v3:
- Use kvmalloc() instead of kmalloc() for the large case (Jane)
- Drop the unrelated whitespace fix into a separate patch
- Add the selftest Jakub asked for
Changes in v2:
- Check the return value of copy_from_user() (Ingo)
- Reword the commit message to describe the user-visible symptom
Tested on x86_64 and arm64 under QEMU with KASAN + lockdep.
Base: net-next commit abc1234 ("...")
---
drivers/foo/bar.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
Crediting the reviewer who prompted each change — (Jane) — is a small courtesy that reviewers
notice, and it lets them check that you actually addressed their point.
Series and Bisectability
A series is an ordered set of patches sent as one email thread with a [PATCH 0/N] cover letter.
The hard rule
Every commit in the series must build and boot. Not just the last one.
This is not aesthetics. git bisect compiles and runs arbitrary intermediate commits, forever. A
commit that does not build turns a bisection into a manual slog for whoever is chasing a regression
in 2030, and it is a standing reason for a maintainer to refuse a series.
# Prove it, do not assume it:
git rebase --exec 'make -j$(nproc) O=../build' <base>
# Or, more thoroughly:
git rebase --exec 'make -j$(nproc) O=../build && ~/kernel-labs/scripts/rig-check.sh' <base>
Ordering
[PATCH 0/4] cover letter — the argument for the whole series
[PATCH 1/4] refactor: extract the helper ← no behavior change
[PATCH 2/4] add the new capability ← enabled by 1/4
[PATCH 3/4] use it in the driver ← the actual point
[PATCH 4/4] add a selftest ← proves 3/4
| Rule | Why |
|---|---|
| Preparatory refactors come first | So the interesting patch is small and reviewable |
| No patch depends on a later one | Otherwise the tree is broken in between |
| A patch that only moves code says so, and moves nothing else | So the reviewer can diff it mechanically |
| Tests come after the code they test, in the same series | So a bisect never lands on a failing test |
| If it is more than about 15 patches, it is probably two series | Reviewers have finite attention |
The cover letter
Only for multi-patch series (some maintainers want one for two patches; check the profile). It is the only place to make the overall argument, and it does not become part of history.
Subject: [PATCH 0/4] drm/vkms: Support plane rotation
This series adds 90/180/270-degree plane rotation to vkms.
Problem: vkms advertises no rotation support, so IGT's rotation tests
skip on it, and we have no CI coverage for the rotation paths in the
atomic helpers.
Approach: patches 1-2 extract the existing blit path into a helper and
add a rotation parameter to it. Patch 3 wires up the property. Patch 4
adds the selftest.
Not addressed: reflection, and rotation combined with scaling. Both need
the composer rework discussed in <link>, which I would rather do
separately.
Testing: igt@kms_rotation_crc on x86_64 and arm64 under QEMU, with
KASAN and lockdep enabled. All previously-passing tests still pass.
Based on drm-misc-next commit abc1234.
The four headings — problem, approach, what is not done, testing — answer the four questions a maintainer would otherwise have to ask.
Producing the Patches
# A single patch:
git format-patch -1 --base=auto
# A series with a cover letter, at version 2:
git format-patch -v2 --cover-letter --base=auto --thread=shallow <base>..HEAD
# What the flags do:
# -v2 subject becomes [PATCH v2 N/M]
# --cover-letter generate 0000-cover-letter.patch (EDIT IT — the template is empty)
# --base=auto append a `base-commit:` line so reviewers and CI know
# exactly what to apply this to. Underused; use it.
# --thread=shallow every patch replies to the cover letter
Then check what you produced, before sending:
./scripts/checkpatch.pl --strict *.patch
grep -c '^Signed-off-by:' *.patch # every patch needs one
head -20 0000-cover-letter.patch # did you actually write it?
grep '^base-commit:' *.patch
Versions and range-diff
When you send a v2, prove to reviewers that it contains exactly the changes you claim:
git range-diff <base>..<v1-tip> <base>..<v2-tip>
Paste the interesting parts under the --- if the series is large. Reviewers who already read v1
will thank you, and it catches the case where a rebase silently swallowed one of your fixes.
Reading Exercise
Learn the local conventions from the tree rather than from this page.
D=<your subsystem directory>
# 1. Subject-line conventions: prefixes, capitalization, verbs.
git log --oneline -30 -- $D
# 2. What a full, well-received message looks like here:
git log -5 --format='%n===== %h%n%s%n%n%b' -- $D
# 3. How often are Fixes: and Cc: stable used?
git log --since="1 year ago" --format='%b' -- $D | grep -c '^Fixes:'
git log --since="1 year ago" --format='%b' -- $D | grep -c 'stable@vger'
# 4. What does a SERIES look like on the list?
# Find one on lore.kernel.org and read the cover letter, then the
# replies, then the v2. Do this for three series before Lab 8.
# 5. Find a patch that was asked to be split, and read why.
# Search lore for: "please split" OR "should be a separate patch"
Common Mistakes and Their Symptoms
| Mistake | Symptom | Fix |
|---|---|---|
| A cleanup mixed into a fix | "Please split this" | Two patches, fix first |
| "This patch fixes…" in the body | Sent back for rewording (tip especially) | Imperative mood, describe the problem |
| A message that describes the diff | "Why?" | Describe the problem, not the change |
No Fixes: on a bug fix | Never reaches stable; users keep the bug | git log -1 --pretty=fixes <sha> |
A fabricated Reviewed-by: | Serious breach of trust | Only carry forward what was actually given |
Carrying Reviewed-by: across a rework | The reviewer's name on code they never saw | Drop it and say why in the changelog |
| Commit 3 of 5 does not build | Series refused, or a broken bisect forever | git rebase --exec 'make' |
| A patch that depends on a later one | Same | Reorder |
| An empty cover letter | Reviewers do not know what the series is for | Problem / approach / not-done / testing |
No base-commit: | "What does this apply to?" | --base=auto |
| Changelog inside the commit message | "Changes in v2" is now permanent history | Put it under --- |
| Wrapping pasted oops output | Nobody can grep it | Do not wrap logs |
| Subject over ~70 chars | Truncated in every listing | Shorten |
Validation / Self-check
- Give the two questions that decide whether something is one patch or two.
- Why is mixing a cleanup with a fix specifically harmful — name the two distinct costs.
- What three things does a commit message body need to contain, in order?
- Why imperative mood? What does
git log --onelinelook like when everyone follows it? - What is the test for whether a commit message is good enough?
- Where do you get the subject-line prefix for a file you have never patched?
- What does
Signed-off-by:certify, and where is its exact text? - Which trailers may you add, and which may only a reviewer add?
- What is
Fixes:for, who consumes it, and what is the exact format? - Give the four conditions a patch must meet for
Cc: stable, and say why you should not put the stable list in yoursend-emailrecipients. - What happens to text between
---and the diff? Name two things that belong there. - State the bisectability rule and the command that proves it.
- In what order do patches go in a series, and what is the one arrangement that is always wrong?
- What four things does a cover letter have to answer?
- What is
git range-difffor, and what mistake does it catch?
Next: Email Workflow — how to physically send this without corrupting it, which is a real and permanent problem.