Open a file you wrote last week and you are looking at a present. The words on the screen are the words that are there now. Yesterday's words are gone unless you saved a copy under another name, and last Tuesday's words are gone unless you saved that too. Two people editing the same file produce, without a system, a last-writer-wins present: whoever saved second erased the other. Version control is the system that refuses this. It produces a reconstructable past of the artifact — every recorded state, who made it, when, and usually why — and a protocol for combining two presents that grew from the same past. That pair is the product. Everything else in this essay is a mechanism for manufacturing it, making it cheaper, or recovering it when the combination fails.

We'll build the product from one file on one machine, add a second person, then a tree of files on a central desk — Subversion and Perforce as two living answers — then two distributed ledgers invented in the same month, Mercurial and Git, and finally the public desks where most combining now happens. Three tools will keep coming back: the ledger, an append-only record you never erase; the letter, a description of difference you can store or send; and the braid, two lines of work that fork and must rejoin. The braid has four software names still in use: a directory copy, a stream, a named field inside a commit, a sliding pointer. The figures are interactive. Drag them as we go. Most of them are built so you can find the point before the sentence that names it.

In this essay a line is a line of text, the native grain of these systems. Sizes are in bytes and in lines. Time is in seconds, days, and calendar years. A revision is one recorded state of a file or of a whole tree.

Part 1One file, many pasts

Before there can be a shared history there has to be a history of one thing. The first version-control systems did not know about projects, trees, or networks. They knew about a file, and about the problem of not drowning in copies of it.

The cost of copies

Suppose you have a source file of 2,000 lines, about 80 kilobytes of C, and you change it every working day. If you keep a full copy of each day's file, 200 working days leave you 16 megabytes of near-duplicates — 200 × 80 kB — and the 201st copy does not tell you what changed. The information you actually want is the difference: usually a few dozen lines. If a typical day's edit rewrites 2 percent of the file, the 200 differences occupy about 200 × 1.6 kB = 320 kB, plus one full copy of 80 kB, or 400 kB against 16 MB. The ledger of change is forty times smaller than the shelf of copies, and unlike the shelf it is a ledger: a sequence of transactions, not a pile of photographs.

That arithmetic is why these systems exist as delta machines rather than as backup tools. A delta is a recipe: insert these lines, delete those. Apply the recipe to yesterday's file and you get today's. Apply it backwards and you get yesterday from today. The letter you would mail to a colleague is the same object as the record you would store.

The idea is older than Unix. Programmers on IBM mainframes in the 1960s already kept card decks and used update utilities to apply a change deck to a master. A 2,000-line program on punched cards is 2,000 cards, one statement each; ten saved versions is a stack you can trip over. Marc Rochkind, twenty-five years old at Bell Labs in 1972, was asked to do something about "source code chaos" in a group writing mainframe programs for telephone companies. He did not know about those earlier tools. Walking a dog one evening in Sayreville, New Jersey, he invented an encoding that would store every version of a file in one place and retrieve any version in a single pass over that place.

Full copies versus deltas, same file, same edit rate. Drag the version count and the typical-edit fraction, and watch the two storage curves. Notice the ledger staying almost flat while the shelf of copies climbs in a straight line — the product is the history, not the photographs.

Play with the figure until the ratio sits still in your head. At 2 percent edits and 200 versions the copies occupy 16 MB and the deltas occupy 400 kB; at 10 percent edits the advantage shrinks but does not vanish. History is cheap when you store the letters instead of the books.

A 2,000-line program as a deck of cards, then as ten decks. Slide the version count and watch the physical stack grow. Notice that the tenth deck is almost the same object as the first — a leftover of copying, not of changing.

A single pass

Rochkind's encoding is called a weave, or an interleaved delta. Imagine the file as a stack of lines, but each line is tagged with the version that inserted it and, if it was later deleted, the version that deleted it. The stored file is not version 17 or version 3. It is every line that ever lived, in the order they appear, wearing those tags. To reconstruct version 7 you read the weave once, from top to bottom, and keep a line if it had been inserted by version 7 or earlier and had not yet been deleted by version 7. One sequential pass. The version you want does not change how much you read.

That is a different design from "store version 1 in full, then a delta to 2, then a delta to 3." In a chain of forward deltas, version 100 is 99 applications away. In a weave, version 100 and version 1 cost the same read. Rochkind proved the reconstruction correct by enumerating the mixed cases of insertion and deletion, implemented it in SNOBOL4 on an IBM System/370 running OS/360, and had it working in days. He rewrote it in C for Unix in 1973. The system was named the Source Code Control System, SCCS. The first paper appeared in IEEE Transactions on Software Engineering in December 1975. A text-format version shipped with the Programmer's Workbench Unix on February 18, 1977.

A weave of a short file: every line tagged with the version that inserted it and, if it died, the version that deleted it. Scrub the target version and watch lines switch on and off in a single downward pass. Notice that jumping from version 2 to version 9 does not skip, rewind, or replay — the pass is the same length either way.

The figure is the algorithm. You did not apply nine deltas. You asked, of each line, whether it was alive at the version you named.

The same weave as a volume: line position along one axis, version along another, presence as depth. Orbit the block, then drag the cutting plane to a version and watch the living file appear as a slice. Notice that a slice at version 1 and a slice at version 12 are the same kind of cut through the same object.

The latest is the whole

SCCS made every version equally cheap to reconstruct. Most programmers, most of the time, want the latest one. Walter Tichy, at Purdue, built the Revision Control System around that observation and published it in 1982, with a fuller account in Software: Practice and Experience in 1985. RCS stores the newest revision as a complete file, and stores older revisions as reverse deltas — recipes that turn a later file into an earlier one. Checking out the tip is a copy. Checking out version 1 of a file with 200 revisions means applying 199 reverse deltas. The common case is a read; the archaeological case is a chain.

