Git Detailed Course: Internals & Architecture#
Git is fundamentally a content-addressable key-value datastore with a Directed Acyclic Graph (DAG) filesystem layered on top. In this in-depth guide, we demystify the internal plumbing of Git, from low-level objects in .git/objects to interactive rebasing, automated conflict reuse (rerere), and automated bisect regression hunting.
Table of Contents#
- 1. Content-Addressable Storage & Hash Keys
- 2. The 4 Core Git Objects: Blobs, Trees, Commits, Tags
- 3. Anatomical Tour of the .git Directory
- 4. The Index (Staging) Binary Format & Hashing
- 5. Packfiles, Delta Compression & git gc
- 6. Interactive Rebase Masterclass
- 7. The Reflog & Dangling Object Recovery
- 8. Reusing Merge Resolutions (git rerere)
- 9. Binary Search Bug Hunting with git bisect
- 10. Low-Level Plumbing Command Reference
1. Content-Addressable Storage & Hash Keys#
Git does not store files by filename or directory path in its object database. Instead, it computes the cryptographic SHA-1 (or SHA-256) checksum of the object type, size, null byte, and payload content.
The hash is a 40-character hexadecimal string. Git splits the hash into:
- First 2 characters: directory name under
.git/objects/ - Remaining 38 characters: filename inside that directory (compressed with zlib)
# Calculate Git object hash manually from standard input
echo "hello world" | git hash-object --stdin
# Output: 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
# Store the object directly into .git/objects
echo "hello world" | git hash-object -w --stdin
2. The 4 Core Git Objects: Blobs, Trees, Commits, Tags#
Every commit in a Git repository decomposes into four fundamental immutable object types:
- Blob (Binary Large Object): Stores pure file content byte-for-byte. A blob contains no filename, directory path, or permissions. Identical file contents across different folders or commits share the exact same blob.
- Tree: Represents a directory listing. Maps filenames and permissions (e.g. 100644 for files, 040000 for subdirectories) to their corresponding child blob or tree SHA-1 hashes.
- Commit: Stores metadata pointing to a top-level root tree hash, parent commit hash(es), author timestamp, committer timestamp, and the commit log message.
- Annotated Tag: An immutable reference object pointing to a specific commit with a message, tagger identity, and optional GPG signature.
# Inspect the type of any Git object
git cat-file -t <object-sha>
# Inspect the raw contents of any Git object
git cat-file -p <object-sha>
# View tree contents formatting
git ls-tree HEAD
3. Anatomical Tour of the .git Directory#
.git/
├── HEAD # Symbolically points to the currently checked-out ref (e.g., ref: refs/heads/main)
├── config # Repository-specific configuration options
├── description # Used by GitWeb or Git daemon
├── hooks/ # Client-side and server-side automation hook scripts
├── info/exclude # Local ignore patterns not committed to shared repository
├── index # Binary file containing the staging area cache and file stat signatures
├── objects/ # Content-addressable object store (blobs, trees, commits, packfiles)
└── refs/
├── heads/ # Local branch pointers (each file contains a commit SHA)
├── tags/ # Tag pointers
└── remotes/ # Remote-tracking branch pointers (e.g., origin/main)
4. The Index (Staging) Binary Format & Hashing#
The .git/index file is a high-performance binary cache that acts as a bridge between your working filesystem and the commit history. It contains sorted records of every tracked file:
- Filesystem metadata: ctime, mtime, device, inode, file mode (permissions), UID, GID, and file size.
- The SHA-1 object ID of the file content currently staged.
- Conflict stage markers (0 = normal, 1 = ancestor, 2 = target HEAD, 3 = incoming branch).
When running git status, Git compares the file's disk mtime and size against the index cache. If they match, Git skips reading the file content entirely, allowing sub-millisecond status checks on repositories with hundreds of thousands of files.
5. Packfiles, Delta Compression & git gc#
Loose objects (individual compressed files in .git/objects/xx/) consume inodes and storage. Periodically, or during git push / git gc, Git packs loose objects into a single .pack file accompanied by a .idx index file.
Git uses sliding-window delta compression: instead of storing whole files repeatedly, it stores a baseline version and compressed binary deltas (insertions, deletions, copies) between related versions. This frequently reduces repository size by 80% to 95%.
# Run aggressive garbage collection and packfile optimization
git gc --aggressive --prune=now
# Verify object database integrity
git fsck --full
6. Interactive Rebase Masterclass#
Interactive rebase (git rebase -i) allows reshaping local commits before sharing them upstream. Git generates an instruction sheet in a temporary file and replays commits in sequence:
git rebase -i HEAD~4
Commands available in the todo list:
pick(p): Keep commit as is.reword(r): Keep commit content but rewrite commit message.edit(e): Pause rebase at this commit to amend files or split into smaller commits.squash(s): Meld this commit into the previous commit and combine commit logs.fixup(f): Meld into previous commit, discarding this commit's message.drop(d): Delete this commit entirely.
7. The Reflog & Dangling Object Recovery#
The Reference Log (reflog) records every time HEAD or any local branch pointer changes position (e.g. commit, checkout, rebase, reset). Commits discarded by git reset --hard are not deleted immediately; they remain in the object store for at least 30 to 90 days.
# Inspect HEAD movement history
git reflog
# Recover from an accidental hard reset
git reset --hard HEAD@{1}
# Find unreachable commits that are no longer referenced by any branch
git fsck --lost-found
8. Reusing Merge Resolutions (git rerere)#
git rerere stands for "Reuse Recorded Resolution". When enabled, Git records conflict pre-images and your manual resolution. If you rebase frequently or maintain long-lived integration branches, Git will automatically resolve repeated conflicts identically without asking you again.
# Enable rerere globally
git config --global rerere.enabled true
# Automatically stage resolved conflicts when recognized
git config --global rerere.autoUpdate true
9. Binary Search Bug Hunting with git bisect#
When a bug is introduced across hundreds of commits, git bisect uses binary search in O(log N) steps to isolate the exact commit that introduced the regression.
# Start bisect session
git bisect start
git bisect bad # Current commit is broken
git bisect good v1.4.0 # v1.4.0 is known good
# Git automatically checks out middle commit. Run test:
# If broken: git bisect bad
# If working: git bisect good
# Automated bisect with a test script or command:
git bisect run npm test
# Reset back to original branch when complete
git bisect reset
10. Low-Level Plumbing Command Reference#
# Write index to a tree object
git write-tree
# Create commit object from tree
git commit-tree <tree-sha> -m "Manual commit" -p <parent-sha>
# Update branch pointer ref directly
git update-ref refs/heads/main <commit-sha>
# View raw object payload
git cat-file -p <object-sha>