Dan's Musings

Git Email Workflow Made Easy: a Walkthrough

I have become interested in the git email workflow. It seems arcane, but also intriguing. I wondered if I could get it to be easy to use, as easy (or close to it) to use as git itself, or better, as easy to use as GitHub.

I wanted to find a workflow that I myself would not only be happy using, but a workflow that I wouldn't be embarrased to ask my friends to use with me as well. Even if my friends don't know what an "email client" is, or if they do, don't think it's "cool" anymore.

This means that I would have to see if I could use git with email as a workflow, both as a maintainer or a contributor, without learning mutt or similar. I wanted to make git email workflows easy to use for "mere mortals", devs who use (gasp!) the gmail web frontend to check their emails regularly.

It also means I would need a guide that worked on all three major OSes. This is so that no matter what company I end up working for, I can get my co-workers to use the git email workflow, regardless of what kind of laptops we are issued.

To that end, I have written this guide. It outlines how to submit, respond to, and accept patches easily. Importantly, it doesn't require the user to switch their primary email client (or even use a dedicated email client). The steps in this guide should work on Linux, Mac, or Windows (as long as Git Bash is used).

This walkthrough assumes familiarity with Git, and the command line in general.

(With regard to running all commands from Git Bash on Windows, I couldn't get away from this requirement, and it made things easier anyways. The current version of Himalaya only works on Windows under Git Bash, though hopefully this will soon be fixed. Also, git-send-email doesn't know "how" to work unless it's run from Git Bash.)

Pre-requisites

This section describes the tools we need to install so that it will be easy to use git email workflows. We need to set up three tools: Himalaya, himalaya-mbox, and git send-email.

Install Himalaya

Himalaya is a tool that allows the user to manage email from the command line. It is dead simple to set up and use, and has a similar feel to other command line tools like ls and git itself.

The idea is that we won't use Himalaya as our primary email client; rather, only as a way to access specific git-related emails from the command line. This will make it easy for us to observe mailing list etiquette, and also will make it easy for us to consume patches.

Once downloaded and put in a directory somewhere on the %PATH%, just run it:

himalaya

And walk through the set-up steps.

At the end of the wizard, the configuration file for Himalaya is set up.

Mine looks like this:

[skin]
backend = "imap"
email = "<REDACTED>"
default = true
display-name = "Daniel Jay Haskin"
sender = "smtp"
smtp-host = "smtp.migadu.com"
smtp-port = 465
smtp-ssl = true
smtp-login = "<REDACTED>"
smtp-passwd-cmd = "pass.sh"
sync = true
imap-host = "imap.migadu.com"
imap-port = 993
imap-ssl = true
imap-login = "<REDACTED>"
imap-passwd-cmd = 'pass.sh'

Since I use KeePassXC to store my passwords, I use git-credential-keepassxc to get my password for SMTP and IMAP. As a bonus, this makes my set-up cross-platform. I downloaded that tool, and set it up.

Then I wrote a bash script called pass.sh and put it on the PATH, with this content in it:

#!/bin/sh

printf 'url=%s\nusername=%s\n' 'MYURL' 'MYUSERNAME' | \
  git-credential-keepassxc get | \
  awk -F' *= *' '/^password=/ {print $2}'

Now, when I run himalaya, I get a print-out of what's in my inbox. Neat!

Install himalaya-mbox

We need to install this janky shell script I wrote somewhere on the PATH:

#!/bin/sh
for i in "${@}"
do
    printf '\n'
    printf 'From nobody Mon Sep 17 00:00:00 2001\n'
    himalaya read --raw --sanitize "${i}"
    printf '\n'
done

I call it himalaya-mbox. It takes a list of email IDs and prints them in order to stdout in "mbox" format. This format is what git am understands, so we'll need this script in order to apply our patches later.

Set up git send-email

Set up git send-email. Basically, this means putting this stuff in your .gitconfig file:

[user]
name = <Your Name>
email = <Your Email>
[sendemail]
smtpserver = <SMTP server URL>
smtpuser = <SMTP user (often your gmail address)>
smtpencryption = ssl
smtpserverport = 465

You may also need to install additional software packages other than just git on some OSes. More information can be found at git-send-email.io.

Walk-through

This walkthrough goes through creating a repository, sending a patch, and accepting a patch.

Creating the repository

Here are the commands I ran to create the repository in this walkthrough:

mkdir poetry
cd poetry
git init -b main
cat > poetry.txt <<POETRY
Mares eat oats
And does eat oats
And little Lambs eat Ivy
Wouldn't you
Like some more
POETRY
git add poetry.txt
git commit -m "Initial commit"

These commands set up a repository called poetry with a poem in the main file, poetry.txt. This file and repository will be the subject of our walkthrough.

As a Contributor: Making the Patches

In this walkthrough, we will edit the file poetry.txt in our sample repository and commit patches to it. Then, we will email the patches.

The first step is to make a new branch, very similar to making a pull request:

git checkout -b "<my-name>/fix-poetry"

Now we make commits, which will turn into "patches", or emails that contain changes we wish to make together with their description.

Each commit in git becomes a patch email on the mailing list where we will send our changes, so it's important we have nice commit messages. When doing this in real life, it's important, then, to use git rebase and git commit --amend generously in this step.

In our walkthrough, though, we will make three patches: a delete, a change, and an insert.

In the original poem, there wasn't anything that came close to Like some more in the lyrics, so let's remove that line.

Now the file poetry.txt looks like this:

Mares eat oats
And does eat oats
And little Lambs eat Ivy
Wouldn't you

And we commit our changes with the following commands:

git add poetry.txt
git commit

And we use this as our commit message, ensuring we follow good commit message practice:

fix: Remove extranneous line

The line "Like some more" is not part of the original poem. This patch
removes it.

Next, we observe that our poem uses inconsistent capitalization on the third line, and fix the file.

The file now looks like this after our fix:

Mares eat oats
And does eat oats
And little lambs eat ivy
Wouldn't you

And we commit:

git add poetry.txt
git commit

With the message:

fix: Correct capitalization of `ivy` and `lambs`

The third line previously had `Lambs` and `Ivy` instead of `lambs` and `ivy`,
which is inconsistent capitalization. This fixes that.

Finally, we observe that the original poem had a line that is currently missing. After adding it, the file looks like this:

Mares eat oats
And does eat oats
And little lambs eat ivy
A kid'll eat ivy too
Wouldn't you

We commit it:

git add poetry.txt
git commit

With the message:

fix: Add missing `kid` line

The original poem used to have the line `A kid'll eat ivy too`, and this
rendition previously did not have it. This patch puts it back in.

As a Contributor: Send the Patch

Now, we have three additional commits on our branch off of main:

$ git log --graph --all --oneline
* 5b7a9a3 (HEAD -> skin/fix-poetry) fix: add missing `kid` line
* 754a879 fix: Correct capitalization of `ivy` and `lambs`
* 2bda625 fix: Remove extranneous line
* 585c0ec (main) Initial commit

We ensure that the commits on our branch we see in the above output are the emails that we wish to send. We will be sending emails for each commit on our new branch, so we want to ensure they look right. This is the part where git rebase and git commit --amend should be done a lot if needed.

Once the commits look good, we sending the patches on our patch branch like this:

git send-email --to=<mailing-list-email> main

This sends all the commits on the current branch that are not on the main branch as patch emails. In the current walkthrough, we will just send the patches to ourselves. Just put <MY_EMAIL> in the command above for the walkthrough's sake.

As a Maintianer: Respond to the Patch

This blog post goes through both perspectives, the contributor and maintainer, so we will now (as the maintainer) need to review the patches that we submitted (as contributors).

All git emails are, by convention, done in plain text. This is by design and is the central reason why it is hard to contribute to git mailing lists.

To make this simple, simply use Himalaya.

First, the maintainer checks their inbox:

$ himalaya

ID │FLAGS │SUBJECT                         │FROM              │DATE
9  │✷     │[PATCH 3/3] fix: add missing `… │Daniel Jay Haskin │12/02/2023 21:22
8  │✷     │[PATCH 2/3] fix: Correct capit… │Daniel Jay Haskin │12/02/2023 21:22
7  │✷     │[PATCH 1/3] fix: Remove extran… │Daniel Jay Haskin │12/02/2023 21:22
6  │✷     │<*** REDACTED PERSONAL EMAIL **>│<** REDACTED **>  │11/02/2023 21:48

When we as the maintainers wants to read one of the emails, we simply run himalaya read <email-id> to read an email:

himalaya read 7

This command should yield the following output:

The line "Like some more" is not part of the original poem. This patch
removes it.
---
poetry.txt | 1 -
1 file changed, 1 deletion(-)

diff --git a/poetry.txt b/poetry.txt
index a5c6725..e59bb94 100644
--- a/poetry.txt
+++ b/poetry.txt
@@ -2,4 +2,3 @@ Mares eat oats
And does eat oats
And little Lambs eat Ivy
Wouldn't you
-Like some more
--
2.37.3.windows.1

Running himalaya reply 7 allows us to respond to the email, or forward, or whatever is needed. The idea is to use himalaya for git-related, plain-text emails because it's easy to mess up and use an HTML email using Thunderbird, Outlook, or the Gmail web frontend. Himalaya will fire up whatever program you have set in your $EDITOR environment variable, whether that's emacs, vim, VS Code, or whatever. Having the email in the same program that's used to edit code on a daily basis makes our job of writing/using plain text emails much easier.

After the maintainer and contributor go back and forth on the patch, the contributor will eventually submit a patch with send-email that is suitable for use.

Accept a Patch

In the walkthrough, we suppose that the patches are ready to use as-is, so we will apply them.

We will go to the repository and check out the branch main. Then, we will create a new branch called patches/<contributor-name>/<subject>. Like this:

git checkout main
git checkout -b patches/skin/correct-poem

Next, we need to identify what patches we want to apply. We will list the emails out using himalaya:

$ himalaya

ID │FLAGS │SUBJECT                         │FROM              │DATE
9  │      │[PATCH 3/3] fix: add missing `… │Daniel Jay Haskin │12/02/2023 21:22
8  │✷     │[PATCH 2/3] fix: Correct capit… │Daniel Jay Haskin │12/02/2023 21:22
7  │      │[PATCH 1/3] fix: Remove extran… │Daniel Jay Haskin │12/02/2023 21:22
6  │✷     │<*** REDACTED PERSONAL EMAIL **>│<** REDACTED **>  │11/02/2023 21:48

We note the ids of the patches we want to apply. In our example, they are 7, 8, and 9, in that order.

Then, we run the following command:

himalaya-mbox 7 8 9 | git am

This command runs the script himalaya-mbox that we created earlier.

In our example, the output should look like this:

$ himalaya-mbox 7 8 9 | git am
Applying: fix: Remove extranneous line
Applying: fix: Correct capitalization of `ivy` and `lambs`
Applying: fix: add missing `kid` line

That's it! The patches will be applied to the current branch. Now we can make changes, run tests, etc. play around with the patches given to us, and either respond to those who sent us the patches or add them into our main branch, like this:

git checkout main
git merge patches/skin/correct-poem

If we keep our code in a central repository, we should not forget to push the changes:

git push origin main

Conclusion

In this post, we walked through an example git repo and how we were easily able to send patches via email, and respond to them in kind. We learned both how to sumbit patches, and how to accept them, using easy-to-learn tools.

Even better, we didn't have to adopt (or learn how to adopt) a full email client to accept git emails; we can continue to use our current workflow while simply using command line tools for git-related emails.

Hopefully this will allow more people to use git's email features in their workflow and asking their friends to do the same without pulling their hair out.