Tichy's paper compared the encodings with usage statistics from real trees. Reverse deltas win when the tip is the usual request, which it is: compile this, test this, edit this. They lose when you replay history, which SCCS's weave does not. The two systems are answers to different questions about the same ledger. SCCS is a volume you can slice at any year for the same price. RCS is a photograph of today plus a stack of letters that describe how to get back.

Unix diff, which produces those letters, is itself a research object. James Hunt and Douglas McIlroy published the original algorithm as Bell Labs Computing Science Technical Report 41 in 1976, after a diff had already shipped in Fifth Edition Unix in 1974. Eugene Myers published an O(ND) algorithm in 1986 — N the sum of the lengths, D the size of the shortest edit script — and GNU diff uses a Myers variant. The letter is a shortest path through an edit graph whose horizontal edges are deletions, vertical edges insertions, and diagonals matches.

An RCS file drawn as a full tip plus a chain of reverse letters. Click a version to reconstruct it; the figure applies one reverse delta per step and counts the applications. Notice that version 200 is a copy, and version 1 is a walk of 199 letters — the opposite of the weave's flat cost.
Time to reconstruct a chosen version under weave versus reverse delta, with sliders for history length and which version you asked for. Watch the two curves: the weave is a flat line; reverse delta is cheap at the tip and expensive at the root. Notice the crossover sitting at the tip, which is where Tichy placed the bet.

The lock on the cabinet

A ledger with one writer is a diary. A ledger with two writers is an accounting problem. SCCS and RCS both solved the problem by locking. To edit a file you checked it out with a lock; the system wrote your name on the cabinet; everyone else could read but not write until you checked in. The lock is a physical leftover as much as a protocol: if you go home on Friday with the lock, the cabinet stays closed until Monday, or until an administrator breaks it.

Let's put numbers on contention. Eight people share a module of twelve files. Each person holds a lock on one file for thirty minutes a day. That is four person-hours of lock per eight-hour day, on a cabinet that can grant only one writer per file. If two people need the same file on the same afternoon, one waits. The wait is not a failure of manners. It is the mechanism working as designed: the product — a linear history of that file — is preserved by forbidding the braid.

A shared cabinet of files and a handful of people who need to write. Toggle a lock on a file, then try to grant it to a second person. Notice that the history of that file stays a single strand — and that the queue of waiters is the price of the strand.
Friday, 17:00: someone takes a lock and leaves. Advance the clock through the weekend and watch the cabinet stay closed while work piles up against it. Notice the residue is not a corrupt file. It is a correct history that nobody can extend.

A name inside the text

SCCS embedded a string in the source, the sccsid, which the tools would update on every delta: a version number, a date, a time, who. A compiled program could still be asked what and would print the embedded strings, so a binary in the field could name the exact sources it was built from. RCS did the same with $Id$ keywords. The ledger leaked a little of itself into the artifact, on purpose, because the product is not only reconstructable on the machine that holds the history file. It is identifiable wherever a copy of the program has traveled.

The letter itself has a size, and that size is the other half of Part 1's arithmetic. A delta of 40 lines against an 80 kB file is a short letter. A rewrite of half the file is a long one, and at some point you would have been better off sending the book. Slide the edit fraction in the last figure of this part until the letter and the book meet: that meeting is why binaries, and files that churn completely, sit awkwardly in a delta ledger. The first generation mostly declined to solve that. It was built for text that changes a little.

A source file with an embedded identity string, then the compiled binary. Advance a few deltas and watch the string inside the binary change. Notice that the running program can still name the ledger entry it came from, even after it has left the machine that holds the history file.
Letter versus book: a file of fixed size, a slider for how much of it you rewrote, and two bars — the delta, and a fresh full copy. Notice the meeting point. Past it, the letter is no cheaper than the book, and the ledger's compression argument expires for that edit.

That is the whole of the first generation, stated once. One file, one history file beside it, a lock, a letter of difference, a name stamped into the text. No project, no network, no merge. The next problem is the one the lock refused: two people, two edits, one file, and a history that has to accept both.

Part 2Many hands, one desk

A lock serializes people. Projects do not. The second generation keeps the RCS ledger and changes the rule about who may write: everyone copies, everyone edits, and the system combines the copies. The braid is now allowed. Combining it becomes the product's second half.

Personal copies

Dick Grune, at the Vrije Universiteit in Amsterdam, needed to work with two students on the Amsterdam Compiler Kit's C compiler. One student worked a steady day, the other irregular hours; Grune could work only in the evenings. Their project ran from July 1984 to August 1985. A lock would have meant that whoever was awake owned the file. Grune wrote a set of shell scripts, first called cmt because they let each person commit independently, then renamed Concurrent Versions System. The scripts wrapped RCS. Each person had a personal working copy of every file. You edited yours. When you committed, CVS asked RCS to merge your copy with whatever had landed on the shared desk since you last updated. He posted the scripts on June 23, 1986, to comp.sources.unix.

Brian Berliner, at Prisma, rewrote the scripts in C and added the machinery of releases and vendor sources. Prisma was a third-party shop working on the SunOS kernel, which is to say a group that had to absorb someone else's tree and keep their own changes on top of it. Berliner presented the rewrite at the Winter 1990 USENIX conference in Washington, D.C., as "CVS II: Parallelizing Software Development." On November 19, 1990, CVS 1.0 went to the Free Software Foundation. For the next decade it was the default shared desk of free software, not because it was loved, but because it ran on the network, cost nothing, and let people work at the same time.

The new rule has a name, copy-modify-merge: copy the file without locking it, modify the copy, merge the modified copy with the original. Isolation is the working copy. Combination is the merge. The lock's leftover — a cabinet nobody else can open — is gone. In its place is a conversation the system has to get right.

