Introduction
A symbolic ref is a reference whose value is another ref name rather than an object hash. The canonical example is HEAD, which usually contains ref: refs/heads/main. Symbolic refs let Git track "the current branch" as data, not state.
Reading and writing
git symbolic-ref HEAD
# refs/heads/main
git symbolic-ref refs/remotes/origin/HEAD
# refs/remotes/origin/main
git symbolic-ref HEAD refs/heads/feature/login # change without checkout
git symbolic-ref --short HEAD # short form: main
git symbolic-ref --delete refs/remotes/origin/HEAD
HEAD as a symbolic ref
When you commit, Git resolves HEAD to find the current branch and updates that branch's tip. When HEAD is detached it stops being symbolic and contains a raw SHA.
cat .git/HEAD
# ref: refs/heads/main (symbolic)
# vs after detach:
# a1b2c3d4... (raw)
Remote HEAD
Each remote can have a symbolic HEAD indicating its default branch. Set or refresh:
git remote set-head origin --auto # query server
git remote set-head origin main # set explicitly
git remote set-head origin --delete
This is what makes git log origin resolve to origin/main.
Why use them
- "Current branch" can change without rewriting history.
- Tools can ask "what is the default?" without hardcoding names.
- Hooks can target the currently checked out branch generically.
Worktrees and per-worktree HEAD
Every linked worktree (git worktree add) has its own HEAD, stored under .git/worktrees/<name>/HEAD. Each can point at a different branch:
git worktree add ../wt-feature feature/login
cat ../wt-feature/.git
# gitdir: /path/to/main/.git/worktrees/wt-feature
cat .git/worktrees/wt-feature/HEAD
# ref: refs/heads/feature/login
Detection
git symbolic-ref -q HEAD && echo "on a branch" || echo "detached"
Renaming the default
To rename master to main safely:
git branch -m master main
git symbolic-ref HEAD refs/heads/main
git push -u origin main
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
Inspecting symbolic resolution
git rev-parse resolves a name through symbolic refs all the way to a SHA, while git symbolic-ref only follows one level. The pair is useful for scripts:
git symbolic-ref HEAD # refs/heads/main
git symbolic-ref --short HEAD # main
git rev-parse HEAD # SHA
git rev-parse --symbolic-full-name HEAD # refs/heads/main
git rev-parse --symbolic-full-name @{u} # refs/remotes/origin/main
Use --symbolic-full-name when you want the canonical ref name no matter what shorthand the user typed.
Common mistakes
Editing .git/HEAD by hand and forgetting the trailing newline, leaving Git unable to parse it. Use git symbolic-ref. Treating refs/remotes/origin/HEAD as authoritative when it is actually cached; refresh with git remote set-head origin --auto. Confusing a symbolic ref pointing at a missing branch with a real error; git symbolic-ref -q exits non-zero, which scripts can handle. Finally, deleting the underlying ref without updating the symbolic one, leaving a dangling symref; tools may report it but it is harmless and easy to fix with another symbolic-ref.