I am mostly writing this as a self memo for how to use git-send-email, and by extension git-format-patch and git-am, to share patches, patchsets, and apply them.

Me and my friends like to do these overly elaborate things to share code, what we jokingly call "fossbro roleplaying", so hopefully it can help them, and you as well!

"Wait what's all that about?"

These commands let you create patches that represent a commit (with all of its metadata, save for cryptographic signature), share them automatically over email, and, once someone downloads the contents of your email, apply those commits to a tree.

Preliminary Settings

TL;DR: These are the settings you want to tweak. Mine would be:

git config --global sendemail.smtpencryption tls
git config --global sendemail.smtpserver smtp.vulpinecitrus.info
git config --global sendemail.smtpuser <user>
git config --global sendemail.smtpserverport 587
git config --global sendemail.from <my email>

These settings replace CLI parameters you can otherwise provide when sending email with git send-email.

Exporting with format-patch

The git-format-patch commands turns a commit or set of commits into files that can be straightforwardly sent as email.

The only real positional argument provides the range or set of commits to format. That can be quite hard to wrap your head around but here's a quick rundown:

ArgumentMeaning
<ref>All commits from <ref> to HEAD
A..BAll commits in the range from A (excluded) to B (included)
-nLast n commits from HEAD

Note that because git is the way it is, and because, when you think about it, it makes sense: when your range includes a divergence, you might pull all of the divergent commit from it. It's just how git works. In the end, you will get all the commits you need, but they will be linearized, so the application order might be a bit wonky or cause merge errors.

Everything else is just optional arguments. Here are those I think would be the most useful:

  • --rfc: your patch or patchset is a request for comments
  • -v <n>: mark this as version <n> of your patchset
  • --cover-letter: generate a file that represents the cover letter, or the big description you send to explain the whole point of your patchset; by default it contains diff stats, and such
  • --thread=<style>: the thread style controls how your commits follow each other. A shallow style means all subsequent email are a reply to the first one (typically the cover letter). That is the standard on LKLMs.
  • --in-reply-to=<message-id>: oddly enough, I have already had to send a patch as a reply to a human-made series of email. This may be useful for that purpose.

Sending with send-email

This is the big complex part. Technically, git-send-email is a big Perl script that wraps around calls to your mail sender. As such, you may find some missing dependencies such as perl-authen-sasl on Arch Linux.

The send-email command takes your files and sends them as email. Easy. Except it does a lot of things for you and is very fuzzy in terms of available options. For example, it takes all emails from the Signed-off-by: and such lines in your commits, and adds them as Cc: lines in the email sent unless you tell it not to.

Here are the options I generally use:

  • --to=<email>: a new To: line
  • --cc=: a new Cc: line
  • --bcc=: a new Bcc: line
  • --compose: if you did not generate and edit a cover letter, write it now
  • --no-signed-off-by-cc: this removes the automated gathering of emails in the Signed-off-by: lines to Cc:
  • --suppress-cc=<choice>: suppress automatically found Cc:, you can see the man page for all choices
  • --dry-run: do everything but send the actual email; the information sent to the SMTP server is shown in the standard output for debugging purposes

There is actually an interesting overlap between send-email and format-patch. You can actually configure most of everything you want for the content of your email messages via either command, depending on your choice. For example, had I not chosen threading style in format-patch, I could have specified it in send-email using --[no-]chain-reply-to.

Applying with am

If you find yourself the (un?)fortunate recipient of a patchset that you fancy adding to your project, you can simply download your email to files (or, honestly, just copy the visible content from your email browser), and run git am on them. Ideally, you want to apply them in order, and over the correct commit. The details for how to coordinate with your collaborators for that to happen is left at your own discretion.

Interestingly, you can use -S with am to sign the objects created. The signature from the original committer in their own repository cannot be maintained, because you create the actual binary git objects tied to that commit in the actual repository yourself, so you must sign and certify that the content you create that commit with is valid. You are the first person liable if something wrong is introduced (and others are on the line if there are Signed-off-by: lines).

As as interesting side note, many places will let you download .patch files that have the correct format to be an email and a git patch. For example:

  • GitHub lets you do so for individual commits by adding .patch at the end of the URL, and similarly with pull requests (except the patch contains all individual patches for the PR's commits)
  • GitLab does the exact same thing for both commits and merge requests
  • Gitea/Forgejo also lets you do the same on both commits and PRs

As a result, you will often find yourself using git am on things that are not purely email.

As an addendum, I can point you to another tutorial by Matheus Tavares.