Two working copies of one file, a shared desk, and a clock. Edit one copy, then the other, then try to commit. Notice that both edits exist until the second commit, and that the desk refuses the second until a merge has named a single new present.

Three versions, one line

A merge needs three inputs, not two. Call the last shared version the common ancestor. Alice's copy and Bob's copy are two presents grown from that ancestor. For each line, the rule is local and almost boring. If Alice and Bob agree, take that. If Alice matches the ancestor and Bob does not, Bob changed it: take Bob. If Bob matches the ancestor and Alice does not, take Alice. If both differ from the ancestor and from each other, the system cannot decide, and it marks a conflict. That is a three-way merge. Two-way comparison — Alice against Bob, no ancestor — cannot tell a change from a preservation. The ancestor is what makes the braid reversible into a single strand.

Let's do one by hand. A twelve-line file. Alice changes line 3. Bob changes line 9. The ancestor still has the old line 3 and the old line 9, so each side's change is unique and the merge is automatic: the new present has Alice's line 3 and Bob's line 9. Now let both change line 3, differently. The ancestor cannot break the tie. The file that comes back contains both versions, wrapped in conflict markers, and a person has to write the line that will actually go in the ledger. The system produced a recoverable shared history up to the point of disagreement. It did not invent a resolution.

Ancestor in the middle, Alice on the left, Bob on the right, result below. Edit any of the three columns and watch the result fill in — automatic where one side matches the ancestor, marked where both diverge. Notice that hiding the ancestor (a two-way comparison) makes every difference look like a fight, including the ones that are not.
A file opened after a colliding merge: the two presents stacked, with the marker lines the tool inserted. Choose Alice, Bob, both, or neither, and commit. Notice that "neither" is a new present — the ledger will record a decision, not a refusal to have one.

The letter that describes a change, line by line, is still Hunt–McIlroy and Myers underneath. CVS asks diff what Alice did, diff what Bob did, and diff3 how to combine them. The edit graph from Part 1 is now a three-body problem.

Two short files as an edit graph: rightward deletes, downward inserts, diagonal matches. Scrub the path the Myers algorithm found, then drag a slider that adds noise to one file and watch D, the edit distance, grow. Notice that similar files make a short path hugging the diagonal, which is why the letter stays small when the braid is gentle.

The letter in the mail

Before CVS had a network server, the letter was the network. Larry Wall posted patch to the source newsgroups in 1985: a program that takes a diff and applies it to a file on someone else's machine. You did not send the book. You sent the recipe, usually as a unified diff. The recipient's tree either accepted the recipe, producing a new present that matched yours, or it rejected a hunk whose context no longer matched — a protocol check on an incomplete or stale message. Whole communities, the Linux kernel among them for years, lived in this pattern: a maintainer, a mailbox, a series of letters, each letter a delta, the ledger implied by the order of application.

A series of 250 letters is not a curiosity. It is a number Linus Torvalds later put on a typical sync with Andrew Morton. If each letter takes 30 seconds to apply and record, 250 × 30 s = 7,500 s, a little over two hours. If a letter in the middle fails to apply, the letters after it sit on a present that does not exist, and you have a protocol failure in the middle of a braid. The mailbox is a shared medium. It congests. It also, unlike a lock, lets the senders keep working.

A short file, a diff beside it as a letter, and a second copy of the file across a gap. Press send; the letter crosses and is applied. Notice that what moved was not the file. It was a description of difference, and the far copy became the near one without a second full copy ever traveling.
A three-letter series applied in order. Corrupt the middle letter — flip a line of context — and watch application stop, with the third letter unapplied on a present that is neither the start nor the end. Toggle a dry-run check that rejects the series before any letter lands. Notice the loop: the context lines are a checksum, and skipping the check is how a corrupt letter becomes a bad history.
A mailbox as a shared medium: letters arriving, a maintainer applying them in order, a congestion slider for arrival rate. Push the rate past the apply rate and watch the queue grow; clear it by applying. Notice that senders never blocked — the residue is delay on the desk, not a locked cabinet.

Commits that don't finish

CVS inherited RCS's grain: the file. A "commit" of forty files is forty RCS check-ins, one after another. If the process dies at file 23, the desk now holds a tree that no person ever built — 23 files at the new revision, 17 at the old. There is no single number that names the attempt. The ledger, at project scale, has a hole in the shape of a crash. This is the most famous defect of the second generation, and it is a defect of grain, not of people. The product was defined per file in 1972. The project arrived later and was assembled out of files.

Berliner also gave CVS a way to absorb an upstream tree without pretending you wrote it: the vendor branch. You import the vendor's drop as a branch, keep your local changes on another, and merge. It is the braid used as a diplomatic protocol. It works until a file is renamed, because RCS identity is the filename, and a rename looks like a delete plus an add with a fresh, empty history. The letter does not know that a.c is b.c. The ledger forgets.

A commit of many files as a sequence of per-file check-ins. Scrub through the sequence and hit stop in the middle — a crash. Notice the tree that results: a present no working copy ever held, with no single revision number that names the attempt.
Upstream drops arriving on one strand, local work on another, merges joining them. Rename a file on the local strand and import the next drop. Notice the history splitting: the new name starts at revision 1.1, and the old name's ledger is a dead end.

A star with one center

By the mid-1990s CVS spoke TCP. A repository lived on a server; working copies lived on laptops; update and commit were the two verbs of a star network. The desk was now a machine. That is a different isolation from the working copy: you can edit on a train, but you cannot record a history until you can reach the desk. The server is also a poll. Clients ask, on a timer or a keystroke, whether the desk has moved. Between polls the working copy is a stale photograph of a ledger that is still being written.

