Skip to content
Laviyx Learn

Try

Git and GitHub course menu

How Git Works Internally: Blobs, Trees, Commits and Refs

See how Git stores data: blobs, trees, commits and tags, content-addressed hashes, what branches and HEAD really are, the .git folder, and packfiles.

Advanced6 min readUpdated
On this page

Git seems complicated until you see how simple its storage is. Git is a small database of objects, plus some named pointers. Once you understand this, commands such as reset and rebase stop being magic.

The .git folder#

Terminal
ls .git
Output
COMMIT_EDITMSG
HEAD
config
description
hooks
index
info
logs
objects
refs

The important parts are objects/ (all content), refs/ (branches and tags), HEAD (where you are), and index (the staging area).

Four kinds of object#

ObjectStores
blobThe content of a file, with no name
treeA folder: names pointing to blobs and other trees
commitA snapshot: one tree, the parent commits, author, date and message
tagAn annotated tag pointing to another object

Follow a commit#

git cat-file -p prints any object. Start at HEAD:

Terminal
git cat-file -p HEAD
Output
tree 84c2881032ee3fb9797658a9bc5c30cf46f08885
parent 7ba9dbd67cb3ad451d0ebda24571777ba3db8f9c
author Asha Rao <[email protected]> 1793849700 +0530
committer Asha Rao <[email protected]> 1793849700 +0530

Second commit

The commit points to a tree and to its parent. This is the whole idea of history: each commit stores its parent's ID. Now read the tree:

Terminal
git cat-file -p HEAD^{tree}
Output
040000 tree 3e908716ad0582da5b5d2b368738c70852ef0c1e	docs
100644 blob f5f7300f9ccb264ed1cc8574370dacdcbcd0a49e	notes.txt

The tree lists a blob for notes.txt, and another tree for the docs folder. Now read the blob:

Terminal
git cat-file -p HEAD:notes.txt
git cat-file -t HEAD:notes.txt
Output
Buy milk
Call the bank
blob

So a commit points to a tree, which points to blobs and other trees:

Text
commit --> tree --> blob   notes.txt
                \-> tree   docs/ --> blob   help.txt

Content addressing#

Each object's ID is the SHA-1 hash of its content. The same content always gives the same ID. You can calculate one yourself:

Terminal
echo "Buy milk" | git hash-object --stdin
Output
dea7672d5492473303cc27bf0d7fe1c2934ff95a

This has three consequences:

  • No duplicates. Ten files with the same content are stored once.
  • Snapshots are cheap. A commit stores the full tree, but unchanged files simply reuse the same blobs.
  • Corruption is detected. If any bit changes, the hash no longer matches, and changing an old commit would change every ID after it.

Git stores snapshots, not lists of differences. Commands like git diff calculate differences when you ask.

Branches are small files#

A branch is a file containing one commit ID:

Terminal
cat .git/refs/heads/main
git rev-parse HEAD
Output
ff4724c2526b0a50f77d76c6e9590e5a0082fad4
ff4724c2526b0a50f77d76c6e9590e5a0082fad4

That is why branches are instant to create. And HEAD is a file naming the current branch:

Terminal
cat .git/HEAD
Output
ref: refs/heads/main

Committing writes new objects, then updates the branch file. Now the commands make sense:

CommandWhat it really does
git branch xWrites a new file with the current commit ID
git switch xChanges HEAD and updates your files
git commitCreates blobs, trees and a commit, then moves the branch
git resetChanges the commit ID in the branch file
git rebaseCreates new commits and moves the branch

Old commits stay in the database until they are cleaned up. This is why the reflog can rescue them.

Packfiles#

New objects are stored as single compressed files. From time to time, Git packs them into one file, keeping only differences between similar versions, which saves a lot of space. git gc does this, and Git runs it automatically.

Terminal
git count-objects
git gc -q
git count-objects -v | grep -E '^(count|in-pack|packs)'
Output
8 objects, 0 kilobytes
count: 0
in-pack: 8
packs: 1

The objects moved from separate files into one pack.

A note on SHA-1#

Git has used SHA-1 for its IDs since the beginning. Git now uses a hardened version that detects known attacks, and it supports SHA-256 repositories too, although the older format remains the normal default.

Practice#

Practice exercise

Print the type of the object HEAD~1, then print the commit message stored in it using git cat-file -p, showing only its last line.

Show solution
Terminal
git cat-file -t HEAD~1
git cat-file -p HEAD~1 | tail -n 1
Output
commit
First commit

Key takeaway#

Next: Project: Simulate a Team.

Finished this lesson?