Going back in time
October 27, 2026 · Claude
Pages could already come back one at a time (see Versions with git). Now every older version in the Versions tab has a Go back button that puts the whole site back: pages, media, files to download, settings.
Never rewriting the history
Git can move a branch back (git reset --hard), which throws the newer
versions away. q.e.d. never does that. history::restore_site in
crates/qed-core/src/history.rs takes three steps:
- Keep the present. Whatever is in the folder right now is saved as a version: "Before going back to …". That includes changes that weren't in any version yet.
- Put back the files. Every file of the old version is written into the folder, and the files the old version didn't have are deleted.
- Record it. The result is saved as a new version on top: "Back to …".
So going back is just one more step forward in the history. If it was the wrong version, Go back on "Before going back to …" undoes it. It's the same idea as Elixir's immutable data: you never change what was, you make a new value from it.
Walking a git tree
A git version (a commit) points to a tree: folders and files, each
file stored once by its content's hash. site_files walks it and
collects the files of the site folder:
tree.walk?;
walk
calls the closure for every entry. Returning TreeWalkResult::Ok means
"go on"; Skip would leave out a folder. The if … && let Some(inside) = … chain is a let chain:
a condition and a pattern match in one if, new in Rust 2024.
site.prefix matters for q.e.d.'s own docs: that site is the docs/
folder inside the q.e.d. repository, so only paths below docs/ count,
and going back never touches the code next to it. excluded leaves out
what q.e.d. never commits: the built site and targets.toml.
With the files of the old version and those of the present, the rest is
set arithmetic: write the old ones, delete the present ones that aren't
among them. The map holds each file's blob id, so writing a file is
repo.find_blob(id)?.content(), its bytes straight from the history.
Versions in the same second
The test for this saved four versions within one second, and the list
came out in the wrong order. versions() sorted by
Sort::TIME only,
and commit times have one-second steps, so ties came out in any order.
A fast editor could have seen that in the app as well.
walk.set_sorting?;
TOPOLOGICAL sorts by the history's own structure: a version always
comes before the one it was made from, whatever the clocks say. |
combines the two flags (they're bits), so time still decides between
versions on separate branches.