The star made the second generation scale across continents, which is why SourceForge and every other public CVS host of 1999 could exist. It also made the desk a single point of delay, of backup, and of corruption. The product — a recoverable shared history — now had a building, a disk, and a password. Lose any of them and the braid of a hundred working copies cannot reconstruct the missing middle, because the working copies hold presents, not the ledger.

A star: one repository, many working copies, an era slider from local-RCS (everyone on one machine) to CVS-over-TCP (laptops around a server). Disconnect a laptop and try to commit. Notice that edits still happen, and that the ledger does not accept them until the spoke is plugged back in.
A rotating poll: the client asks the server, once per period, whether the desk has moved. Slide the period from 1 s to 5 min; watch another user's commits appear in jumps. Notice that between polls you are looking at the past of the ledger, and that a shorter poll is just a more expensive way to be slightly less wrong.

The second generation produced something the first could not: a history of a project, kept on a desk that many hands could reach, with a braid that usually combined itself and a person who stepped in when it did not. The grain was still the file. The next system keeps the desk and changes the grain.

Part 3Two central desks

If the defect is grain, the repair is to record a tree as one transaction. Two systems did that work on a central desk, in the same years, for different rooms. Subversion was built to feel like CVS and fix it. Perforce was built for shops that already locked files and shipped binaries. Both produce a reconstructable present of a whole tree. They disagree, load-bearingly, about what a branch is.

All or nothing

In early 2000 CollabNet went looking for a CVS replacement. Their collaboration suite used CVS and had inherited its holes. Brian Behlendorf offered Karl Fogel — author of Open Source Development with CVS — a job to write something better. Fogel was already talking through a design with Jim Blandy. They did not want a new model of collaboration. They wanted CVS without the misfeatures: commits that either land whole or not at all, directories that are versioned, copies and renames that keep history, and a single revision number that names a whole tree. After fourteen months, Subversion became self-hosting on August 31, 2001. Version 1.0 shipped on February 23, 2004.

An atomic commit is a transaction against the ledger. Forty files, a directory add, a rename: either the new tree exists or the old one does. There is no file-23 present. The number that names the attempt is global. Revision 1482 is not "foo.c at 1.17"; it is the whole project at the moment of that commit. Unchanged files are not recopied. They are the same nodes, pointed to by the new tree. The photograph is of the forest, cheaply, because most of the trees did not move.

The same forty-file commit as in the CVS crash figure, now as one transaction. Scrub toward completion and cut the power: the tree either stays at N or advances to N+1. Notice there is no mixed present, and that the revision number that appears is a name for the whole attempt.

The working copy, though, is allowed to be mixed. You update src/ and not docs/, and for a while you hold revision 1482 of one directory and 1470 of another. Subversion tracks this on purpose. It is convenient, and it is a new way to be wrong: you can test a present that the ledger never recorded as a single revision. The mixed-revision working copy is a braid inside one person's directory tree.

A working copy whose directories wear different revision numbers. Update one folder, not the other, then try to explain to the figure which revision "the project" is. Notice that the honest answer is a list, and that a commit from this state still produces one new global revision — the mixedness was only on your disk.

Copies for free

Branching in CVS was a per-file operation, slow and easy to get wrong. Blandy's design makes a branch a copy of a directory. The copy is a new name pointing at the same node. It takes constant time and constant extra space, regardless of how many files sit under the node. People did not believe this. Blandy recalled telling Cygnus engineers that cutting a branch would be instantaneous; they said they would believe it when they saw it. They saw it. A tag is the same trick: a name for a tree. The braid, at last, is cheap enough to use for a day's work rather than for a year's release.

Let's put numbers on it. A tree of 100,000 files, each file a node. A CVS-style branch that snapshots every file is 100,000 new identities. A Subversion cheap copy is one new directory entry. The history of every file continues through the copy, because the node did not change. Rename is a copy plus a delete of the old name, and because the node is the identity, a.c really is b.c.

The software does not know the words trunk, branch, or tag. Those are a directory convention: /trunk, /branches/1.0, /tags/1.0.0, three names in one repository, the last two made by copying the first. A branch is a folder. Merging is copying changes between folders. Until Subversion 1.5, shipped 19 June 2008, the desk did not remember which revisions had already crossed. Repeated merges re-applied the same letters and conflicted with themselves. Version 1.5 stored that memory in a versioned property, svn:mergeinfo — a list of paths and revision ranges already absorbed. The braid finally had a ledger of its own combining. The property is famously chatty, and later releases spent years teaching it to speak less.

A Subversion tree with the trunk / branches / tags convention. Press "copy to branches/1.0" and watch a new directory name appear, pointing at the same nodes, cost remaining one entry. Then edit a file on the branch and watch only that file's node fork. Notice that the software never said "branch" — it said "copy."
A file renamed on a branch, then merged back. Toggle "identity is the name" (CVS) versus "identity is the node" (Subversion) and watch the merge either carry history through the new name or treat the rename as a delete plus an unrelated add. Notice that the letter of difference cannot fix a wrong identity; the ledger has to remember which object it was.

Directories remember

Once the tree is the grain, directories have history too: adds, deletes, properties. The repository is a versioned filesystem. Each commit writes a new root. Jim Blandy described it as a virtual filesystem that tracks tree structure over time. Unchanged children are shared pointers. Changed children are new nodes whose parents, all the way to the root, are new as well — a spine of new directories above a mostly-old tree. The photograph is incremental. The address of the photograph is an integer that counts up.

That integer is a different kind of name from RCS's 1.17. It does not tell you which file changed. It tells you which present of the project you mean. "Get revision 1482" is a complete instruction. "Get foo.c 1.17" was never a complete instruction, because the other files might have been anywhere.

