git & bare it; F2; Organic Maps
I spent way more time outside this weekend than I have for most of the summer combined (save for a vacation with the grandkids). So, please forgive this late-ish and theme-less Bonus Drop.
git & bare it

There was a recent post on the intertubes regarding git bare repos, and since I’ve been threatening to talk about them for a while, I figured me starting a series on how I use them might be better than pointing to a 100% Claude-generated post (you can read the weekly ASN posts and daily Observatory posts on my ai.rud.is blog if you want to read prose composed without any human intervention since at least the core content for those is timely and relevant for defenders and website runners).
A bare repository is a git repo with no working tree: the object database, the refs, and a hooks/ directory sitting next to them. Nothing to git status, and nothing to accidentally edit over SSH. It exists to receive pushes, and the hooks directory is where some of the magic happens – a set of scripts git runs at specific points, each with a different job and a different ability to say “um…no”.
Bare repos are important, especially if you want to get off the gosh-awful GitHub. GitHub’s server runs various hooks on every push you make, but you just never see them. When you run your own bare repo, you are the forge.
Bare repos are ridiculously simple to make:
git init --bare /data/git/REPONAME.git # this is my storage scheme
That .git suffix is a convention, not a requirement, but it’s the same suffix GitHub and every other forge uses (every “checker” wants me to say “use” but it doesn’t sound right), and it’s a constant reminder that this directory isn’t a project you cd into and poke around in. What you get looks like the inside of a normal repo’s .git/, because, well, that’s precisely what it is:
$ ls /data/git/REPONAME.gitHEAD config description hooks info objects refs
There’s no index, no worktree, and absolutely no “checkout”. The repo can’t build or run anything in place – it only exists to receive pushes (usually over SSH) and hand off to the hooks.
The hooks directory in a fresh repo ships full of .sample files, none of which are active. The critial ones are the receive-side hooks, which the scripts git-receive-pack runs when a push lands. Here’s the full set, in the order they fire:
| Hook | When it runs | Input | What it can do |
|---|---|---|---|
| pre-receive | Once per push, before any ref is updated | old new ref lines on stdin | Reject the entire push |
| update | Once per ref being updated | refname, old, new as arguments | Reject individual refs |
| post-receive | Once per push, after refs are committed | old new ref lines on stdin | React, report, deploy; cannot reject |
| post-update | Once per push that updated refs, after commit | updated ref names as arguments | Legacy housekeeping |
| reference-transaction | Any ref transaction: prepared, committed, aborted | old new ref lines on stdin | Observe or veto (in prepared) every ref change |
git-receive-pack runs these in order: pre-receive first, then update once per ref, then the ref updates are committed as one transaction, then post-receive and post-update. Anything the hooks print goes back to the person pushing, prefixed remote:, over the same SSH connection. A hook that exits non-zero prints its last words and the push (or the ref, for update) is declined.
Two of these – pre-receive and update – are gates. The other three are observers, and you really see the difference when things go pear-shaped since a bad gate rejects pushes loudly, a bad observer fails silently.
To activate any hook, drop the .sample suffix and make it executable. Git ships a sample for pre-receive and update but never one for post-receive.
There are a couple things to know before you write any of them…
First, GIT_DIR is set for you. When git invokes a hook it exports GIT_DIR pointing at the repository the push landed in, and the hook runs from that directory. Refrain from listening to the urge to hardcode the repo location.
And, the shebang decides the interpreter. #!/usr/bin/env bash and the file must be executable. A hook that isn’t executable is silently skipped (this will likely bite regularly as it does me).
We won’t be covering all about hooks today, since there’s alot there, so we’ll end with a complete bare setup flow:
# initial setup on the servergit init --bare /data/git/REPONAME.git# there will be more to do next time
# and connecting to it from your source dirgit remote add origin user@ssh-host:/data/git/REPONAME.gitgit push origin main
We’ll cover safety valves, hooks, and more in upcoming Drops, but you now know how to start your weaning off of Microsoft’s terrible service (just make sure to keep offline and also offsite backups, something which I really doubt Microsoft does for GitHub-proper anymore).
F2

While some folks launch an agent to talk to one or more LLMs to then execute a structured directory renaming flow, proper Linux/BSD/UNIX folks rely on scripts or a funky new-ish batch renaming tool written by @ayoisaiah, @muzimuzhi, @fanyang89, and @d-Rickyy-b (it’s been “a week”, so please forgive the lazy handle vs names + links). F2 (GH) is a cross-platform Go binary that treats batch renaming as a real problem worth solving properly.
The CLI interface is flag-driven: -f for the find pattern, -r for the replacement, and the rest falls out from there. It even has a robust variable system: F2 can pull EXIF tags from images – shoot date, camera model, GPS coordinates – and weave them into the filename template. This is much more useful than it sounds when you’re trying to sort five-year backlog of grandkid photos into something coherent. The same story applies to audio files via ID3 tags; an -r "{id3.artist}-{id3.title}" type pattern turns a folder of whatever-my-music-app-named-these into something organized. It also ships support for exiftool tags if you want the full fat EXIF extraction experience.
Every rename operation runs a dry-run validation by default before touching anything on storage, and the conflict resolution step catches clobbering situations before they happen rather than after (which, speaking from experience, is super nice if you’ve ever destroyed a filename that didn’t have a backup o_o). When something does go pear-shaped, there’s a full undo log. It handles symlinks, recursive operations, and runs identically on Linux, macOS, and Windows – a single statically-linked binary, no deps, and installed via go install github.com/ayoisaiah/f2/cmd/f2@latest or through the usual package managers.
If you’ve ever needed to do anything more complex than some scripted mv commands, F2’s the tool to reach for.
Organic Maps

I’ve generally been as lazy as the next human when it comes to apps I use for getting around. However, we are entering “interesting times” where the regular use of anything on the pseudo-centralized internet could become…problamatic…if you, as a human, disagree with anything “the State” has a policy on. This weekend I finally got off my tail and set up Meshcore nodes with a repeater since I plan on using that for comms and notifications moving forward as much as possible.
We also discovered a new hiking spot this weekend and when I counted all the services that tracked where I was — either deliberately (e.g., Peloton), or in the background (Apple, Verizon, and any other app that snuck in GPS logging to the internet) — I decided I likely needed an alternative sooner than later (things are really going to get pretty bad in the U.S. very soon).
Organic Maps (GH) is a free Android & iOS offline maps app used (now) by more than 6M travelers, tourists, hikers, and cyclists. It uses crowd-sourced OpenStreetMap data and is developed by a solid community. No ads, no tracking, no data collection, nada.
I won’t bore you with repeating or paraphrasing the great documentation they have, I can just note that it does what it says on the tin, and will remain a mandatory app for all of my future mobile device installs.
FIN
Remember, you can follow and interact with the full text of The Daily Drop’s free posts on:
- 🐘 Mastodon via
@dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev - 🦋 Bluesky via
https://bsky.app/profile/dailydrop.hrbrmstr.dev.web.brid.gy
☮️
Leave a Reply