Announcing git snail-mail April 1, 2022 on Drew DeVault's blog

You’ve heard of git-over-email thanks to git send-email — now you can enjoy git snail-mail: a new tool making it easier than ever to print out git commits on paper and mail them to your maintainers.

Running git snail-mail HEAD~2.. prepares the last two commits for post and sends them directly to the system’s default printer. Configuration options are available for changing printer settings, paper size, and options for faxing or printing envelopes automatically addressed to the maintainers based on address info stored in your git config. Be sure to help the maintainers review your work by including a return envelope and a stamp!

And for maintainers, code review has never been easier — just get out your red marker and write your feedback directly on the patch! When you’re ready to import the patch into your repository, just place it on your scanner and run git scan-mail.

A picture of a patch printed out on paper

At least, this is what I’d like to say, but I ended up cancelling the project before it was ready for April Fool’s. After my friend kline (a staffer at Libera Chat) came up with this idea, I actually did write a lot of the code! Git is mostly written in Perl, but I could not really rouse the enthusiasm for implementing this idea in Perl. I did the prototype in $secretlang instead, and got it mostly working, but decided not to try to do some sneaky half-private joke release while trying to maintain the secrecy of the language.

Essentially how it works is this: I have a TeX template for patches:

\documentclass{article}
\usepackage[
	a4paper,
	top=1cm,
	bottom=1cm,
	left=1cm,
	right=1cm,
]{geometry}
\usepackage{graphicx}
\usepackage{fancyvrb}
\pagenumbering{gobble}

\begin{document}

\section*{implement os::exec::peek\{,any\}}

From: Bor Grošelj Simić \textless{}bor.groseljsimic@telemach.net\textgreater{} \\
Date: Fri, 25 Feb 2022 01:46:13 +0100

\VerbatimInput{input.patch}

\newpage
Page 1 of 2 \\
\includegraphics[]{./output-1.png}

\newpage
Page 2 of 2 \\
\includegraphics[]{./output-2.png}

\end{document}

This is generated by my git snail-mail code and then run through pdflatex to produce a file like this. It pipes it into lp(1) to send it to your printer and ta-da!

I chose not to make the commit selection work like git send-email, because I think that’s one of the most confusing parts of git send-email. Instead I just use a standard revision selection, so to print a single commit, you just name it, and to print a range of commits you use “..”. Here’s a peek at how that works:

fn get_commits(
	data: *texdata,
	workdir: str,
	range: str,
) (void | exec::error | exec::exit_status | io::error | fs::error) = {
	const fmt = `--format=%H%x00%s%x00%aN%x00%aE%x00%aD`;

	const pipe = exec::pipe();
	const cmd = exec::cmd("git", "show", "-s", fmt, range)?;
	exec::addfile(&cmd, os::stdout_file, pipe.1);
	const proc = exec::start(&cmd)?;
	io::close(pipe.1);
	const pipe = pipe.0;
	defer io::close(pipe);

	static let buffer: [os::BUFSIZ]u8 = [0...];
	const pipe = &bufio::buffered(pipe, buffer[..], []);

	let path = path::init();

	for (true) {
		const line = match (bufio::scanline(pipe)?) {
		case let line: []u8 =>
			yield strings::fromutf8(line);
		case io::EOF =>
			break;
		};

		// XXX: This assumes git always does the thing
		const tok = strings::tokenize(line, "\0");
		let commit = commitdata {
			sha = strings::next_token(&tok) as str,
			subject = strings::next_token(&tok) as str,
			author = strings::next_token(&tok) as str,
			email = strings::next_token(&tok) as str,
			date = strings::next_token(&tok) as str,
			...
		};

		path::set(&path, workdir)!;
		path::add(&path, commit.sha)!;
		commit.diff = strings::dup(path::string(&path));
		append(data.commits, commit);

		const file = os::create(commit.diff, 0o644)?;
		defer io::close(file);
		const parent = strings::concat(commit.sha, "^");
		defer free(parent);

		const cmd = exec::cmd("git", "diff", parent, commit.sha)?;
		exec::addfile(&cmd, os::stdout_file, file);
		const proc = exec::start(&cmd)?;
		const status = exec::wait(&proc)?;
		exec::check(&status)?;
	};

	const status = exec::wait(&proc)?;
	exec::check(&status)?;
};

The --format argument provided to git at the start here allows me to change the format of git-show to use NUL delimited fields for easily picking out the data I want. Point of note: this is minimum-effort coding for a joke, so there’s a lot of missing error handling and other lazy design choices here.

Anyway, I would have liked to have rewritten this in Perl and pitched it to the git mailing list for inclusion upstream, but alas, after prototyping in $secretlang I could not bring myself to rewrite it in Perl, and the joke fell flat. Not every idea pans out, but they’re still worth trying, anyway. If you want to see some joke projects I’ve made that actually work, check these out:

Take care!

Articles from blogs I read Generated by openring

Status update, April 2024

Hi! The X.Org Foundation results are in, and I’m now officially part of the Board of Directors. I hope I can be of use to the community on more organizational issues! Speaking of which, I’ve spent quite a bit of time dealing with Code of Conduct matters latel…

via emersion April 16, 2024

M2dir: treating mails as files without going crazy

Sometime recently in the past I complained about Maildir. You can go read the post, but the executive summary is that I think Maildir uses an actively user-hostile directory structure and extremely convoluted filenames that do not convey any meaning at all. …

via blogfehler! April 15, 2024

Go Developer Survey 2024 H1 Results

What we learned from our 2024 H1 developer survey

via The Go Blog April 9, 2024