A small tree evolving through a handful of commits. Scrub the global revision and watch the root replaced, with unchanged files still pointing at old nodes and a highlight on the spine of directories that had to be rewritten. Notice that the integer and the tree are the same fact, counted two ways.

A second central desk

Five years before Subversion's 1.0, Christopher Seiwald's Perforce Software shipped a commercial desk aimed at the same product by a different door. A Perforce changelist is an atomic set of files, numbered, described, submitted as one. The tree lives in a depot — a path namespace that starts with // — and each person maps a slice of that namespace onto a local workspace with a client spec. You do not check out the world. You map what you need. That mapping is why the same desk can hold a million-file game and still give an artist a folder of textures.

The lock comes back as policy, not as the only writer gate. Text can merge. Binaries usually cannot, and Perforce shops treat exclusive checkout as the default for those files: you open the asset, the desk writes your name on it, someone else sees the lock. Forget it on Friday and the leftover is the same leftover as RCS, now sitting on a texture that blocked a build. Subversion made that lock optional around a merge-first tree. Perforce made the lock ordinary around a binary-heavy tree. Same residue, different default.

Classic Perforce branching is integration between depot paths: //depot/main/... and //depot/rel1.0/... are two trees, and p4 integrate schedules the letters that should flow. The relationship is not in the software until someone draws it. Streams, added later, put the relationship in the spec: a mainline, development streams below it, release streams above it, with a rule the graph enforces — merge down, copy up. Less-stable work merges from its parent to stay current, then copies up when it is ready. The braid is no longer a folder you might forget to merge. It is a typed edge.

One central desk, two products. Toggle Subversion (working copies around a repository) and Perforce (workspaces mapped into a depot). Disconnect the server. Notice that both presents survive offline, and that neither ledger extends until the desk is back — the star is the same shape.
A tree of mergeable text and a few unmergeable binaries. Toggle Subversion's optional lock against Perforce's exclusive open. Commit the text either way; try the binary. Notice that the braid continues around the locked file, and that Perforce's default is the lock Subversion treated as a courtesy.

In 2011 Dan Bloch described Google's Perforce installation as still one server, under a stairwell in Building 43, serving more than twelve thousand users a day, with a lucky engineer having just taken changelist 20,000,000. About 15 million lines a week were changing across about 250,000 files — on the order of rewriting a 2014 Linux kernel, weekly. The desk scaled by mapping, not by cloning. When Google later replaced it with Piper, the migration took years because the workspace-and-changelist shape had grown into every tool around it. Game studios never left. A 40-gigabyte cinematic is a Perforce problem Subversion's line-oriented letter was not hired to solve, and Git's full-tree clone was not eager to download.

The desk as bottleneck

Both stars serialize. You cannot commit on the train. If the desk accepts 2 commits per second and 40 people try to land a change in the same minute, 40 / 2 = 20 s of queue at the peak. Throughput of the product is a property of a machine. Subversion spoke WebDAV to Apache, then svnserve, and moved storage from Berkeley DB to FSFS. It grew a binary delta, svndiff, so a slightly changed image did not cost a full new copy. Perforce treated the large binary as the common case from the start: exclusive opens, lazy workspace sync, integration recorded per file.

Rate in versus rate out on a central desk: changelists or revisions arriving, a serializer in the middle, a queue, landed numbers on the right. Slide arrival rate and capacity. Notice the queue, not the edits, becoming the history's clock when arrivals outrun the desk.
A binary edited slightly — a header changed, pixels not. Toggle full copies against a binary delta, then mark the file as a game asset under exclusive open. Notice that the letter-versus-book meeting from Part 1 is why Perforce shops lock what they cannot merge.

By 2002 the kernel's relationship with CVS was already a closed case: Linus Torvalds would not use it. The incident that belongs here is what a per-file ledger does to a tree the size of a kernel, and why a generation that fixed atomicity still could not be the desk that project would accept. Scrub a reconstructed afternoon of a large CVS commit: files landing in whatever order the client sent, a tagged release that does not name a consistent tree, a rename that severs blame.

A reconstructed afternoon: a multi-file CVS commit in flight, a crash, a tag applied to whatever landed. Scrub the encounter. Notice that the tag, which should be a photograph of a present, is a photograph of a collision — and that this is the defect Subversion's global revision and Perforce's changelist were built not to be able to produce.

The desk in the middle, even an atomic one, has a second ceiling: every combination still happens there. The next generation copies the ledger itself — twice, in the same month, as two answers to the same missing desk.

Part 4Two distributed ledgers

A working copy holds a present. A clone holds a history. Once every machine has the ledger, committing no longer waits for a desk, and combining histories becomes a conversation between ledgers. In April 2005 that conversation was invented twice. Mercurial and Git are siblings, not a sequel. They disagree about what a branch is allowed to forget.

Changesets, not files

BitKeeper, built by Larry McVoy's BitMover in the late 1990s, taught a generation of kernel developers to think in changesets. A changeset is a commit as a first-class object: the whole delta of a tree, with parents, with a name, sendable as a letter. BitKeeper stored history per file in SCCS weaves — McVoy called interleaved deltas a brilliant design — but it spoke to users in changesets, and it was distributed: every developer had a repository, not a working copy waiting on a star. Torvalds started using it for Linux in 2002. The license was proprietary, with a no-cost version for open-source projects and a condition that you would not reverse-engineer the protocol.

Perforce had already numbered the same idea on a central desk: the changelist. Subversion numbered the tree. BitKeeper made the numbered change a letter you could send without the desk. That is the move the fourth generation keeps.

