Why
Until 2026-08-05 every nightly committed the built packages (.deb / .msi / .zip /
.tar.gz) into dist/. They compress to ~0%, so each nightly added permanently unique
bytes to the history.
Measured on the full object graph:
|
size |
blobs |
dist/*.{deb,msi,zip,tar.gz} |
1.1 GiB — 93.4% |
925 |
| all other files in the whole history |
82 MiB |
43,492 |
274 commits touch them, spanning 2024-11-16 to 2026-08-05. The packed repo is 1.2 GiB, so
a clone is ~12x bigger than the project actually is. That is now a real cost: any workflow
using fetch-depth: 0 clones the whole thing (website.yml, dist-pool-gc.yml).
Going forward this is already fixed — the globs are in .gitignore and the packages
live in the ci-build-pool release-asset pool instead. This issue is only about the
binaries already recorded in history.
Why we can't just delete them all at once
Stripping a file from a commit changes that commit's SHA, which changes every descendant,
which moves the tags. That matters because vcpkg pins the SHA512 of GitHub's
auto-generated tag archive:
# microsoft/vcpkg ports/talib/portfile.cmake (note: the port is "talib", not "ta-lib")
vcpkg_from_github(REPO "TA-Lib/ta-lib" REF "v${VERSION}"
SHA512 c8b9daf922cc98119e96a5bdb54187669e2f26be…)
That hash is of https://github.com/TA-Lib/ta-lib/archive/v0.7.1.tar.gz, fetched live from
GitHub. And GitHub embeds the commit SHA in the tarball's pax header:
v0.7.1 archive contains: comment=2247d599bddf37ed37e3a709371517e46efc66f6
So the archive hash changes if the commit SHA changes — even when the file tree is
byte-for-byte identical. Move the v0.7.1 tag and every vcpkg install talib fails with
File does not have the expected hash, immediately, on all baselines, until a PR lands in
microsoft/vcpkg. That outage is measured in days-to-weeks of their review queue, not ours.
vcpkg's version database is append-only, so historical entries (0.6.4) can never be
repaired at all.
What each downstream packager pins, checked 2026-08-06:
| packager |
pins |
affected by a tag move? |
| vcpkg 0.7.1 |
GitHub tag archive |
yes — fixable by a PR |
| vcpkg 0.6.4 |
GitHub tag archive |
yes — not fixable, ever |
| vcpkg 0.4.0 |
SourceForge ta-lib-0.4.0-msvc.zip |
no |
| Homebrew |
release asset ta-lib-0.7.1-src.tar.gz |
no |
| conda-forge |
tracks ta-lib-python, different repo |
no |
Homebrew is immune precisely because it downloads an uploaded release asset rather than a
generated tag archive. That is the fix for step 2.
The plan
Step 1 — strip only the history after v0.7.1 ✅ DONE 2026-08-06
Rewrite the 569 commits on dev (568 on main) that are descendants of
v0.7.1 (2247d599bddf37ed37e3a709371517e46efc66f6, 2026-07-03). Everything reachable
from v0.7.1 keeps its exact SHA.
The direction is forced, not a preference: a commit's SHA covers its ancestry, so stripping
anything below v0.7.1 would move v0.7.1 itself — which is the outage we're avoiding.
Because v0.7.1's ancestry subsumes every older tag, this leaves all of them untouched:
v0.4.0 … v0.6.4, reference-pre-cutover (0d6b0e649, 2026-06-28) — all ancestors of
v0.7.1, no retagging, no downstream impact
- the two frozen-oracle worktrees keep working as-is, same SHAs:
ta-lib-064 @ 43f9d5042 (in v0.6.4) and ta-lib-ref @ 0d6b0e649
ci-build-pool @ d5593923f (2026-08-05) is the only tag that moves. It is the
internal asset-pool pre-release, it moves weekly anyway, and dist-pool-gc.yml reads
origin/dev + origin/main rather than the tag's tree, so retagging it changes nothing
functionally. Moving a tag does not delete the release, so no watcher notification.
Recovers 782 MiB. Repo goes 1.2 GiB → ~430 MiB.
Every commit, message, author and date is preserved — only the binary blobs leave.
Step 2 — at the next release, get vcpkg off the tag archive
Submit a microsoft/vcpkg PR switching ports/talib/portfile.cmake from vcpkg_from_github
to vcpkg_download_distfile against the uploaded release asset
(releases/download/vX.Y.Z/ta-lib-X.Y.Z-src.tar.gz) — the same thing Homebrew already does.
Worth doing on its own merits: it permanently removes GitHub's tag-archive instability from
the release path. GitHub has changed archive generation before and forced mass re-hashing
across vcpkg ports.
Step 3 — much later, strip the remaining 344 MiB
Once ~2 releases have shipped with a vcpkg port that does not pin a tag archive, rewrite
the old tags too and recover the last 344 MiB (repo → ~100 MiB).
By then the only people who could notice are those pinned to a vcpkg baseline old enough to
still resolve 0.6.4 — which by that point is years stale. Deliberately deferred so the blast
radius is as close to zero as we can make it.
Do not start step 3 before step 2 has been live for two releases. That wait is the whole
point of splitting this up.
Server-side gc: one request, at the end of step 3
A local rewrite does not shrink GitHub. Old objects stay reachable there, and clearing them
needs a request to GitHub Support asking them to run gc — that is their documented route,
not a workaround. Deliberately deferred to after step 3: step 3 produces a second batch of
unreachable objects, so one request at the end covers both. Nothing is lost by waiting —
clone size, which is what CI and contributors actually feel, is already fixed by step 1.
The payoff is capped, whenever it is filed. refs/pull/* refs are permanent and GitHub
does not delete them, so every commit that was ever a PR head stays reachable forever.
Measured 2026-08-06: 39 PR refs, and 11 of the 19 whose objects are still available locally
carry dist/ binaries in their trees. refs/pull/90/head is a worked example — the
docs/rust-crate-no-std branch was deleted during step 1 and the commit is already pruned
locally, yet GitHub still pins it.
So expect GitHub's stored size to land somewhere above the local figure even after step 3
plus a successful gc. Ten of those PR heads sit inside v0.7.1's ancestry, which step 3
strips from the branches but cannot strip from the PR refs. That is a known, accepted
ceiling — not a sign the rewrite failed.
Do not try to reduce the PR refs — it is not possible and not worth effort. They are
server-maintained and read-only: git push cannot delete them, closing a PR does not remove
one, and neither does deleting the source branch (PR #90 again). 29 of the 39 come from
contributor forks, which share an object store with upstream in GitHub's fork network, so
those commits survive through the fork regardless.
And it costs us nothing: git clone does not fetch refs/pull/* — only refs/heads/*
and refs/tags/*. Measured during step 1: 20 of the 39 PR heads are absent from a fresh
local clone. PR refs are invisible to contributors and to CI; they exist only in GitHub's
internal storage accounting. Worth one sentence in the Support ticket (Support can purge
them, unlike us) and no effort beyond that.
Progress
Why
Until 2026-08-05 every nightly committed the built packages (
.deb/.msi/.zip/.tar.gz) intodist/. They compress to ~0%, so each nightly added permanently uniquebytes to the history.
Measured on the full object graph:
dist/*.{deb,msi,zip,tar.gz}274 commits touch them, spanning 2024-11-16 to 2026-08-05. The packed repo is 1.2 GiB, so
a clone is ~12x bigger than the project actually is. That is now a real cost: any workflow
using
fetch-depth: 0clones the whole thing (website.yml,dist-pool-gc.yml).Going forward this is already fixed — the globs are in
.gitignoreand the packageslive in the
ci-build-poolrelease-asset pool instead. This issue is only about thebinaries already recorded in history.
Why we can't just delete them all at once
Stripping a file from a commit changes that commit's SHA, which changes every descendant,
which moves the tags. That matters because vcpkg pins the SHA512 of GitHub's
auto-generated tag archive:
That hash is of
https://github.com/TA-Lib/ta-lib/archive/v0.7.1.tar.gz, fetched live fromGitHub. And GitHub embeds the commit SHA in the tarball's pax header:
So the archive hash changes if the commit SHA changes — even when the file tree is
byte-for-byte identical. Move the
v0.7.1tag and everyvcpkg install talibfails withFile does not have the expected hash, immediately, on all baselines, until a PR lands inmicrosoft/vcpkg. That outage is measured in days-to-weeks of their review queue, not ours.
vcpkg's version database is append-only, so historical entries (0.6.4) can never be
repaired at all.
What each downstream packager pins, checked 2026-08-06:
ta-lib-0.4.0-msvc.zipta-lib-0.7.1-src.tar.gzta-lib-python, different repoHomebrew is immune precisely because it downloads an uploaded release asset rather than a
generated tag archive. That is the fix for step 2.
The plan
Step 1 — strip only the history after v0.7.1 ✅ DONE 2026-08-06
Rewrite the 569 commits on
dev(568 onmain) that are descendants ofv0.7.1(2247d599bddf37ed37e3a709371517e46efc66f6, 2026-07-03). Everything reachablefrom v0.7.1 keeps its exact SHA.
The direction is forced, not a preference: a commit's SHA covers its ancestry, so stripping
anything below v0.7.1 would move v0.7.1 itself — which is the outage we're avoiding.
Because v0.7.1's ancestry subsumes every older tag, this leaves all of them untouched:
v0.4.0…v0.6.4,reference-pre-cutover(0d6b0e649, 2026-06-28) — all ancestors ofv0.7.1, no retagging, no downstream impact
ta-lib-064@43f9d5042(in v0.6.4) andta-lib-ref@0d6b0e649ci-build-pool@d5593923f(2026-08-05) is the only tag that moves. It is theinternal asset-pool pre-release, it moves weekly anyway, and
dist-pool-gc.ymlreadsorigin/dev+origin/mainrather than the tag's tree, so retagging it changes nothingfunctionally. Moving a tag does not delete the release, so no watcher notification.
Recovers 782 MiB. Repo goes 1.2 GiB → ~430 MiB.
Every commit, message, author and date is preserved — only the binary blobs leave.
Step 2 — at the next release, get vcpkg off the tag archive
Submit a microsoft/vcpkg PR switching
ports/talib/portfile.cmakefromvcpkg_from_githubto
vcpkg_download_distfileagainst the uploaded release asset(
releases/download/vX.Y.Z/ta-lib-X.Y.Z-src.tar.gz) — the same thing Homebrew already does.Worth doing on its own merits: it permanently removes GitHub's tag-archive instability from
the release path. GitHub has changed archive generation before and forced mass re-hashing
across vcpkg ports.
Step 3 — much later, strip the remaining 344 MiB
Once ~2 releases have shipped with a vcpkg port that does not pin a tag archive, rewrite
the old tags too and recover the last 344 MiB (repo → ~100 MiB).
By then the only people who could notice are those pinned to a vcpkg baseline old enough to
still resolve 0.6.4 — which by that point is years stale. Deliberately deferred so the blast
radius is as close to zero as we can make it.
Do not start step 3 before step 2 has been live for two releases. That wait is the whole
point of splitting this up.
Server-side
gc: one request, at the end of step 3A local rewrite does not shrink GitHub. Old objects stay reachable there, and clearing them
needs a request to GitHub Support asking them to run
gc— that is their documented route,not a workaround. Deliberately deferred to after step 3: step 3 produces a second batch of
unreachable objects, so one request at the end covers both. Nothing is lost by waiting —
clone size, which is what CI and contributors actually feel, is already fixed by step 1.
The payoff is capped, whenever it is filed.
refs/pull/*refs are permanent and GitHubdoes not delete them, so every commit that was ever a PR head stays reachable forever.
Measured 2026-08-06: 39 PR refs, and 11 of the 19 whose objects are still available locally
carry
dist/binaries in their trees.refs/pull/90/headis a worked example — thedocs/rust-crate-no-stdbranch was deleted during step 1 and the commit is already prunedlocally, yet GitHub still pins it.
So expect GitHub's stored size to land somewhere above the local figure even after step 3
plus a successful
gc. Ten of those PR heads sit inside v0.7.1's ancestry, which step 3strips from the branches but cannot strip from the PR refs. That is a known, accepted
ceiling — not a sign the rewrite failed.
Do not try to reduce the PR refs — it is not possible and not worth effort. They are
server-maintained and read-only:
git pushcannot delete them, closing a PR does not removeone, and neither does deleting the source branch (PR #90 again). 29 of the 39 come from
contributor forks, which share an object store with upstream in GitHub's fork network, so
those commits survive through the fork regardless.
And it costs us nothing:
git clonedoes not fetchrefs/pull/*— onlyrefs/heads/*and
refs/tags/*. Measured during step 1: 20 of the 39 PR heads are absent from a freshlocal clone. PR refs are invisible to contributors and to CI; they exist only in GitHub's
internal storage accounting. Worth one sentence in the Support ticket (Support can purge
them, unlike us) and no effort beyond that.
Progress
v0.7.1..devandv0.7.1..main— done 2026-08-06,1.20 GiB → 423.79 MiB.
dev20560551b,main671b5089a.0.8.1 port update ([talib] update to 0.8.1 microsoft/vcpkg#53896).
talib0.8.1 installs fromta-lib-0.8.1-src.tar.gz, so no published version pins a tag archive from here on.