Step 1 of 5 · Reading · ~4 min
Read
Refs & History
Refs and HEAD
Here is the number that reframes git: a branch is 41 bytes. Forty hex characters of SHA-1 plus a newline, in a plain text file. Everything that feels heavy about branches (creating, deleting, switching, moving) is writing 41 bytes to a file. After this lesson your program keeps a small store of refs and HEAD and behaves like git update-ref, git symbolic-ref, git show-ref and git rev-parse on it.
Refs: names for hashes
Hashes are perfect identifiers and terrible names. A ref maps a human name to a hash and lives exactly where you would hope:
.git/refs/heads/main contains "a1b2c3...\n" a branch
.git/refs/tags/v1.0 contains "e5f6a7...\n" a tag
.git/refs/remotes/origin/main a remote-tracking branch
Creating a branch is creating a file; deleting it is deleting the file. The ten thousand commits a branch "contains" are found by walking parents from the one hash it holds. Branches and tags are stored identically. The only difference is who moves them: committing advances a branch, and nothing ever moves a tag. (Git also packs old refs into one packed-refs file for speed; same mapping, different storage.)
HEAD: where you are
.git/HEAD answers "which branch am I on?" Normally it does not hold a hash. It holds the name of a ref:
ref: refs/heads/main
That is a symbolic ref, a pointer to a pointer, and it is how committing works. Git builds the commit, reads HEAD, sees it names refs/heads/main, and writes the new hash into that branch. HEAD itself never changes, but where you are advanced, because the name it points at moved. Updating HEAD therefore means updating whatever it points to.
When HEAD holds a bare 40-digit hash it is detached. Commits made now advance no branch; they are reachable only from HEAD and are lost once it moves. That is the whole phenomenon, and the scary warning is its description.
Chains and loops
A symbolic ref can point at another symbolic ref. Resolving follows the chain until it reaches a ref that holds a hash. Git follows at most four symbolic links in a row (it reads at most five files, the last of which must hold a hash), because a chain that never ends (a points to b, b points back to a) must not hang a program. A chain can also end at a name that does not exist yet. That is a dangling ref, and it is the normal state of a brand-new repository: HEAD names refs/heads/main before the first commit creates it.
Resolving a short name
People type v1.0 or main, not refs/tags/v1.0. Git tries these, in this order, and takes the first that exists:
<name> refs/<name> refs/tags/<name> refs/heads/<name> refs/remotes/<name> refs/remotes/<name>/HEAD
So when a branch and a tag share a name, the tag wins. A full 40-digit hash is its own answer.
What counts as a ref name
Git refuses names that would break the file system or the syntax around them: an empty piece between slashes, a piece that starts with . or ends in .lock, two dots in a row, a control character, a space, any of ~ ^ : ? * [ \, a trailing dot, or @{ anywhere. Writing these rules once, in one function, is what keeps every command consistent.
Common mistakes
- Writing a new commit to
HEADas if it were a plain file, destroying the symbolic ref instead of moving the branch it names. - Resolving a short name against branches first. Tags come before branches.
- Following symbolic refs without a depth limit.
- Treating a dangling HEAD as an error in every command. Only commands that need a hash fail.
- Accepting a name with
..or a leading dot in a component because "it is just a file name".
Your exercise
The starter holds the store (a map from name to either a hash or the text ref: <name>) and handles the command parsing. You write the three rules everything else depends on: valid_ref_name, follow (the symbolic-ref chain with the five-hop limit) and dwim (the short-name search order). The expected output for the lookup cases was checked against a real repository with a branch and a tag of the same name, and the name rules against git check-ref-format.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…