The BitKeeper condition is a protocol about a protocol. In early 2005 Andrew Tridgell wrote a client that could talk to BitKeeper servers by watching the wire. McVoy withdrew the free license. On April 6, 2005, Torvalds told the kernel list he would stop using BitKeeper. On April 7 he committed to a new tool, on the tool itself: e83c516, "Initial revision of 'git', the information manager from hell." Twelve days later, on April 19, Matt Mackall announced Mercurial 0.1 to the same list, with the same vacancy as the cause. Torvalds had looked at Monotone, which already named objects by hash, and found it too slow for a tree the size of the kernel. Two ledgers, two weeks, one missing desk.

The same project change drawn three ways: per-file RCS revisions, a Perforce/Subversion-style numbered changelist or revision, and a sendable changeset with a parent. Toggle. Notice that only the changeset is a letter with a boundary you can mail without the desk.

Two weeks in April

The performance argument was not abstract. Torvalds wrote on April 7, 2005, that if applying and recording a patch took 30 seconds, a 250-letter sync with Andrew Morton would take two hours, and a failure in the middle would be "bad bad bad." BitKeeper had been doing that work in 10–15 seconds per letter. On April 29, Git was measured recording kernel patches at 6.7 per second. 250 / 6.7 ≈ 37 seconds, against 2.08 hours at the 30-second pace. The first merge of multiple Git branches happened on April 18. Kernel 2.6.12 was managed with Git on June 16. Maintainership of Git moved to Junio C. Hamano on July 26, 2005.

Mercurial was not a runner-up. It was a different bet: a small command set, history stored as revlogs — append-only files of deltas with an index, closer to a weave's temperament than to a bag of hashed objects — and no staging area. You commit the working copy. Mozilla ran Firefox on it for nearly two decades. OpenJDK used it. Facebook scaled it until the client became Sapling. The kernel chose Git for local reasons: speed on that tree, the data model Torvalds already had in his head, and twelve days of head start. Parallel invention is a braid. One strand became the default of the forge. The other became the default of several of the largest trees that were not the kernel.

April 6–29, 2005, as two parallel strands: Git's first commits and Mercurial 0.1 on the same vacancy. Drag the date. Notice that Mercurial is not a sequel. It is a sibling clock, and the kernel's choice was local, not a verdict on revlogs.
Two hundred fifty letters applied to a kernel-sized tree. Slide seconds-per-letter from 30 down through 10 to 0.15 (6.7 per second) and watch a two-hour bar collapse toward half a minute. Toggle a mid-series failure. Notice that the disaster Torvalds named is a protocol failure in a long letter-chain, and that speed is how you make the chain short enough to retry.

Revlogs and objects

Git's storage is a content-addressed filesystem, an object store. A blob is the bytes of a file, named by the hash of a small header plus those bytes. A tree is a directory listing named by its hash. A commit is a tree, parent hashes, author, message, named by its hash. The name is the content. Duplicate files collapse to one blob. A packfile later stores some objects as deltas against similar neighbors, not necessarily against their parents.

A Mercurial revlog is one file per tracked path, plus a changelog and a manifest, each an append-only sequence of revisions. New data is written at the end. Old revisions stay put. The index says where version 17 begins. Retrieval is closer to SCCS's single-pass temperament than to Git's "hash, then maybe pack." Both compress. They compress different graphs. Git asks "have I seen these bytes?" Mercurial asks "what is the next delta on this file?" Facebook's later scaling work — virtual filesystems, a server-side monorepo, a client that still spoke Mercurial verbs — started from the revlog, not from the object store.

The hash Git used in 2005 was SHA-1: 160 bits, 40 hexadecimal characters. The space has 2160 ≈ 1.46 × 1048 names. A chosen-prefix collision against SHA-1 was demonstrated in public on February 23, 2017. Mercurial used SHA-1 in its nodeids too. Both systems grew newer hashes. The mechanism is the same: if you have the name, you can check that what came back hashes to what you asked for.

A small Git repository as a volume of objects: blobs at the bottom, trees in the middle, commits along a spine. Orbit, then slice by type. Notice that two files with the same bytes are one blob, and that the commit does not contain the files — it contains the name of a tree.
Type a few bytes; watch a header prepended and the hash appear. Flip one byte in the payload and watch the name change completely. Then ask a second peer for that hash and have it return the wrong bytes: the check fails. Notice that the protocol does not trust the sender. It trusts the name.
Toggle Git packfile against Mercurial revlog. On the pack side a sliding window finds similar objects anywhere; on the revlog side each file appends a delta to its own history. Notice that both store letters, and that they index different questions: "seen these bytes?" versus "next revision of this path?"
A fetch in flight: objects advertised, sent, hashed on receipt. Flip a bit in one object on the wire. Watch the receiver refuse the pack and keep its ledger where it was. Disable the hash check and watch a corrupt blob land under an honest name. Notice the loop is the same as the patch context from Part 2, moved onto the name of every object.

On April 16, 2005, nine days after Git hosted itself, Torvalds imported Linux 2.6.12-rc2 as commit 1da177e: 17,291 files, 6,718,755 insertions. That is about 388 lines per file if you average blindly, and 6.7 million lines of present in one letter. He did not import the earlier history. The product, on that Saturday, was a reconstructable past that began at 2.6.12-rc2, on purpose.

Four names for a strand

Git's branch is a movable name for a commit. The name lives outside the object. Delete the name and the commits remain until garbage collection, if nothing else points at them. Two names on the same commit is cheap. Checking out a commit without a name is the "detached HEAD" that Git warns about, because the next commit might become hard to find.

