Skip to content
~/freshteapot

Writing

Checking Git disk usage after working with Codex

How I reduced Git storage from about 7 GiB to 611 MiB by checking Codex snapshots, cleaning temporary packs, and pruning unreachable objects.

4 min read
On this page

To check how much space Git is using, run these two commands inside your repository:

du -sh "$(git rev-parse --git-common-dir)"
git count-objects -vH

My repository’s .git directory was about 7 GiB. Removing 58 abandoned temporary pack files reclaimed 3.44 GiB, leaving about 3.4 GiB. Removing unreachable objects then brought it down to 611 MiB.

Read the storage breakdown

In the output from git count-objects, look for:

  • size-pack: space used by packed Git objects.
  • size: space used by loose objects.
  • garbage: the number of files Git does not recognise as valid object storage.
  • size-garbage: the space those files occupy.

My warnings named files such as .git/objects/pack/tmp_pack_…. These were temporary pack files. Their presence was consistent with interrupted packing operations; I did not establish what interrupted them.

Codex snapshots can include untracked files

Codex can store snapshots of files you have never added or committed to Git. That means local files can occupy space inside .git even when they are absent from your branch history.

In my repository, Codex had stored snapshots under these references:

refs/codex/turn-diffs/captures/...
refs/codex/turn-diffs/checkpoints/...

You can check for them with:

git for-each-ref --format='%(objecttype) %(refname)' refs/codex

These references pointed to Git trees: stored directory snapshots. Their names indicate that they support tracking changes between turns. A Git worktree is a checkout directory; the snapshots we found were objects inside Git’s storage.

Git reuses identical file objects, while changed content can create new versions. In my case, snapshots included several versions of an untracked local database, each over 1 GiB before compression.

This makes .gitignore useful even for files you never intend to commit. After ignoring that database, I confirmed that newer Codex captures excluded it. Older snapshots still retained their copies.

Remove abandoned temporary packs

Before cleaning up, stop Codex sessions and other Git activity for the repository, including linked worktrees. Keep them stopped during cleanup. These commands work on macOS with lsof installed:

git_dir="$(git rev-parse --git-common-dir)"
lsof +D "$git_dir/objects/pack"

If lsof lists processes, wait for them to finish. No output normally means no open files were found; if it reports an error, resolve that first. This is a point-in-time check, so it cannot protect against another process starting afterwards.

List temporary pack files older than an hour:

find "$git_dir/objects/pack" -type f -name 'tmp_pack_*' -mmin +60 -print

Once you have reviewed that list and confirmed they are abandoned, the following command permanently deletes those matching temporary files. Age alone does not establish that a file is abandoned. Keep regular pack-*.pack files intact.

find "$git_dir/objects/pack" -type f -name 'tmp_pack_*' -mmin +60 -print -delete

Then run normal garbage collection and check the result:

git gc
git fsck --connectivity-only --no-dangling
du -sh "$git_dir"
git count-objects -vH

In my case, garbage and size-garbage became zero, and the connectivity check passed. The temporary-file removal produced the 3.44 GiB saving; git gc alone was not the whole cleanup.

Why unreachable objects remain

After that cleanup, .git was still 3.4 GiB.

About 2.84 GiB was unreachable data: stored objects no longer retained through references or reflogs. Git keeps recent unreachable objects for a grace period. This can help recover discarded work and protects objects another Git process may have written before attaching a reference to them.

Normal git gc uses a two-week expiry cutoff by default. In this repository it collected that data into a cruft pack, a pack for retained unreachable objects. A later collection can remove objects once they qualify for expiry. There is no background countdown that deletes them automatically.

Another roughly 0.59 GiB remained referenced, including Codex checkpoints. Ordinary branch, tag, and remote-tracking history accounted for only about 79 MiB of stored objects. Garbage collection preserves referenced data, even when it is an old database snapshot.

Remove unreachable objects now

To remove unreachable objects immediately, you can skip the age grace period. This permanently removes otherwise recoverable data. Stop Codex sessions and all other Git activity in the repository and its linked worktrees first; concurrent writes can risk corruption. Keep them stopped until collection finishes.

git gc --prune=now

This preserves objects retained by references and reflogs, including branches, tags, stashes, and Codex checkpoints. It does not remove those checkpoints.

Check the resulting size and connectivity:

du -sh "$(git rev-parse --git-common-dir)"
git count-objects -vH
git fsck --connectivity-only --no-dangling

The result

For this second cleanup, my du output went from:

3.4G    .git

to:

611M    .git

That reclaimed roughly another 2.8 GiB, on top of the 3.44 GiB from temporary files. The remaining storage includes referenced Codex snapshots as well as the repository’s ordinary history.