Basis Lab

How a Repository Remembers

Why Your Push Was Rejected, and the One Push That Destroys Work

Last timeNothing Is Lost Yet

A second copy is another object store with the same names and different labels, which makes a transfer a set difference and a rejection a refusal to drop a commit.

A copy is just another store

Take the repository worked through so far, with its three files, its main line

of four commits 6f74b13, 5c79a65, 2c74519 and a2f7109, and its side line of

three leaving the second of those and ending at f512f47. Copy it to another

machine and look at what arrived.

Twenty-three objects, which is seven commits, seven directory listings and nine

file contents, and every one of them under exactly the name it had in the

original. The commit at the tip of the main line is a2f7109 on both machines.

The contents of readme.md at that commit are 33769db on both machines. Nothing

was negotiated to make that true. The name is a fingerprint of the contents, so

two machines that have never exchanged a message will file identical contents

under an identical name, and would do so even if neither knew the other existed.

This is worth stating as the whole content of what a second copy is, because it

removes a question people expect to be hard. The two stores cannot disagree

about content. If both hold a2f7109, both hold the same bytes, and no comparison

is needed to establish it. What the two copies genuinely have of their own is

their labels. Where main points, where the side branch points, what local

branches exist that the other has never heard of: all of that is a handful of

small files holding names, and all of it is private to one copy until something

moves it.

FIG 1Three stores holding the same objects under different labels
Everything that moves along the arrows is objects and then one label value. Because the objects are already named identically on both sides, the only decision anyone has to make is the one at the bottom, which is about labels.

What a transfer has to move

Now the cheapness, derived rather than asserted. When a copy asks another for

what it does not have, the exchange begins with names. The asking side says

which names it already holds; the sending side works out what is reachable from

the label being asked for, subtracts what the asker said it has, and sends the

difference.

FIG 2The set of objects a transfer actually sends
the name the asking side already holds for this label, from its last exchange
the name the label holds on the other side now
every object reachable from x, which is x, its listings, its file contents and all of its ancestors
the objects actually put on the wire
Read what is absent from this. There is no comparison of file contents anywhere in it, because equal names already mean equal bytes, so the whole calculation is set arithmetic over forty character strings. That is why the cost of a transfer tracks what changed rather than how large the project is.

Work it on the toy repository first, where the numbers are small enough to

check by hand. Ben commits once, changing settings.ini, which produces one new

commit, one new directory listing and one new version of one file, so three new

objects, taking the shared store from twenty-three to twenty-six. Ada then

fetches. She already holds twenty-three of those twenty-six, so the difference

is three objects out of twenty-six, which is under one eighth of a store that

was already tiny.

Then scale it with the project fixed earlier in this chain: two hundred files

totalling 8 MB, with a hundred commits that each change one line of one 50 KB

file, which packs to under 9 MB. Cloning that moves the pack once, under 9 MB.

Three more commits of the same shape then add three file versions at 50 KB

each, which is 150 KB, three directory listings of two hundred entries at about

30 bytes an entry, which is 6 KB each and 18 KB in all, and three commits of

about 250 bytes, which is under 1 KB. The transfer is 150 plus 18 plus 1, so

169 KB against a project of 8192 KB.

The lesson stops here

5 more paragraphs to go

You have read the opening. The rest of the argument, the problems that check whether it landed, and the lines worth keeping at the end all come with a plan.

The first lesson of every course in the library reads the whole way through, free, so you can see exactly what the rest of them are.

See the planThe contents

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

The rest of this course

  1. 01What a Git Hash Is Actually a Hash Of
  2. 02Why a Commit Is a Whole Snapshot and Still Costs Almost Nothingopening only
  3. 03Every Commit Names Its Parent, and a Branch Is One Line in a Fileopening only
  4. 04Why the Same File Is Reported as Both Staged and Not Stagedopening only
  5. 05Why a Merge Only Stops to Ask About Some of Your Changesopening only
  6. 06Why Your Commits Get New Names When You Rebaseopening only
  7. 07A Hard Reset Deletes Nothing, and You Have About Thirty Daysopening only
  8. 08Why Your Push Was Rejected, and the One Push That Destroys Workyou are here

Read alongside