Mercurial baked a different name into the changeset itself: the named branch. Every commit records which branch it was made on. The default name is default. The name never moves; new commits on that branch add to it. That is useful for a long-lived release line you want to audit in ten years, and noisy if every feature gets a permanent name. Mozilla's Firefox trees mostly refused named branches for features. They used separate repositories as channels — mozilla-central, beta, release — and later a unified repository whose bookmarks pointed at each channel's head. A bookmark is Mercurial's Git-like name: a pointer that slides, not a field inside the commit. Mercurial also lets you commit without a bookmark at all. Heads do not need labels. Git heads do, or they risk being swept.

Phases are Mercurial's rewrite rule. Draft commits may be rebased; public commits may not. Publishing is a one-way door. Git's equivalent is social — a protected branch on a forge, a refusal to force-push — not a property of the object. The two systems produce the same product. They hide the braid in different places: Git in a file of names, Mercurial in the changeset and, if you want the Git workflow, in a bookmark beside it.

The same fork under four namings: a Git pointer that slides, a Mercurial named branch baked into commits, a Mercurial bookmark that slides, a Subversion directory copy. Commit on both strands, then merge. Notice what deleting the name does — Git may hide the strand, Mercurial's named branch cannot forget it, a bookmark can vanish without hiding commits.

This is why distributed ledgers do not need a serializer in the middle of every commit. Alice commits locally; Bob commits locally; their graphs diverge; later a letter moves, and one of them ties the braid. The star is optional. It is a convenience for publishing, not a condition for recording.

Letters at scale

A clone copies the ledger. A fetch copies new objects from a remote. A pull fetches and then ties the braid. A push offers your new objects to another ledger, which accepts them if they fast-forward its names, or refuses if they would silently drop a strand. Mercurial's verbs are the same idea with fewer nouns: hg pull, hg push, hg merge. The refusal is the product protecting itself. Force-pushing is how Git overrides the protection. Mercurial's phases try to make the override unnecessary: once public, the strand does not slide out from under someone else.

The leftover of a Git force-push is a commit that still exists, often, in a reflog — a local diary of where a name used to point, kept for weeks. It is an orphaned commit until garbage collection sweeps it. Residue, again: a past the desk no longer names but the machine has not forgotten.

Signing a commit puts a cryptographic identity inside the object. Verifying it is another checksum, on the person rather than on the bytes. At the object store the defenses are already two deep: the hash names the content, the signature names the author of that name.

A server, a clone (full ledger), and a CVS-style working copy (present only). Disconnect the network and commit on each. Notice that the clone extends its history, and the working copy can only dirty its files. Reconnect: one side has letters to send, the other has an unrecorded present.
A short DAG with optional signatures on commits. Peel the signature layer off and a swapped-author commit lands undetected. Leave it on and the same swap fails verification. Notice that the hash still agrees — the bytes are the bytes — and that the new layer answers who named the object, not whether the object is itself.
A branch name moved by a force-push; the old tip orphaned. Advance a 30-day clock and watch the reflog still hold the old hash, then expire. Notice the leftover: a recoverable past that is no longer shared, decaying on a timer rather than by being overwritten.

The fourth generation produced the product in a form that can leave a building. What it did not produce, by itself, is a place everyone agrees to send to — or a single answer to what a branch is. Those answers still live side by side: a Subversion folder, a Perforce stream, a Mercurial name inside a commit, a Git pointer on the side.

Part 5Four kinds of branch

A distributed ledger still needs a shared present — not a single server that records every commit, but a name everyone uses when they mean the project. The forge is that name for Git and, for a while, for Mercurial. Subversion and Perforce never needed it in the same way: the desk already had a name. The interesting disagreement in 2026 is not which website wins. It is that four living systems still mean four different things by the word branch.

The forge

GitHub opened to the public on April 10, 2008, with the line "Social Code Hosting." Tom Preston-Werner, Chris Wanstrath, and PJ Hyett built it for a Ruby community that had already started to like Git. Rails moved over near launch. Bitbucket opened the same year for Mercurial; it added Git in 2011, a date that is easy to read as a verdict. SourceForge and Google Code had already been Subversion desks. The star came back as a convention. You can still clone from anyone. In practice you clone from the named URL, because that is where the names are.

A pull request is a letter with a waiting room. You push a strand, ask the named desk to tie it, and other people write on the letter before anyone merges. Bitbucket did the same for Mercurial. Subversion and Perforce shops more often review a changelist or a revision still sitting on the central desk. The three-way merge still runs. What the forge added is a protocol around it: review, status checks, a button. Rejecting the request leaves your strand intact. That is the product being careful in public.

The same developers under three hosting eras: 2005 maintainer mesh, 2008 Bitbucket-for-Hg and GitHub-for-Git, 2011 onward a Git star. Slide the year. Notice Mercurial's forge arriving with Git's, and Git's star thickening after Bitbucket added Git in 2011 — the objects did not change. The default URL did.
A pull-request protocol: open, review, a failing check, a fixup commit, a merge. Corrupt the diff in transit — a rewritten history on the branch — and watch the review conversation point at lines that no longer exist. Notice the loop catching an incomplete letter only if the check reruns on the new tip.

How a branch is a branch

Put the four namings on one table, because they are the through-line this essay has been building.

Subversion: a branch is a cheap copy of a directory. The convention /trunk, /branches, /tags is social. Merge tracking, after 1.5, is a property on the copy. Delete the directory and the copy is gone from the tree, though old revisions still hold it.

Perforce: a classic branch is two depot paths with integration history between them. A stream is a path plus a parent plus a type. Merge down, copy up. The graph tells you where the letter is supposed to go. Exclusive opens keep unmergeable files from growing a braid they cannot combine.

Mercurial: a named branch is a field inside the changeset, permanent, auditable, easy to clutter. A bookmark is a sliding pointer, closer to Git, optional. Phases stop you rewriting what you have already published. Firefox treated whole repositories as channels, then bookmarks in a unified repo, and in 2023 announced a move of the source of truth to Git, with the developer-facing cutover expected 30 April 2025 — not because revlogs failed, but because two SCMs had become a burden.

