git-snail-mail.md (5576B)
- ---
- 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. 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:
- ```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!