commit: 472a2c5cacd94cca670e95a07231395290c6a606
parent d671179e9780dc434de49d0ccfaa06ae53bfeffa
Author: Drew DeVault <sir@cmpwn.com>
Date: Fri, 1 Apr 2022 11:14:06 +0200
git snail-mail
Diffstat:
1 file changed, 160 insertions(+), 0 deletions(-)
diff --git a/content/blog/git-snail-mail.md b/content/blog/git-snail-mail.md
@@ -0,0 +1,160 @@
+---
+title: Announcing git snail-mail
+date: 2022-04-01
+---
+
+> You've heard of git-over-email thanks to [git send-email][0] — 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`.
+
+[0]: https://git-send-email.io
+
+![A picture of a patch printed out on paper](https://l.sr.ht/w9hP.jpg)
+
+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. I did actually write a lot of the code for
+this idea! 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:
+
+```tex
+\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][1]. It pipes it into lp(1) to send it to your printer
+and ta-da!
+
+[1]: https://l.sr.ht/2VXT.pdf
+
+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:
+
+```hare
+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:
+
+- [shit](https://git.sr.ht/~sircmpwn/shit): a git implementation in POSIX shell
+- [bfbot](https://git.sr.ht/~sircmpwn/bfbot): a working IRC bot written in brainfuck
+- [classic6](https://git.sr.ht/~sircmpwn/classic6): a working Minecraft server written in 6 hours
+- [evilpass](https://git.sr.ht/~sircmpwn/evilpass): a password strength checker that detects password reuse
+- [tw](https://git.sr.ht/~sircmpwn/tw): a Wayland compositor for your terminal in 80 lines of "code"
+
+Take care!