Name tags for commits: References are like name tags stuck on specific commits — without them, commits are just anonymous blobs of data identified only by hash. References give you human-friendly names like main, HEAD, and v1.0 to navigate the commit graph.
Why it matters: Without references, you would need to remember 40-character SHA-1 hashes to do anything in Git. References make the history navigable and enable workflows like feature branches and releases.
The key insight:HEAD is a special reference that points to the currently checked-out commit — when you make a new commit, HEAD moves forward. Understanding this explains why detached HEAD states feel weird and why git reset moves HEAD.
A reference (or “ref”) is a named pointer to a Git object — almost always a commit. References are What make Git’s object graph navigable. Without them, commits would exist as isolated objects with No way to find them (except by hash).
References are stored as plain text files under .git/refs/Each containing a 40-character SHA-1 Hash:
.git/refs/
├── heads/
│ ├── main # contains: a3f2b1c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6
A branch is a reference that moves forward as you make commits. It is stored at .git/refs/heads/<branch-name>.
Terminal window
## Create a branch
$gitbranchfeature-login
## Equivalent to: echo $(git rev-parse HEAD) > .git/refs/heads/feature-login
# Switch to a branch
$gitswitchfeature-login
# Equivalent to: echo feature-login > .git/HEAD
# List all branches
$gitbranch-a
Design decision: Branches in Git are extremely lightweight — they are a single file containing 41 bytes. This is why Git encourages branching freely, unlike CVS or SVN where branching involves Copying the entire directory tree. The cost of creating a branch is O(1); the cost of merging Depends on the divergence between branches.
When HEAD points directly to a commit (rather than a branch reference), you are in detached HEAD state. This means commits you create will not belong to any branch and will eventually be Garbage-collected unless you create a branch pointing to them.
gitGraph
commit id: "A"
commit id: "B"
commit id: "C"
branch feature
checkout feature
commit id: "D"
checkout main
checkout C
commit id: "E"
commit id: "F"
In the graph above, after checking out commit C (detached HEAD), commits E and F are orphaned — no branch points to them. To preserve them:
Terminal window
# While in detached HEAD at commit F
$gitbranchrecover-feature# Creates a branch pointing to F