Documentation
Git history retention
A repository that collects build artifacts, uploads, or generated data grows forever, and a large file committed once keeps costing disk long after it was deleted. Everlock can bound that: a repository set to keep ninety days of history has everything older dropped by a daily job, and the objects only those commits referenced are reclaimed.
Truncation removes history, not files. A commit stores a complete snapshot,
so every file present at a branch tip after a truncation was present before it.
What you give up is the ability to look back: git log stops at the cut, and
git blame attributes everything older to the commit that survived.
What a repository costs
Before changing anything, ask what the repository is actually holding:
/git repo size my-project
my-project: 1.9 GiB in 14203 loose object(s) reachable 1.9 GiB (14180), unreachable 2.1 MiB (23) — the daily git.gc job reclaims the unreachable ref reachable exclusive ──────────────────────────────────────── refs/heads/main 512.0 MiB 412.0 MiB refs/tags/v1 1.4 GiB 1.4 GiB largest object size kind path ─────────────────────────────────────────── 9f2a1c04be31 1.2 GiB blob data/dump.bin
Two numbers per ref, because objects are shared: reachable is everything that ref can reach, counted for every ref that reaches it; exclusive is what no other ref reaches — the bytes deleting that ref alone would make prunable. Sizes are on-disk bytes, so a byte reported here is a byte a prune gives back.
Add a window to see what a truncation would achieve:
/git repo size my-project retention=90d
retention 90d: drop 318 commit(s) older than 2026-05-21, keep 44 behind a shallow boundary, reclaim 512.0 MiB expires 2 pull-request ref(s): refs/pull/old-feature/main, refs/pull/spike/main 1.4 GiB stays pinned by refs/tags/v1 (tip predates the window)
The command reads the repository and writes nothing. Repository reader access
is enough to run it.
Setting a policy
To have it happen by itself:
/git repo set my-project retention=90d
my-project: keeping 90d of history the daily git.retention job drops the rest and reclaims what it held; commit ids are left alone, so existing clones keep working
A clone of a repository under a policy is a shallow clone: git log ends at
the cut and git fsck passes. Commit ids never change, so clones made before
a cut keep working. git clone --depth=<n> works as it does anywhere else,
and a repository under a policy has less to send in the first place.
The git.retention job truncates every repository that has a policy.
Repositories without one are untouched, so nothing changes until you opt one
in, and the job itself has nothing to configure — the window is its only input.
It runs once a day at a fixed time the scheduler derives from the job's name,
so different jobs land at different times rather than all at once. /jobs list
shows when it next runs; /jobs run git.retention runs it now:
/jobs list
kind schedule next run ───────────────────────────────────────────── git.retention daily 2026-08-20 03:00 system.self-update daily 2026-08-19 22:04
A run reports one line per repository it changed. A repository it could not
truncate — missing, an unparsable window, a ref that moved mid-run — is named
in the summary and fails the job, alongside the ones that succeeded, so the
failure is visible in /jobs list rather than passed over. /git repo list
shows each repository's window in its history column.
The window is the whole policy — there is nothing else to configure. Clear it
with /git repo unset my-project retention, which stops future truncation but
does not bring back history already dropped.
Nothing is backed up before a cut: a backup ref would keep the dropped objects
reachable, and reclaiming their space is the point. /git repo size is how a
cut is inspected before it is set — run it first.
Does a push undo it?
No. A client that cloned before a cut still holds the dropped history, but pushing new work does not send it back: git works out what to upload from the refs the server advertises, and the branch it is pushing to already points at the boundary commit, which the client has. Only the commits after it go over the wire. The history below the cut is never offered, and the repository stays bounded.
The one thing that client cannot do is push a ref that points below the boundary — an old feature branch, or a tag on a commit the server dropped:
git push origin 4f2a1c0:refs/heads/archive
! [remote rejected] 4f2a1c0 -> archive (missing objects)
git sends no objects for it — it assumes anything under an advertised ref is already there — so the server refuses the ref rather than record one pointing at nothing. Bounding history means giving up the ability to add branches reaching into the part that was dropped.
If the objects do arrive by some other route, the boundary retires itself: git
commits are content-addressed, so restoring them gives back exactly the ids the
boundary was missing, and the next push re-derives the shallow file and finds
nothing shallow.
What happens to clones that already exist
A cut is server-side. It bounds what a new clone costs — the usual win for
CI and throwaway checkouts — but it does not reach into the working copies
people already have. A plain git fetch leaves a clone exactly as deep as it
was: the commits below the server's cut are still reachable from the client's
own branch, so even its git gc keeps them.
A client that wants to follow the server down asks for it:
git fetch --depth=50 origin main git gc --prune=now
The fetch shortens the local history and records the client's own boundary; the prune is what actually frees the space. A fresh clone does the same thing in one step, and gets exactly the history the server still has.
So an old working copy is not wrong, just deep — nothing breaks, it simply keeps what it already downloaded until someone asks it not to.
What retention frees, and what it does not
A blob survives as long as any kept commit's tree names it. With a 90-day window:
| Reclaimed? | |
|---|---|
| Big file added 100 days ago, deleted 95 days ago | yes — every commit holding it is outside the window |
| Big file added 100 days ago, deleted 10 days ago | not yet — commits inside the window still carry it |
| Big file added 100 days ago, still in the tree | no — it is live content |
So a time-based policy bounds history; it does not delete content that is still there. A file added and deleted within the window costs disk until the deleting commit itself ages out.
Refs pin their own trees, too. A two-year-old tag is kept — it is a deliberate
marker — and keeps every blob that existed at that commit alive; /git repo size
reports those bytes per ref so a disappointing reclaim has a visible cause. A
refs/pull/* ref whose tip predates the window is workflow state nobody closed,
so the policy expires it.
A branch is never deleted by retention: however old its tip, the tip survives and becomes the start of that branch's history.
Repositories that back a store
The versioned stores behind sites, galleries, calendars, and the system
configuration are Git repositories in the same namespace, and their commits are
the data — history is what Store reads to answer for a path. Retention refuses
those names:
/git repo set everlock-system retention=90d
'everlock-system' backs a versioned store; its history is the data and is not truncated