While I was still at ZipRecruiter, our Git monorepo was developing an annoying problem: it had too many refs.

Whenever there was a deployment of any of the applications in the repo, to staging or to production, the deployment tool would create a tag object with a name like this:

refs/release/stg.www.our-app-2019-09-06T114410.954-0700-treesha-179dab6fd11bee5195f0f8e31cea3edfb86ae23b

The tag contained the name of the target tier of the deployment (stg here, for “staging”), the app name and class of hosts on which it ran, www.our-app in this case, the date and time, and the hash of the tree that was deployed. This made a complete record of what was deployed, where, and when.

After we had been doing this for a while, there were thousands of these tag objects. Git is well-designed for handling thousands of commits, but not for handling thousands of refs. Perhaps this has changed, but at the time, Git would store refs in a file called packed-refs, which, for each ref, contained an entry with the ref's name and the ID of the object to which it referred.