Sending and Applying Git Patches via Email – No GitHub Needed
Git today is the most widespread and popular version control system. Probably 99% of all current projects use Git, from the Linux Kernel to simple JavaScript libraries consisting of just one file and one function.
The Linux Kernel is a huge and very complex project. It involves a large number of programmers worldwide. Coordinating changes in this project would be simply impossible without an effective solution that allows this entire community to work independently of one another.
Now, this seems like a simple and obvious solution. However, the path to it was long and thorny.
A Brief Retrospective
1998 was an important year for Linux. Large vendors took notice of the project, and more and more developers joined. At that time, the project followed a fairly simple model for changes: developers would send their patches to Linus Torvalds, who decided whether to include the code or not. Torvalds liked this model because it gave him control over all changes.
The patch mechanism was used back when code trees were small and computers were very large. A patch literally was a set of instructions on punch cards telling what and how to replace in a stack of these media to get a new program version. Punch tapes were literally cut into pieces and glued together in a specific way to introduce changes to the program code of that time.
In general terms, a set of patches is a set of instructions that allow editing (semi- or fully automatically) the source program to get a new version. A patch set is always smaller than the full code version. This turned patches into a convenient interface for transferring changes and collaborative programming.
Problems arose when the developer community began to grow. Linus Torvalds became a "bottleneck"; the number of patches grew, and the time to review them increased. Developers began using the CVS version control system to ease collaboration. Of course, this went against Torvalds' original policy on Linux kernel changes. He disliked the existence of parallel project branches with their own workflow. On the other hand, developers felt frustrated sending patches to Torvalds, who physically could not review, accept, request fixes, or reject them in a timely manner. Developers complained they had to send multiple emails to get the "benevolent dictator's" attention.
The Emergence of Git
The solution was to use a decentralized proprietary version control system called BitKeeper. The project used this software for a long time, but eventually, relations between the company developing BitKeeper and the Linux kernel developers soured.
There was an amusing paradox: Linux Kernel is an open and free product licensed under the GNU General Public License (GPL). The main GPL principle is that anyone can freely use, distribute, and modify software released under this license, but all modifications must also be released under GPL. BitKeeper, however, was a fully closed proprietary commercial product owned entirely by its company.
Thus, the open and free project used a closed, non-free technology for coordinating development and versioning. Sooner or later, this fragile balance was going to break — and it did.
This made using BitKeeper impossible. Torvalds rejected using Subversion and proposed Monotone instead. However, Monotone was unbearably slow. Eventually, Torvalds began writing his own version control system from scratch in C. Thus, Git was born.
The new VCS was far from perfect but was positively received by the developer community and quickly gained the necessary tools. The new version control system rapidly gained popularity, and GitHub turned Git into the dominant solution for source code management in both open and commercial projects.
Dominant... Indeed, any project, whether small or large (with thousands of contributors), is likely to be registered and hosted on GitHub. Even projects that don't use Git internally (like FreeBSD or OpenBSD) have read-only copies on GitHub.
GitHub or Not GitHub?
New developers (and not only them) tend to believe that without GitHub, project development and management are impossible. So, when you join a project as a developer (freelancer or FOSS contributor), you’ll be added to the team on this platform. Even if there are only two, three, or four of you... Even if the project consists of just a few dozen source files. GitHub everywhere.
Is this good? It’s hard to answer simply yes or no. Certainly, GitHub has many useful tools; it’s convenient, fast, and reliable. Developers feel comfortable there, like in well-worn jeans. However, one should not forget that it’s a paid service managed by the well-known corporation Microsoft. Like any commercial product, GitHub is primarily focused on profit. If, for some reason, your project starts to interfere with that (damaging the platform’s image, etc.), your access will be instantly cut off. Recall the disputes GitHub had with the YouTube Downloader team, whose repositories were blocked, closed, and deleted simply because the RIAA demanded that GitHub restrict access to allegedly copyright-infringing software. This caused some (not a small number) teams to leave GitHub and switch to alternatives like GitLab or Gitea.
In summary, setting aside moral and legal aspects, we see a contradiction: Git was designed as a decentralized version control system (unlike Subversion, for example), yet GitHub, which uses Git, enforces centralized management. Moreover, the developer effectively owns nothing; everything belongs to the "managing company."
Is there life outside comfort? Can you use this great VCS without a third-party service? Can you accept patches without GitHub and send them to your team for review?
Despite GitHub’s strong influence, Git’s architecture remains almost unchanged — it’s still a decentralized version control system. Git imposes absolutely no requirements on the exchange environment. You can use ordinary files (transfer them any way you want, even by copying to external media), upload patches to an FTP server, use SSH, or even Git’s built-in exchange protocol. This is very convenient. Recall the start of this article: Linus Torvalds accepted patches without GitHub (which didn’t exist then) by email and posted results on FTP servers.
Sending Patches by Email
Now, let's get to the main topic. Suppose we are a small, brave team that wants to be independent from anyone or anything. We have some money to buy a domain, VPS, and corporate email to exchange information and, of course, send and receive patches by email.
Let's list tasks to build the necessary infrastructure for our project:
Buy a domain.
Buy corporate email and link it to our domain.
Create mailboxes.
Is it mandatory to buy a domain and corporate email? Not at all! You can use free mailboxes without a domain or purchase a domain later when needed. Everything depends on project requirements. However, from the early stages, the project may need a website, messaging (email), file exchange, and deployment infrastructure. You can buy these separately or combine them under one account for your project.
Suppose we are developing a web app and need infrastructure. After buying a domain and setting up DNS, we register as many mailboxes as needed.
After creating mailboxes, we must configure access to them in mail clients and Git.
Setting Up Git to Send and Receive Patches via Email
It all starts with installing a special Git extension package called git-email.
This is done using the package manager of your operating system or its distribution. For example:
Fedora:
sudo dnf install git-email
Ubuntu / Debian:
sudo apt-get install git-email
On Windows, git-email is included in the standard Git installation package.
Next step — configuration.
In your OS terminal, run:
git config --global --edit
This will open your favorite terminal (or other) text editor, where you need to add the following lines to your Git configuration (the example uses test credentials; you should use your own!):
[user]
name = Maria Ortega
email = zerozero@hostman-example.com
[sendemail]
smtpserver = smtp.hostman.com
smtpuser = zerozero@hostman.site
smtpencryption = ssl
smtpserverport = 465
The parameter smtpencryption can be set to either ssl or tls. The second mode uses STARTTLS to initiate communication over an encrypted channel, while the first mode encrypts the connection immediately after it is established. The choice of mode and port depends on your email provider’s requirements.
The [user] section is mandatory. Here, you identify yourself, and this information will appear in all patches and commits made by you. For stricter identification of patches and commits, Git supports signing sent information with GPG keys — but that’s another story.
Now that we’ve set up Git to send patches via email let’s try it out.
First, we need to clone a copy of the current working repository version. There are various ways to do this, which we’ll discuss at the end of the article.
After cloning, make some changes to your project.
Create a file named log_stderr.go:
package main
import (
"fmt"
"time"
"os"
)
func logStderr(message string, args ...interface{}) {
x := time.Now()
fmt.Fprint(os.Stderr, x.Format(time.RFC822))
fmt.Fprint(os.Stderr, " - ")
fmt.Fprintf(os.Stderr, message, args...)
}
Stage and commit the changes:
git add log_stderr.go
git commit -m "log into stderr func"
Now send your patch to the project lead for review:
git send-email --to="project-boss@hostman-example.com" HEAD^
The --to argument can accept multiple addresses separated by commas. This way, you can send your patch to all project members. You can also use --cc (carbon copy) to send the patch to additional email addresses separated by commas. This is useful when you want to send patches for review to the entire team or specific interested parties.
To avoid specifying recipients every time on the command line, you can add them to your Git config:
git config sendemail.to "project-boss@hostman-example.com"
git config sendemail.cc "user1@email.tld","user2@email.tld",…,"userN@email.tld"
After that, just run:
git send-email HEAD^
…And your patch will be sent to the configured addresses.
In this example, we sent the current changes from our working copy (HEAD^). You can send any changes, for example, two commits before the current one, or by commit hash. More details are in the Git documentation.
Git will generate the patch and try to send it via the SMTP server specified in the config. If the SMTP server requires authentication, you’ll need to enter your password. If you send many patches, this can be tedious. You can save the password in the config, but note it will be stored unencrypted:
git config --global sendemail.smtpPass 'your password'
A better option might be to configure Git to cache your password for some time:
git config --global credential.helper 'cache --timeout 3600'
More advanced solutions can use password managers and the git-credential extension, but we won’t cover that here.
Receiving and Integrating Patches
Your team members receive your patch as a plain text email message, and they can review it — and, imagine that, reject your changes with requests to “fix” or “rewrite.” This is natural and the core of collaborative software development. The freedom and manual patch management are what attract developers to create their own information exchange solutions.
What if You Are Asked to Fix Your Patch?
Suppose developers ask to reduce calls to the Fprintf function and add a logging severity level.
The updated code will look like this:
package main
import (
"fmt"
"time"
"os"
)
type LogSeverity string
const (
ERR LogSeverity = "ERROR"
WARN LogSeverity = "WARN"
INFO LogSeverity = "INFO"
DEBUG LogSeverity = "DEBUG"
)
func LogStderr(message string, severity LogSeverity, args ...interface{}) {
x := time.Now()
fmt.Fprintf(os.Stderr, "%s - %s - ", x.Format(time.RFC822), severity)
fmt.Fprintf(os.Stderr, message, args...)
fmt.Fprint(os.Stderr, "\n")
}
Since we’re fixing our previous patch and haven’t released any newer patches, we can simply amend the current commit:
git commit -a --amend
Now send the patch again, remembering we already configured the recipients:
git send-email --annotate -v2 HEAD^
The -v2 flag means this is the second version of the patch. If you need another fix, use -v3, and so on.
The --annotate flag allows you to add comments to your email message. Git will open a text editor showing something like:
Subject: [PATCH v2] Logging function to stderr
---
Added log level, reduced fmt.Fprintf calls
Add your notes, save, and close the editor; the patch will then be sent again to the recipients.
Always add annotations to your patches — it makes life easier for both you and your colleagues. Typing --annotate every time can get tedious, so you can automate it:
git config --global sendemail.annotate yes
How to Receive and Apply Patches?
Receiving patches is a bit trickier. Git sends specially formatted patches in plain text email messages. There can be many such patches, and Git does not restrict the transport method (email, FTP, etc.), so it doesn’t handle how to receive patches — that’s up to the developer.
Just use your mail client’s capabilities. After receiving approved annotated patches, save one or more email messages containing patches in an mbox file (Unix mailbox format). This format stores one or more email messages in a single file.
Then run:
git am <path_to_patches.mbox>
All patches will be incorporated into your working copy. You can continue working and impressing your team.
Email-based Git workflows can be as simple or sophisticated as you want.
The main thing is that it suits the team and does not create unnecessary inconvenience.
It seems there is nothing simpler, neater, or more elegant than working with Git over email.
However, there is one major problem: distributing the working copy to new developers joining the project.
If the project is large and has a rich history, the repository size might be many megabytes or even gigabytes. Sending that over email is impossible — it’s simply not designed for that.
How to Provide a Newcomer with the Entire Project History?
Git has an interesting feature called a bundle. It’s a snapshot of the working copy or the entire repository in a binary format of Git changes. Bundles are much more compact than a set of text patches; history and data inside the bundle are compressed, and the format allows transmitting both text and binary data.
Project leads or other responsible persons can upload the current project bundle to a file-sharing service — for example, an FTP server or an S3-compatible object storage like Hostman.
The newcomer downloads the project bundle and clones it:
git clone project.bundle <new_place>
Now <new_place> contains a new working copy ready to work with email patches.
However, to be honest, bundles are somewhat of an alternative to the patch email exchange workflow described above.
Collaborative work using bundles is a different story.
07 July 2025 · 12 min to read