How Git Works: The Data Structures Behind Version Control
Introduction: Why Git is Elegant
Git is fast, reliable, and conceptually simple, yet deceptively sophisticated. Understanding how git works reveals deep principles of computer science: immutability, content-addressed storage, and functional data structures.
Git was created by Linus Torvalds in 2005 to manage the Linux kernel source code. Before git, Linux kernel development used a commercial tool (BitKeeper) that became unavailable. Linus needed something better, so he built git in roughly two weeks. He named it "the stupid content tracker," a tongue-in-cheek reference to himself.
What makes git elegant is its foundation: a directed acyclic graph (DAG) of immutable objects. Everything is hashed. Nothing changes; only new objects are created. This simplicity enables powerful features: branching, merging, distributed collaboration, complete history recovery.
Part One: The Core Data Structures
Git Objects: The Foundation
Git's repository is fundamentally a database of objects. There are four types:
| Object Type | Purpose | Contains | Identified By |
|---|---|---|---|
| Blob | File content | The actual bytes of a file | SHA-1 hash of content |
| Tree | Directory structure | List of blobs and trees with names and permissions | SHA-1 hash of tree contents |
| Commit | Snapshot of project | Tree root, parent commit(s), author, message, timestamp | SHA-1 hash of commit metadata |
| Tag | Named reference to commit | Commit hash, tagger, message (annotated tags) | SHA-1 hash or name |
Content-Addressed Storage
The genius of git's design: every object's hash is the SHA-1 hash of its content. All objects are compressed with zlib before being written to disk, which reduces storage requirements and speeds up network transfers.
This has profound implications:
- Immutability: If you change content, the hash changes. All commits built on top become invalid. You can't corrupt history secretly.
- Deduplication: Two identical files produce the same blob. No redundant storage.
- Integrity: If any bit flips, the hash doesn't match. Corruption is detected immediately.
- Distributed: You can verify objects anywhere. No central server needed.
The Commit DAG
Commits form a directed acyclic graph. Each commit points to one or more parent commits (or zero parents for the initial commit). The DAG structure enables all of git's power.
References: The Human-Readable Layer
Hashes like 95d09f2b are hard to remember. References (refs) are pointers to objects:
- Branches: Moving pointers. `main`, `develop`, `feature/login`. They point to commit objects.
- HEAD: Special pointer indicating current position. Usually points to a branch.
- Tags: Permanent pointers to specific commits. Used for releases (v1.0.0, v2.1.3).
- Remotes: Pointers to commits in remote repositories (origin/main, upstream/develop).
Part Two: The Working Directory, Index, and Repository
Three Levels of Storage
Git maintains three levels of state:
Working Directory: The actual files on disk that you edit. Untracked by git until you stage them.
Index (Staging Area): The `.git/index` file. A binary file listing files and their hashes. You `git add` files here before committing. It's a snapshot of what the next commit will contain.
Repository: The `.git` directory containing objects, refs, and configuration. This is what gets pushed to remote servers.
The Git Workflow
Part Three: Branching and Merging
Branching is Cheap
In git, a branch is just a pointer to a commit. Creating a branch doesn't copy files or create new objects. It just creates a new ref file:
This is why git branching is so lightweight compared to older version control systems like SVN, which physically copied entire directory trees.
Merging: Three-Way Merge
When you merge branches, git performs a three-way merge:
If there's a conflict, git marks it in the file and makes you resolve it manually. Once resolved, you commit a merge commit (with two parents).
Rebase: Replay Commits
Rebasing is an alternative to merging. Instead of creating a merge commit, git replays your commits on top of the target branch:
Rebasing creates a linear history but produces entirely new commits. The replayed commits have the same diffs but different hashes, because both the parent hash and the timestamp change. This makes rebasing dangerous if you've already pushed commits, since it rewrites shared history.
Part Four: Distributed Operation
No Central Server Required
Git is fundamentally distributed. Every repository is complete and independent. You can commit locally without network access. Remotes (like GitHub) are just other repositories that happen to be on a server.
Part Five: The History: Git's Origins and Evolution
Why Linus Created Git
Before 2005, Linux kernel development used BitKeeper, a proprietary tool. When BitKeeper's license changed unfavorably, Linux community needed an alternative. Linus Torvalds, needing to manage thousands of developers and patches, sat down and wrote git in roughly two weeks.
Linus's requirements:
- Very fast (Linux kernel is massive)
- Distributed (no single point of failure)
- Support non-linear development (many branches)
- Able to handle merge conflicts elegantly
He created a simple but powerful design based on content-addressed objects and DAGs. The result was so elegant that it became the standard for open source and eventually enterprise software.
Key Contributors and Evolution
Linus Torvalds (2005): Created git's core architecture and initial implementation.
Junio Hamano (2005-present): Became maintainer after Linus stepped back. Responsible for most of git's development and refinement.
GitHub (2008): Made git accessible to non-Linux developers through a web interface. Popularized git in non-kernel development.
GitLab (2011), Gitea (2016): Alternative implementations showing git's flexibility.
Part Six: Complexity and Performance
Simplicity of Core Concepts
Git's core is remarkably simple. Understanding objects, commits, and refs is achievable in a day or two. The fundamental operations are straightforward data structure manipulations.
Complexity in Implementation
Implementing git efficiently, however, is complex:
- Packing objects: Raw objects are stored individually. Git packs them to save space, using delta compression (storing differences between similar objects rather than full copies).
- Garbage collection: Removing unreachable objects requires careful traversal of the DAG.
- Merge strategies: Three-way merging and conflict resolution involves sophisticated algorithms.
- Performance optimization: Large repositories need careful indexing and caching (git index files, bloom filters for fast searches).
The git codebase (written in C) is roughly 100,000 lines, with most of that devoted to optimizations and edge cases.
Part Seven: Could Git Be Replaced?
Alternatives Exist
Mercurial (hg): Similar design to git. Slightly simpler API. Popular in some communities but less dominant.
Pijul: Newer system based on patch theory rather than snapshots. More sophisticated conflict resolution but less adoption.
Fossil: Designed for decentralized development with integrated bug tracking. Smaller following.
Darcs: Patch-based like Pijul. Older but still used in Haskell communities.
Why Git is Hard to Replace
Despite alternatives, git dominates because:
- Simplicity: Core concepts are easy to understand and reason about.
- Efficiency: Fast for common operations. Scales to massive codebases.
- Ecosystem: GitHub, GitLab, countless tools built on git. Switching costs are enormous.
- Robustness: Immutability and content-addressing make it virtually immune to corruption.
A new version control system would need to be substantially better in practical ways to overcome git's incumbency.
Part Eight: The Philosophy Behind Git
Immutability and Trust
Git's design assumes you don't trust files to stay unmodified. By hashing everything, corruption is detectable. This philosophy differs from earlier systems that assumed "the server is trustworthy."
Decentralization
Git assumes no single point of authority. Every copy is complete. This distributes power and resilience.
History Preservation
Git never forgets. You can recover any commit ever made (unless you explicitly run garbage collection on unreachable commits). The full history is always available locally.
Conclusion: Elegant Simplicity
Git's power comes from elegant simplicity: immutable objects, content-addressed storage, and a DAG of commits. These ideas combine to create a system that is fast, reliable, and distributed.
Understanding git's data structures reveals principles that apply far beyond version control: how immutability enables safety, how content-addressing ensures integrity, and how simple abstractions can scale to manage millions of files across thousands of contributors.
"In many ways, Git's strength comes from its simplicity. It doesn't try to be everything. It does one thing (track changes to files) and does it with elegance. The DAG of immutable objects is so fundamental that you can build anything on top of it."