Git objects and references: commits, trees, blobs and HEAD
Inspect Git content-addressed storage: blobs hold bytes, trees map filenames and commits link snapshots to parents. Read the objects behind a branch and explain detached HEAD.
TL;DR: Git commits refer to snapshots represented by trees and blobs, while branch references identify moving positions in history. Changing a reference and changing a commit object are different operations, which explains rebase, detached HEAD and recovery behavior.
Objects hold content; references give it names
A blob stores file content without its filename. A tree maps names and modes to blobs or other trees. A commit identifies a tree, parent commits and metadata such as author, committer and message. An annotated tag is another object type that can name an object with metadata.
A branch is a reference to a commit, not a complete copy of every file. HEAD ordinarily refers symbolically to the current branch. In a detached state it refers directly to a commit, so new commits can exist without advancing a named branch.
The official Git objects chapter explains the object model, while Git references explains naming and HEAD.
The arrows express object references. Storage can use packfiles and compression, but those optimizations do not change the logical snapshot model.
Inspect a small repository
Run this in a fresh disposable directory. It has no remote and modifies only its demonstration files.
git init -q -b main object-demo
cd object-demo
git config user.name 'Example Learner'
git config user.email '[email protected]'
printf 'ready\n' > app.txt
git add app.txt
git commit -qm 'Record application snapshot'
git cat-file -t HEAD
git cat-file -p HEAD
git ls-tree HEAD
git cat-file -p HEAD:app.txt
git symbolic-ref HEAD
The object type is commit; the commit's printed content includes a tree reference. The tree listing maps app.txt to its blob, and reading that blob prints ready. The symbolic reference is refs/heads/main.
This separation explains why renaming a file without changing its bytes can reuse content storage while producing a different tree. The filename belongs to the tree entry. It also explains why two commits with identical file snapshots can still have different identities: their parents or metadata can differ.
Worked explanation: rebase changes the commit ID
Suppose a feature commit is based on an older main-branch commit. Rebase replays the change onto a newer parent. Even if the resulting file contents match a previous snapshot, the new parent relationship changes the commit object and therefore its identity.
A reviewer comparing only filenames misses that history transformation. This is why rewriting a shared branch affects collaborators whose work points to the old commits. Merge versus rebase develops the workflow decision; the object model supplies the mechanism.
| Item | What it identifies | Operational implication |
|---|---|---|
| Blob | File bytes | Filename changes need not change blob content |
| Tree | Names, modes and child object references | Directory snapshot changes alter tree identity |
| Commit | Tree, ancestry and metadata | Rebase or amend creates a different commit |
| Branch ref | Current named history position | Reset can move the branch without editing old objects |
| HEAD | Current checkout position | Detached commits need a named ref to retain an obvious branch path |
Detached HEAD is a state, not immediate data loss
Checking out a particular commit can detach HEAD. You can inspect and even commit there. The risk is moving away without preserving a reference to work you want to keep.
Create a branch at the intended commit before leaving, or use reflog recovery to locate the recent position if needed. Unreachable objects can eventually be removed by maintenance, so a reflog is not a permanent archival guarantee.
Do not assume an object is deleted because it disappeared from an ordinary branch log. First inspect references and ancestry. Conversely, deleting a branch name does not securely erase sensitive content from all clones or repository storage.
Use the model in delivery investigations
A deployment should identify the intended source revision and resulting artifact. A branch name alone can move between build and investigation. Preserve the commit ID in build provenance, then relate it to the artifact digest rather than assuming the two are the same identifier.
Can two commits contain the same tree? Yes. Different parents or metadata can produce different commits referencing an identical snapshot.
Does cherry-pick retain the original commit identity? Ordinary cherry-pick records the change in a new ancestry context, usually creating a new commit. Backport discipline explains how to preserve traceability despite that new identity.