commits as sendable patch files instead of merge requests
Not every Git project runs through a central forge with a pull request interface. git format-patch turns a series of commits into sendable patch files that recipients can reassemble into full commits with git am, with no GitHub, GitLab or other web interface involved at all. This article shows how to generate patch series, attach a cover letter, version them, and send them via git send-email, and when this workflow genuinely beats pull requests.
Table of Contents
- 1. Core idea: commits as patch files instead of a pull request
- 2. git format-patch: the structure of the generated files
- 3. Generating a patch series with a cover letter
- 4. Sending patches by email with git send-email
- 5. Versioning patch series with v2, v3 and a reroll count
- 6. Comparing series with git range-diff
- 7. Recipient side: checking and applying patches
- 8. When the patch-based workflow beats pull requests
- 9. Practical pitfalls and a checklist for getting started
- 10. Summary
- 11. FAQ
1. Core idea: commits as patch files instead of a pull request
A pull request is essentially a reference to two branches compared on a central server. That central instance simply does not exist in many decentrally organized projects, or it is deliberately avoided, for instance because a project has been organized through a mailing list for decades and nobody wants to give up the established rhythm of a well practiced contribution and review culture.
git format-patch removes this dependency on a central instance entirely: every commit gets turned into a self-contained, text-based file that carries all the information needed to become an identical commit again on the recipient's side. That file can travel over practically any channel, an email, a USB stick, or a simple file drop.
2. git format-patch: the structure of the generated files
Without further options, git format-patch produces one file per commit in the given range, named after a running number and a short description derived from the commit subject, such as 0001-fix-cache-invalidation.patch. Each of these files contains the full email headers with author and date, the complete commit message, and the associated diff in unified diff format.
The range can be specified either as a single commit reference, exporting every commit from that point to HEAD, or as an explicit two-dot range between two commits. The -o option sets the target directory for the generated files, which is useful for keeping several series cleanly separated from one another.
# Export every commit since diverging from main as patch files
git format-patch main --output-directory patches/
# Export exactly the last three commits
git format-patch -3
3. Generating a patch series with a cover letter
For several related commits, a short introductory description of the whole series is helpful before the recipient reads each individual patch in detail. The --cover-letter option automatically generates an extra file numbered 0000, which serves as a placeholder for such a summary and should be filled in by hand before sending, with motivation, an overview, and relevant background information.
Combined with -N, or the numbering that automatically kicks in once a series has two or more patches, every file additionally shows which part of the overall series it represents, for instance [PATCH 2/5] in the subject line, giving recipients an immediate sense of scope and position within the series.
# Generate a patch series including a cover letter file
git format-patch main --cover-letter --output-directory patches/
4. Sending patches by email with git send-email
Instead of manually sending generated patch files as attachments, which many mail clients render unusable through automatic reformatting, git send-email connects directly to a configured SMTP server and sends every patch file as its own, plain text email with correctly set headers, so the diffs it contains arrive unchanged on the recipient's end.
Configuration happens once via git config sendemail.smtpserver and the associated credentials, after which git send-email patches/*.patch is enough to send an entire series, including the cover letter, in the correct order and with correctly chained In-Reply-To headers, as a single coherent thread.
# Configure the SMTP server once
git config sendemail.smtpserver smtp.mironsoft.internal
git config sendemail.smtpserverport 587
# Send the entire series as a coherent email thread
git send-email --to=team@mironsoft.de patches/*.patch
5. Versioning patch series with v2, v3 and a reroll count
If an already sent series gets reworked after feedback, it usually is not resent without comment, but clearly marked as a new version. The --subject-prefix option lets you replace the default PATCH prefix in the subject line with custom text, such as PATCH v2, so everyone involved can immediately see which revision they are looking at.
Even more convenient is the -v option, which takes a version number directly and automatically adjusts both the prefix and the file names accordingly, without anyone having to construct a custom prefix string by hand. That keeps every revision of a series clearly traceable to the actual version it represents.
# Generate the second revision of a patch series, prefix and filenames adjusted automatically
git format-patch main -v2 --cover-letter --output-directory patches/
6. Comparing series with git range-diff
Anyone reviewing a second version of a patch series usually wants to know what actually changed content-wise since the first version, rather than reading each patch again from scratch. git range-diff compares two commit ranges by content for exactly this purpose and marks which patches stayed identical, which changed, and which were newly added.
For reviewers of a new version, that is the fastest way to focus purely on the actual changes relative to the previous version, instead of re-reviewing the entire series from the ground up with every revision.
# Compare the first and second version of the same series by content
git range-diff main feature-v1 feature-v2
7. Recipient side: checking and applying patches
On the recipient's side, a single file or a whole directory can usually be turned into full commits with the original author and original date using just git am. Before actually applying anything, a quick check with git apply --check is often worthwhile, since it only verifies whether a patch would apply cleanly without making any change yet.
For larger series, it is common practice to first apply the patches in a separate, local test branch, run the automated test suite there, and only then decide whether to merge into the main branch, quite similar to a pull request merge, just without the accompanying web interface.
# Check whether the patch would even apply cleanly before applying it
git apply --check 0001-fix-cache-invalidation.patch
# Apply in a dedicated test branch to test before merging
git checkout -b test-patch-series main
git am patches/*.patch
8. When the patch-based workflow beats pull requests
The patch-based workflow pays off especially where no single, central forge connects everyone involved, for instance in projects with several independent mirror repositories, contributions over unstable or restricted internet connections, or when contributors cannot or will not create an account with a particular provider for legal or organizational reasons.
For a typical, centrally organized Magento or web project running on GitLab with a fixed team, the effort is rarely justified, since pull requests offer inline comments, automatic CI runs, and a lower barrier to entry for new team members, none of which a pure email workflow readily replicates.
9. Practical pitfalls and a checklist for getting started
The most common source of trouble is a mail client that automatically converts plain text into HTML, altering line breaks or indentation of the diff along the way, which leaves the patch unable to apply cleanly on the recipient's end. Anyone unable to use a plain-text mail client or git send-email should therefore send patches as plain text attachments wherever possible, not embedded as formatted message text.
Another frequent pitfall is unclear ordering in multi-part series: without consistent numbering and a cover letter, recipients often cannot immediately tell in which order the patches need to be applied. Consistently using --cover-letter together with automatic numbering reliably avoids this problem from the start.
| Aspect | Pull request | format-patch workflow | Recommendation |
|---|---|---|---|
| Requires a central instance | Yes, a forge like GitLab | No, any transport channel works | format-patch when no forge exists |
| Inline comments on the web | Yes, built in | No, replies only by email | Pull request for web-based review |
| Automatic CI runs | Usually built in | Only with extra infrastructure | Pull request when active CI is needed |
| Preparable fully offline | Limited | Yes, fully | format-patch for unstable connectivity |
| Versioning a series | Force push onto the same branch | Dedicated v2, v3 files and threads | format-patch for a traceable history |
Mironsoft
Git workflows, branching strategies, and CI hooks
Chaotic Git history and unclear branching rules across the team?
We set up clean Git workflows, clarify branching strategies for the team, and automate quality checks via Git hooks and CI pipelines so the history stays traceable.
Workflow Audit
Review the existing branching strategy and merge practice for weak spots.
Hook Automation
Set up pre-commit and pre-push hooks for linting, tests, and commit conventions.
Team Training
Teach rebase, cherry-pick, and conflict resolution hands-on across the team.
10. Summary
format-patch Workflow
Core principle
git format-patch turns commits into sendable text files that the recipient reassembles into commits with git am.
Series with context
--cover-letter produces an extra overview file explaining the motivation and scope of the whole series.
Sending
git send-email sends patches as correctly formatted plain text emails without destroying the diff's line breaks.
Comparing revisions
git range-diff shows by content what actually changed between two versions of a series.