Inside the .git folder: objects, refs and HEAD
Everything Git knows about your project lives in one hidden folder. It holds only two kinds of thing: objects named by a fingerprint of their content, and small name files that point at them. Once you have walked from HEAD to a file's bytes by hand, every command in this course stops being magic.
By the end you can
- Explain content-addressable storage and why the same content always gets the same ID
- Name the four object types and walk commit, tree and blob with
git cat-file - Read refs,
packed-refsandHEAD, and explain why a branch costs almost nothing - Recognise a detached HEAD and save work made in it
- Describe packfiles, delta compression and where SHA-256 fits, and compute a blob ID yourself in code
The everyday picture
Imagine a distribution centre where every carton gets a label computed from exactly what is inside it. Pack 24 bottles of the same sunflower oil and you get the same label, every time, in any of the four DCs. Two identical cartons? You only need to store one. Change one bottle and the label changes, so nobody can swap contents without it showing.
Next to the racks hangs a board of index cards: "Festival stock: carton 3f9a...", "Current price list: carton 81c2...". The cards are cheap. Moving a card to a new carton takes a second and moves nothing heavy. Git's .git folder is that DC. The cartons are objects, labelled by their content. The cards are refs: branches and tags.
Content-addressable storage
Git stores data in an object database in .git/objects. Each object's name is the hash of its content. This is called content-addressable storage. For a file, Git hashes a short header (the word blob, a space, the size in bytes and a zero byte) followed by the file's bytes. The result, in a normal repository, is a 40-character SHA-1 ID such as cd39bff4ac5ff93704ecccc19d3e86bc886791af.
Three consequences follow, and most of Git's behaviour comes from them:
- Same content, same ID, everywhere. Your laptop and a teammate's compute identical IDs for identical files, which is why fetching only sends objects the other side lacks (GitHub: remotes, clone, push and pull).
- Storage is deduplicated. A file unchanged in 500 commits is stored once. 500 copies of the same logo are one object.
- Objects never change. Changing content means a new ID, so an "edited" object is really a new one. That is why amend and rebase create new commits (Rewriting history safely), and why
git fsckcan detect a corrupted file: its content no longer matches its name.
On disk, a new object is a loose object: zlib-compressed, in a folder named by the first two hex characters, with the other 38 as the file name. That is why you see folders like .git/objects/cd/.
Quick check
Git has just written a loose object with ID 6195263fe767120970c04b6f61c22cf22215ebf5. Where is it on disk?
The first two hex characters become a folder and the other 38 the file name, which keeps any one folder from holding millions of files. Refs hold names that point at objects; they are not where objects live.