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.

Blob (file.txt): SHA-1("hello world\n") = 95d09f2b10159347eece98399c432b1c007bff64 This hash IS the object's identity. Tree: 40000 tree abc123... .gitignore 100644 blob 95d09f... file.txt SHA-1(tree contents) = def456... Commit: tree def456... author Alice <alice@example.com> 1234567890 message "Initial commit" SHA-1(commit metadata) = 789abc...

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.

Initial commit: commit abc123 tree tree1 Next commit: commit def456 tree tree2 parent abc123 Creates a chain: abc123 --parent--> def456 --parent--> ghi789 Branch is just a pointer: main -> ghi789 feature -> def456 (behind main) When merging, create a commit with TWO parents: commit xyz999 parent ghi789 (main) parent jkl000 (feature)

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).
$ cat .git/refs/heads/main abc123def456... (the commit hash this branch points to) $ cat .git/HEAD ref: refs/heads/main (we're on the main branch) $ git symbolic-ref HEAD refs/heads/main (human-readable form)

Part Two: The Working Directory, Index, and Repository

Three Levels of Storage

Git maintains three levels of state:

Working Directory (what you edit) ↓ (git add) Index / Staging Area (what's staged) ↓ (git commit) Repository (.git folder with objects)

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

1. Edit file.txt in working directory 2. git add file.txt - Hash the file content - Create a blob object in .git/objects - Update index to reference this blob 3. git commit -m "message" - Create a tree object referencing current index - Create a commit object pointing to tree and parent commits - Move branch pointer to new commit 4. git push origin main - Send commit objects to remote - Remote updates its refs/heads/main pointer

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:

$ git branch feature # Creates .git/refs/heads/feature pointing to current commit $ git switch feature # Changes HEAD to point to refs/heads/feature

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:

Common ancestor commit: file.txt = "hello" Feature branch commit: file.txt = "hello world" Main branch commit: file.txt = "hello\nworld" Three-way merge: - Look at ancestor, feature, and main versions - If only one side changed, use that change - If both sides changed differently, CONFLICT - If both sides made same change, no conflict

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:

Before rebase: main: a -- b -- c feature: d -- e After `git rebase main` on feature: main: a -- b -- c feature: d' -- e' (d' and e' are new commits, replayed on top of c)

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.

Local repository: .git/objects/ ab/c123... (blob) de/f456... (tree) gh/i789... (commit) refs/ heads/main remotes/origin/main Remote repository (on GitHub): same structure git push: - Sends your commits' objects to remote - Updates remote's refs git fetch: - Gets remote's objects - Updates your remote tracking refs (origin/main) git pull: - git fetch + git merge

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."