Inspect and Change Git Symbolic Refs
Your .git/HEAD file is not a commit ID, it is a pointer to another ref, and git symbolic-ref reads or rewrites that pointer directly. You will use it to see which branch HEAD names, create a symbolic ref that points at a branch, and remove that ref without editing files by hand. The examples use Git 2.43.0, supplied here by the Ubuntu git-man package. They take about five minutes in an existing test repository. No command here needs elevated privileges.
The route
Jump straight to the step you need, or tick off Done means at the end.
What a symbolic ref is
A normal ref ultimately identifies a commit. A symbolic ref instead contains a name beginning with ref: refs/ and points at another ref. The familiar .git/HEAD file is normally one: while you are on branch main, it points to refs/heads/main. Git uses this portable text-file mechanism instead of relying on filesystem symbolic links.
The command has three useful shapes:
git symbolic-ref <name>
git symbolic-ref <name> <ref>
git symbolic-ref --delete <name>
- First shape reads. Just the name, and Git prints the target.
- Second shape creates or updates. Give it a name and a ref.
- Third shape deletes. The name is commonly
HEAD, but it can be another ref.
The command reports ref paths relative to the repository's .git/ directory.
Checkpoint: confirm the current branch
Change into the repository you want to inspect.
cd /path/to/repository
git symbolic-ref HEAD
On a checkout of main, the expected output is:
refs/heads/main
For the shorter branch name, add --short:
git symbolic-ref --short HEAD
main
This is useful in shell scripts, but it succeeds only when the argument is a symbolic ref. A detached checkout has a commit ID in HEAD, not a branch ref. In that state, the ordinary command exits with status 128 and prints an error. Use quiet mode when that is an expected possibility:
if branch=$(git symbolic-ref --quiet --short HEAD); then
printf 'on branch %s
' "$branch"
else
printf '%s
' 'detached HEAD or another symbolic-ref error' >&2
fi
--quiet suppresses the diagnostic for a non-symbolic HEAD, and the exit status is 1 in that case. Do not treat the absence of a branch name as proof the repository is broken: detached HEAD is a valid Git state.
Create a symbolic ref
Creating a symbolic ref changes repository metadata. Use a disposable repository or a ref name owned by your workflow before trying this in automation.
Checkpoint
Point refs/heads/release at the existing main branch.
git symbolic-ref refs/heads/release refs/heads/main
git symbolic-ref refs/heads/release
git symbolic-ref --short refs/heads/release
Expected output is:
refs/heads/main
main
The first command creates the ref; the next two verify both its full target and its shortened target. If the symbolic ref already exists, the same two-argument form updates it. This is a ref operation, not a commit and not a branch merge: the target name changes, while the commits named by the target branches do not.
Tip
If you want an audit reason recorded in the ref's reflog, supply -m while creating or updating it.
git symbolic-ref -m 'point release at main' refs/heads/release refs/heads/main
The reason option is valid only for creation or update. It cannot be combined with deletion or a read-only query.
Follow one level or the whole chain
Git follows symbolic refs recursively by default. Suppose refs/heads/release points at another symbolic ref, refs/heads/stable, which points at refs/heads/main. A normal read follows the chain to refs/heads/main. To inspect only the first link, use --no-recurse:
git symbolic-ref --no-recurse refs/heads/release
The output in that chain would be:
refs/heads/stable
This distinction matters when debugging indirection. A shortened result can hide the fact you are inspecting a link rather than a direct branch target, so use the full form first when correctness matters.
Delete only the ref you own
Warning
--delete removes the named symbolic ref. It is a destructive metadata change, although it does not delete commits. Check the exact name before running it, and do not use it on HEAD as a way to leave detached state.
git symbolic-ref refs/heads/release
git symbolic-ref --delete refs/heads/release
Deletion prints no success message. Verify it by reading the ref and checking the status:
if git symbolic-ref --quiet refs/heads/release; then
printf '%s
' 'release still exists' >&2
exit 1
else
printf '%s
' 'release symbolic ref is absent'
fi
Recovery
If you deleted the wrong symbolic ref, recreate it with the two-argument form, provided you know the intended target.
git symbolic-ref refs/heads/release refs/heads/main
That restores the ref name and target, but it does not restore any reflog history that was removed with the ref.
Common failure modes
- "not a symbolic ref". The name may be a direct ref, missing, or detached
HEAD. Query it with--quietwhen that distinction is expected, then inspect the relevant ref withgit show-ref. - Unexpected full output.
--shortaffects displayed output only. It does not change the stored target. - Wrong target after an update. Use fully qualified names such as
refs/heads/main. Verify with a read immediately after every update. - Assuming it moved commits. symbolic-ref changes an indirection. It does not rewrite commits, switch the worktree, or merge branches.
Done means
- Checked the branch:
git symbolic-ref --short HEADreports the expected branch, or your script deliberately handles detachedHEAD. - Verified any created or updated ref returns the expected target from a follow-up read.
- Checked any deletion against the exact ref name, and verified it as absent.
- Understood the boundary: symbolic-ref changes ref metadata, not commit history or working-tree files.