Git: a branch is a file containing a hash. Cheap to make, cheap to delete, silent about which strand a commit was born on unless you keep the name. The forge's protected branch is how that silence gets policy.

None of these is a wrong braid. They are different prices for the same product: a reconstructable past, and a way to combine pasts that diverged. The directory copy is easy to explain and hard to merge until mergeinfo. The stream graph is policy in the software. The named branch remembers too much for feature work. The pointer remembers too little for archaeology unless the forge keeps the names.

Let's put that search in units. Fourteen checkouts to find one failing change among 16,384 is why bisect is a reason to keep history linear enough to search. A braid with merge commits still searches, with more care. A history that was rewritten to hide the failure cannot. Recoverability is not a feeling. It is an operation with a step count.

The installed base

Tools do not replace each other the way papers do. They replace each other the way kitchens do. Subversion remains the desk of places that wanted a star on purpose — a single policy, a single backup, no force-push. Perforce remains the desk of game studios and other binary-heavy shops, and it was Google's desk for eleven years before Piper. Mercurial remains in the tools Facebook grew out of it, and in trees that have not finished a Git migration. Git is the default of the forge, which is the default of the course, which is the default of the next project.

A stock-and-flow of that installed base is the honest picture of 2026. Inflow: new repositories, almost all Git, almost all born on a forge. Stock: decades of ledgers in every encoding from this essay, including Perforce depots that never showed up in public clone counts. Outflow: conversions that are lossy in the ways Parts 2 and 3 named. The product is only as reconstructable as the encoding you still have a reader for.

A stock-and-flow of repositories by encoding, 1985–2026: SCCS/RCS, CVS, Subversion, Perforce, Mercurial, Git. Slide the year; watch Git's public inflow dominate after 2008 without the other stocks going to zero. Notice Perforce's band: quieter in public clone counts, stubborn in studios and in Google's history.

Some artifacts outgrew the working-copy-plus-clone assumption. Google's Piper and other monorepo systems keep a tree so large that cloning it is the expensive joke, and the working copy is a sparse view onto a desk that is, again, central — the Perforce mapping idea with different internals. Git grew shallow clones, sparse checkouts, and partial clones so that a present can be fetched without the whole past. The fourth generation, at the limit of size, reimports a little of the third.

A huge tree and three ways to take it: full clone, shallow Git clone, Perforce-style mapped workspace. Slide tree size and watch bytes-to-present. Notice the funnel: as the tree grows, the reconstructable past is the first thing people stop fetching, which is why the central desk never left the largest trees.

What still fails

The combining still fails in the ways Part 2 taught. Two edits to the same line still need a person. A Git rebase still rewrites names. Mercurial phases refuse the same rewrite once a commit is public. A force-push still drops a shared present. The defenses stack, and they peel: hash checks, signed commits, protected branches, required reviews, exclusive opens, a reflog on the machine that did the damage. No layer is the product. Each layer is a way the product survives a particular lie.

A stack of filters: object hash, signature, protected branch, review, tests, reflog. Send a hostile or merely mistaken change through, then peel layers off one by one. Notice that any single layer almost always suffices for its own lie, and that the change which lands is the one that found a hole in every layer you left on.
A published strand rewritten two ways. On Git, a rebase plus force-push moves the shared name and leaves the other clone holding old hashes. Toggle Mercurial phases: once public, the rewrite is refused. Notice that both systems can protect the braid — Git by social policy on a name, Mercurial by a property of the commit.

The whole path, from a keystroke to a reconstructable past other people can fetch, is now long enough to scrub as one motion: edit, stage if you are on Git (Mercurial skips that noun), commit, push or submit, review, merge, fetch, checkout. Each step is a mechanism from an earlier part.

End to end: a line edited in a working copy, a letter formed, a commit or changelist named, a push or submit to a named desk, a request, a merge, a present updated on a second machine. Scrub forward and back. Notice which steps need the network, which need a person, and which are local ledger arithmetic.

The last figure is a small desk you can lose. Two people, one file, the rules this essay has named. You may lock as Perforce often does, or copy-modify-merge as Subversion and the distributed systems do. You may commit atomically or per file. You may push to a shared name or only keep a local ledger. The ways to lose are the ways the product fails.

A playable two-person repository. Choose locking or merging, atomic or per-file commits, a shared desk or two ledgers. Make a day's edits. You can lose: a stranded lock, an inconsistent tree, a dropped branch, a present that overwrote the other. Notice that every loss maps onto a part of this essay, and that the undo — when you still have one — is the recoverable past.
A crashed writer leaving a lock file behind — Git's index.lock, or Friday's Perforce exclusive open. Try to commit; the cabinet refuses. Remove the leftover and the present is writable again. Notice the family resemblance: the history was fine. The residue of a lockout is a claim that someone is writing, written by someone who is not.

Final words

The system produces a recoverable shared history of a changing text, and a way to combine histories that diverged. It learned to do the first half by storing letters instead of books, then by photographing whole trees at once, then by naming photographs after bytes or appending them to a revlog. It learned to do the second half by forbidding it with a lock, then by allowing it with a three-way merge, then by making the braid a directory copy, a stream edge, a named field, or a sliding pointer.

None of those steps retired the earlier ones. Subversion, Perforce, and Mercurial are not prehistory. They are still how a large fraction of the world's trees remember themselves. A delta is still a letter. A working copy is still a present. A merge is still three versions of the same line. If you scroll back, the figures are still there, and they still do the arithmetic. The product is what they produce when they work, and what they try to give back when they do not.

Sources

Primary papers and contemporaneous documents