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.
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>
HEAD, but it can be another ref.The command reports ref paths relative to the repository's .git/ directory.
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.
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.
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.
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.
HEAD. Query it with --quiet when that distinction is expected, then inspect the relevant ref with git show-ref.--short affects displayed output only. It does not change the stored target.refs/heads/main. Verify with a read immediately after every update.git symbolic-ref --short HEAD reports the expected branch, or your script deliberately handles detached HEAD.