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.