publish: make the rewrite deterministic, so publishing fast-forwards

The script ended with `git rm -r accessibility/publish` plus a commit, which
was created fresh with the current time on every run. That changed the tip SHA
even when nothing else had, making the old tip a sibling rather than an
ancestor of the new one -- so every publish was a force-push. It also was not
achieving its stated purpose: `git rm` only removes a file from the tip, so
publish.sh was still present in every older commit of the published repo.

Fold the exclusion into the filter instead, as one filename callback covering
keep, rename and drop. The machinery now leaves the whole history, and the
rewrite is deterministic -- verified by publishing twice into separate
directories and diffing git rev-list --all: all 24 SHAs identical.

Publishing is now `git push`, and a rejected push means something real: that
the dev history itself was rewritten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Accessibility Mod
2026-08-03 02:02:08 +02:00
co-authored by Claude Opus 5
parent 93ef26349a
commit 1f48dea550
+40 -9
View File
@@ -197,17 +197,21 @@ all 1838 files of a commercial title. Pushing it anywhere would distribute the
game.
Publishing therefore means: rebuild a separate repository that contains only the
mod, and force-push *that*.
mod, and push *that*.
```
sh accessibility/publish/publish.sh
```
This clones the dev tree to `../alchemist-accessibility-publish`, runs
`git-filter-repo` to keep only `accessibility/` and `CLAUDE.md`, lifts
This clones the dev tree to `../alchemist-accessibility-publish` and runs
`git-filter-repo` over the clone with a single filename callback that does three
things at once: keeps only `accessibility/` and `CLAUDE.md`, lifts
`accessibility/publish/README.md` and `install.patch` up to the root (so a visitor
to the mod repo finds them where they expect), and drops the publish machinery
itself.
itself from the **whole history**.
All three being one filter is what makes the rewrite deterministic, and that is
worth more than it sounds — see [Why this is not a force-push](#why-this-is-not-a-force-push).
Then **the check that matters**: it lists every path in the rewritten history and
aborts if anything that is not the mod or its docs survived. A clean run says:
@@ -224,16 +228,43 @@ The script prints the push commands and stops rather than pushing for you:
```
cd ../alchemist-accessibility-publish
git remote add origin https://code.iamtalon.me/talon/alchemist-accessibility.git
git push --force -u origin master
git push -u origin master
```
`--force` is not a mistake and not a recovery step. `git-filter-repo` rewrites the
history from scratch every time, so the published commits are new objects on every
publish. The mod repo is derived output; force-pushing it is the normal path.
⚠ Paste those as **separate lines**. If a shell wraps the `remote add` line, the
URL ends up on its own and gets run as a command.
### Why this is not a force-push
`git-filter-repo` is deterministic: given the same input commits and the same
filter, it produces byte-identical output commits, because it preserves author
and committer timestamps and a commit's SHA is a function of its tree and its
parents. Verified by running `publish.sh` twice into different directories and
diffing `git rev-list --all` — all 24 SHAs identical.
So **a publish that only adds commits to the dev tree is a plain fast-forward**,
and pushes only the new objects. Git is content-addressed on top of that: the
bulk of the repo is the four wall-tone WAVs (~740 KB) and the Tolk DLLs
(~135 KB), and those blobs are keyed by content, so they are never re-uploaded
even when a rewrite does happen.
It used to be a force-push every time, for a reason worth not reintroducing. The
script ended with a `git rm -r accessibility/publish` and a `git commit`, and
that commit was created fresh with the *current time* on every run — so the tip
SHA changed even when nothing else had, and the old tip was a sibling of the new
one rather than an ancestor. It was also not achieving much: `git rm` only
removes a file from the tip, so `publish.sh` was still present in every older
commit of the published repo. Filtering it out instead fixed both.
**If the push is rejected as non-fast-forward, that now means something**: the
dev history itself was rewritten — an `amend`, a `rebase`, or a rebase onto a new
game version. `--force` is correct there, and the mod repo is derived output so
nothing is lost. But it should be a deliberate act, not the daily command. A
force-push you always run is a force-push you stop reading.
⚠ The corollary, unchanged: any commit made **directly in the published repo** is
destroyed by the next publish. Always work in the development tree.
---
## Rebasing onto a new game version