A ref gives a commit a name: branches live under refs/heads/, tags under refs/tags/ and remote-tracking branches under refs/remotes/. In the default files backend each ref is a text file holding a hash, and HEAD is a symbolic ref that normally names a branch rather than a commit:
cat .git/HEAD
cat .git/refs/heads/main
git branch feature
git show-ref
git switch --detach -q HEAD~1
cat .git/HEAD
git switch -q mainOutput
ref: refs/heads/main 0be51cec615ba290d0cd9cd68508412c95aa6182 0be51cec615ba290d0cd9cd68508412c95aa6182 refs/heads/feature 0be51cec615ba290d0cd9cd68508412c95aa6182 refs/heads/main 653fc97f05515c8f22fe7b7865e1ae16351816d0
Creating feature wrote one 41-byte file and copied nothing. A commit writes the new object, then moves the branch that HEAD names. After switch --detach, HEAD holds a hash itself, a detached HEAD, and commits made there belong to no branch, which is how work gets "lost" (Reflog and Recovery gets it back). 4 shows both states.
