Patch-Based and Email Workflows: format-patch, am, apply, and Maintainer Flows: Guided Hands-On Workflow and Core Operations
Create a two-commit topic, export raw and mailbox artifacts, compare git apply with git am, parse one message with mailinfo, abort a failed am safely, and produce a v2 reroll with range-diff review metadata.
Learning objectives
- Export and preflight a raw diff without creating commits.
- Generate a threaded mailbox series with cover letter and signoff trailer.
- Use mailinfo to observe the split between message metadata and patch content.
- Apply mailbox patches with git am and compare authorship/commit boundaries with raw apply.
- Create and review a v2 series using reroll count and range-diff.
1. Create one source repository and two clean receiving clones
Everything stays inside git-patch-workflow-lab. No SMTP
server, hosted account, or valuable repository is involved.
Git Bash / Bash / zsh
mkdir git-patch-workflow-lab
cd git-patch-workflow-lab
git init -b trunk source
cd source
git config user.name "Contributor Example"
git config user.email "contributor@example.invalid"
printf "# Patch Lab\n\nStable baseline.\n" > README.md
printf "mode=baseline\n" > app.conf
git add README.md app.conf
git commit -m "base: create patch workflow lab"
BASE=$(git rev-parse HEAD)
git switch -c topic-v1
mkdir scripts
cat > scripts/show-mode.sh <<'EOF'
#!/bin/sh
mode=$(sed -n 's/^mode=//p' app.conf)
printf 'mode=%s\n' "$mode"
EOF
git add scripts/show-mode.sh
git commit -m "tooling: add mode reporter"
printf "\nUse scripts/show-mode.sh to inspect the configured mode.\n" >> README.md
git add README.md
git commit -m "docs: explain mode reporter"
V1=$(git rev-parse HEAD)
git branch series-v1 "$V1"
cd ..
git clone --branch trunk source raw-consumer
git clone --branch trunk source mailbox-consumer
PowerShell file-creation equivalent
New-Item -ItemType Directory git-patch-workflow-lab | Out-Null
Set-Location git-patch-workflow-lab
git init -b trunk source
Set-Location source
git config user.name "Contributor Example"
git config user.email "contributor@example.invalid"
"# Patch Lab`n`nStable baseline." | Set-Content README.md
"mode=baseline" | Set-Content app.conf
git add README.md app.conf
git commit -m "base: create patch workflow lab"
# Create topic-v1 with the same two logical commits using Set-Content/Add-Content.
2. Prove the series and receivers share the same base
cd source
git status --short --branch
git log --graph --decorate --oneline --all
git merge-base trunk topic-v1
git rev-parse trunk
git diff --check trunk..topic-v1
git diff --stat trunk..topic-v1
The merge base should equal trunk.
diff --check should report no whitespace errors. This
preflight is the evidence that both export forms describe a clean
two-commit topic on a known base.
3. Export one raw diff for the whole topic
git diff trunk..topic-v1 > ../topic-v1.diff
git apply --stat ../topic-v1.diff
The raw diff contains the net file changes from trunk to the topic tip. It does not retain the fact that the contributor intentionally split those changes into two commits.
4. Apply the raw diff and observe that no commit appears
cd ../raw-consumer
git config user.name "Raw Importer"
git config user.email "raw-importer@example.invalid"
BEFORE=$(git rev-parse HEAD)
git status --short --branch
git apply --check ../topic-v1.diff
git apply ../topic-v1.diff
git status --short
test "$(git rev-parse HEAD)" = "$BEFORE"
git diff --stat
Expected: files are modified/added in the working tree, but HEAD is exactly the same commit as before. That is the defining property of this path.
git add -A
git commit -m "import: apply contributor topic as one raw patch"
git log -1 --format=fuller
The receiver has created a new local commit with its own message/committer and has collapsed the contributor's two-commit review structure into one commit.
5. Export the same topic as an explicit v1 mailbox series
cd ../source
mkdir -p ../series-v1
git format-patch --reroll-count=1 --cover-letter --thread=shallow \
--signoff -o ../series-v1 trunk..series-v1
ls ../series-v1
Expected files: one
v1-0000-cover-letter.patch plus two numbered commit
patches. --signoff adds a signoff trailer to the
exported messages; it does not rewrite the commits in
source.
6. Inspect mailbox headers and the review/patch boundary
sed -n '1,45p' ../series-v1/v1-0001-tooling-add-mode-reporter.patch
grep -E '^(From:|Date:|Subject:|Message-ID:|In-Reply-To:|References:)' \
../series-v1/*.patch
PowerShell inspection alternative
Get-Content ../series-v1/v1-0001-tooling-add-mode-reporter.patch -TotalCount 45
Select-String -Path ../series-v1/*.patch -Pattern '^(From:|Date:|Subject:|Message-ID:|In-Reply-To:|References:)'
The mail headers organize authorship and threading. The commit message follows, then a three-dash separator and the patch/diffstat material.
7. Parse one message with mailinfo
git mailinfo ../mailinfo-message.txt ../mailinfo-patch.diff \
< ../series-v1/v1-0001-tooling-add-mode-reporter.patch
cat ../mailinfo-message.txt
git apply --stat ../mailinfo-patch.diff
Expected stdout: author name, email, and subject.
The two output files separate commit-message body from patch
content—the same conceptual split git am consumes.
8. Apply the mailbox series and preserve commit boundaries/authorship
cd ../mailbox-consumer
git config user.name "Maintainer Example"
git config user.email "maintainer@example.invalid"
BEFORE=$(git rev-parse HEAD)
git am ../series-v1/v1-0001-tooling-add-mode-reporter.patch \
../series-v1/v1-0002-docs-explain-mode-reporter.patch
git log -2 --format=fuller
git status --short --branch
test "$(git rev-list --count "$BEFORE"..HEAD)" -eq 2
Expected: two commits are created. Their author comes from the mailbox metadata; the current maintainer is the committer. The exported signoff appears in the recreated commit messages. OIDs need not match the source commits.
9. Compare the two import paths
cd ../raw-consumer
git log --oneline -2
cd ../mailbox-consumer
git log --oneline -3
git log -2 --format='%h author=%an <%ae> committer=%cn <%ce>%n%B%n---'
| Property | Raw git apply then local commit |
git am |
|---|---|---|
| Working-tree changes | Yes | Yes |
| Creates commits automatically | No | Yes |
| Preserves original series boundaries | No, unless receiver recreates them manually | Yes, one commit per mailbox patch |
| Uses mailbox author/message | No | Yes |
10. Engineer a failed git am and abort it safely
Create a third receiver from the base, then make an incompatible local edit before applying a one-commit patch:
cd ..
git clone --branch trunk source abort-consumer
cd abort-consumer
git config user.name "Abort Tester"
git config user.email "abort-tester@example.invalid"
printf "# Patch Lab\n\nReceiver changed this baseline independently.\n" > README.md
git add README.md
git commit -m "receiver: diverge documentation"
PRE_AM=$(git rev-parse HEAD)
git am --3way ../series-v1/v1-0002-docs-explain-mode-reporter.patch || true
git status --short --branch
git am --show-current-patch=diff
git rev-parse ORIG_HEAD
git am --abort
test "$(git rev-parse HEAD)" = "$PRE_AM"
git status --short --branch
The failed am session is stateful.
--show-current-patch identifies the stopped patch, and
--abort restores the original branch/pre-am file state.
That is safer than ad-hoc reset commands.
11. Build v2 as a separate review iteration
cd ../source
git switch -c topic-v2 trunk
mkdir -p scripts
cat > scripts/show-mode.sh <<'EOF'
#!/bin/sh
set -eu
mode=$(sed -n 's/^mode=//p' app.conf)
printf 'mode=%s\n' "$mode"
EOF
git add scripts/show-mode.sh
git commit -m "tooling: add robust mode reporter"
printf "\nRun scripts/show-mode.sh to inspect mode; the script exits on errors.\n" >> README.md
git add README.md
git commit -m "docs: explain robust mode reporter"
V2=$(git rev-parse HEAD)
v2 is deliberately created from the same base rather than rewriting the already-exported v1 branch. This keeps both review iterations easy to compare in the lab.
12. Compare v1 and v2 before exporting v2
git range-diff trunk..series-v1 trunk..topic-v2
Expect the two v1 patches to correspond to two v2 patches, with the script and documentation differences highlighted. Use this output for human review, not machine parsing.
13. Export v2 with reroll and embedded range-diff review material
mkdir -p ../series-v2
git format-patch --reroll-count=2 --cover-letter --thread=shallow \
--range-diff=series-v1 --signoff -o ../series-v2 trunk..topic-v2
sed -n '1,120p' ../series-v2/v2-0000-cover-letter.patch
The cover letter should identify v2 and include range-diff material comparing it with the v1 tip because both series share the same base.
14. Challenge — choose the artifact from the preservation requirement
- You need only file changes and will write a new local commit: raw diff plus which command?
- You must preserve two commit messages/authors and their order: which export/import pair?
- You want to prove a patch will apply before changing files: which option?
- You need to see the patch where an am session stopped: which command?
- You need to review how v2 differs from v1 as a series: which command?
15. Verification checklist
- Source v1 has exactly two commits beyond trunk.
- Raw apply leaves HEAD unchanged until a local commit is created.
- Mailbox import creates two commits and preserves mailbox author/message metadata.
- Generated v1 contains cover letter plus two numbered patches with threading headers.
-
mailinfoextracts message and patch material separately. - Failed am shows the current patch and abort returns to the pre-am tip.
- v2 shares the same base as v1 and range-diff shows review deltas.
- No network, SMTP account, hosted review object, or real credential was required.
16. Cleanup
cd ../..
pwd
rm -rf git-patch-workflow-lab
PowerShell equivalent:
Set-Location ../..; Remove-Item -Recurse -Force
git-patch-workflow-lab.
17. Knowledge check
Question 1. Why did HEAD stay unchanged after
git apply?
Question 2. Which identity becomes the committer for commits
made by git am?
Question 3. What does ORIG_HEAD record when an am session begins?
Question 4. Why did v1 and v2 not need matching commit IDs for range-diff?
Question 5. Did adding --signoff to format-patch cryptographically sign the source commits?
18. Summary
You exported the same topic as a raw diff and a mailbox series, proved the different state transitions, parsed mailbox metadata, handled an am failure by aborting, created an independent v2, and used range-diff plus reroll metadata to make review changes explicit.
Authoritative references
git-format-patch
git-apply
git-am
git-mailinfo
git-range-diff
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.