Git Internals - bparlaker/cmpe-git-practice GitHub Wiki
Git Internals - what is a commit, really?
Most Git confusion disappears once you see that Git is just a content-addressed key-value store plus a few pointers.
1. Four object types
| Object | Stores | Analogy |
|---|---|---|
| blob | the contents of a file (no name!) | a page of text |
| tree | a directory listing: names → blobs/trees | a folder |
| commit | one root tree + parent(s) + author + message | a snapshot with a label |
| tag (annotated) | a named, signed pointer to a commit | a bookmark |
Every object is named by the SHA-1 hash of its content (SHA-256 is supported in newer repos). Same content ⇒ same hash ⇒ stored once.
2. See it yourself
mkdir demo && cd demo && git init
echo "hello git" > hello.txt
git add hello.txt
git commit -m "first commit"
git cat-file -t HEAD # -> commit
git cat-file -p HEAD # show the commit object
Output (hashes will differ):
tree 07ed5a7aebb914e3a02edf6d622b82d364037e3c
author Berker Parlaker <[email protected]> 1790680000 +0300
committer Berker Parlaker <[email protected]> 1790680000 +0300
first commit
git cat-file -p 07ed5a7 # the tree
# 100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f hello.txt
git cat-file -p 8d0e412 # the blob
# hello git
You can even compute a blob's hash without Git storing anything:
echo "hello git" | git hash-object --stdin
# 8d0e41234f24b6da002d962a26c2495ea16a425f
3. Commits form a graph (DAG)
Each commit points to its parent(s), so history is a directed acyclic graph:
A <-- B <-- C <-- D (main)
\
E <-- F (feature)
Changing anything in an old commit changes its hash - and therefore the hash of every commit after it. That is why history is tamper-evident, and why rewriting pushed history is disruptive.
4. Branches and HEAD are just tiny files
cat .git/HEAD # ref: refs/heads/main
cat .git/refs/heads/main # 40-char commit hash
A branch is a 41-byte file holding a hash. Creating a branch is therefore instant and nearly free.
5. Cool facts
git gcpacks objects and stores deltas, so repositories stay small even though Git conceptually stores full snapshots.- An empty folder cannot be committed - trees only list things that contain blobs. (Convention: add a
.gitkeepfile.) git fsckverifies every hash in the repo - corruption can't go unnoticed.