Source: Maybe a better way to track the release history of a Git repo, by Mark Dominus
One tag per deploy turns “what do we roll back to?” into a list, filter and sort over thousands of refs. Mark Dominus proposed one ref per app and tier instead, where each deploy adds a commit with two parents. He wrote it in 2019 and doubts it was ever adopted.
At ZipRecruiter, every deploy to staging or production created a tag object named like refs/release/stg.www.our-app-2019-09-06T114410…-treesha-…. Tier, app, time and tree hash all lived in the name.
There came to be thousands of them. Git, he writes, handles thousands of commits well but not thousands of refs. At the time, refs lived in a packed-refs file, and resolving a name meant searching it, locally and on the Git server. git fetch and git branch output filled with release tags.
The natural questions got hard. “When did we last deploy www.our-app to production?” meant: list every tag, filter for prod.www.our-app, sort by date, take the last. Rolling back a bad staging deploy meant the same list, then the second-last item.
He names two more problems: the metadata was only a naming convention Git could not read, and tags are not immutable. They can be deleted or added under any name.
Keep one ref per (tier, app), for example stg.www.our-app. It points to a metainfo commit. That commit has two parents: the previous metainfo commit, and the commit that was actually released. The release time is the metainfo commit’s own creation time.
first parent → previous record · second parent → what shipped
Any useful text. He suggests TOML or git interpret-trailers for structure.
git commit-tree -p refs/release/stg.www.our-app -p H H^{tree} with the message on stdin. It prints the new commit’s SHA, N.
git push origin N:refs/release/stg.www.our-app. The ref count stays at one.
| Question | Before: tags | Proposal |
|---|---|---|
| Current release | list, filter, sort, last | HISTORY^2 |
| Rollback target | list, filter, sort, second-last | HISTORY~1^2 |
| Release n ago | list, filter, sort, count back | HISTORY~n^2 |
| Full history | parse tag names | git log HISTORY |
| Last release before a date | parse and compare names | git log -1 --until=date HISTORY |
| Diff of last two releases | find both tags first | git diff HISTORY~1^2 HISTORY^2 |
Fetch the whole history once with git fetch origin refs/release/stg.www.our-app:HISTORY. A rollback can point a new metainfo commit at the exact same release commit as before.
On performance, he notes this is the same linked list git log already walks: Git took a few seconds to trace their 275,000-commit monorepo history, so a few hundred release commits is not a concern.
Mark Dominus: a proposal for release history in Git.
Sources: every fact on this page is from the Dominus post above. “1 ref”, “2 parents” and “2019” are stated there.