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.
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#
ls .gitCOMMIT_EDITMSG
HEAD
config
description
hooks
index
info
logs
objects
refsThe important parts are objects/ (all content), refs/ (branches and tags), HEAD (where you are), and index (the staging area).
Four kinds of object#
Follow a commit#
git cat-file -p prints any object. Start at HEAD:
git cat-file -p HEADtree 84c2881032ee3fb9797658a9bc5c30cf46f08885
parent 7ba9dbd67cb3ad451d0ebda24571777ba3db8f9c
author Asha Rao <[email protected]> 1793849700 +0530
committer Asha Rao <[email protected]> 1793849700 +0530
Second commitThe 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:
git cat-file -p HEAD^{tree}040000 tree 3e908716ad0582da5b5d2b368738c70852ef0c1e docs
100644 blob f5f7300f9ccb264ed1cc8574370dacdcbcd0a49e notes.txtThe tree lists a blob for notes.txt, and another tree for the docs folder. Now read the blob:
git cat-file -p HEAD:notes.txt
git cat-file -t HEAD:notes.txtBuy milk
Call the bank
blobSo a commit points to a tree, which points to blobs and other trees:
commit --> tree --> blob notes.txt
\-> tree docs/ --> blob help.txtContent 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:
echo "Buy milk" | git hash-object --stdindea7672d5492473303cc27bf0d7fe1c2934ff95aThis 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:
cat .git/refs/heads/main
git rev-parse HEADff4724c2526b0a50f77d76c6e9590e5a0082fad4
ff4724c2526b0a50f77d76c6e9590e5a0082fad4That is why branches are instant to create. And HEAD is a file naming the current branch:
cat .git/HEADref: refs/heads/mainCommitting writes new objects, then updates the branch file. Now the commands make sense:
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.
git count-objects
git gc -q
git count-objects -v | grep -E '^(count|in-pack|packs)'8 objects, 0 kilobytes
count: 0
in-pack: 8
packs: 1The 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
git cat-file -t HEAD~1
git cat-file -p HEAD~1 | tail -n 1commit
First commitKey takeaway#
Next: Project: Simulate a Team.
Finished this lesson?