Basis Lab

How a Repository Remembers

Why a Commit Is a Whole Snapshot and Still Costs Almost Nothing

Last timeNaming Things by Their Contents

A commit records every file in the project, not your edits. Here is the arithmetic that turns 800 MB of apparent snapshots into under 9 MB of real storage.

What a commit object actually holds

The repository used throughout is the one built in the previous step of this

chain, with three files, four commits on its main line and three more on a side

line that branches off the second commit. Open the second commit and read it.

It is not a long read.

FIG 1A commit object and the directory object it names
bash
$ git cat-file -p main~2
tree c1a1e6c0874c143bbdfea8defadeaeac3eb1b0fb
parent 6f74b130bc453e3eac1436ca41795df6c3d5e583
author A Writer <[email protected]> 1767603600 +0000
committer A Writer <[email protected]> 1767603600 +0000

Draw the frame

$ git cat-file -p c1a1e6c
100644 blob 88282c839753300b7e5384ca3304676eee091e49    readme.md
100644 blob 5b6cf77b6348d72977eab587467cbf306a1bc31c    render.py
100644 blob 4a5dd801be5f22ed0834f75ad5691f3af13e83a5    settings.ini

$ git cat-file -s main~2 && git cat-file -s c1a1e6c
221
114
Two hundred and twenty one bytes of commit and a hundred and fourteen bytes of directory record, and between them they pin down the entire state of the project at that moment. Read the commit object line by line and notice what is missing: no file names, no file contents, and no mention of the renderer being the thing that changed. A commit does not know what it changed.

A commit holds four things. One directory name. Zero or more parent names, one

here because this is the second commit on a line. Two author lines, which are a

person and a time, twice over. And a message. That is the whole object, and the

221 bytes it occupies are 221 bytes whether the project holds three files or

thirty thousand, because the project enters the commit only through that single

directory name.

The directory object is where file names live. Each line is a mode, a type, a

twenty byte name and a file name, and the whole record for this project is 114

bytes. A directory holding subdirectories has lines naming those instead, so

the structure is a tree of records whose leaves are files, and each record is

named by a fingerprint of itself in exactly the way a file is.

This is already enough to settle the question in the title. There is no field

in a commit that could hold a difference. The commit names a complete state.

FIG 2What the second commit added to the store
Three arrows leave each directory record, so each one describes a complete project, and yet two of the six arrows land on objects the other record also names. Sharing is not an optimisation layered on top of snapshots here. It is what a snapshot is, once the things in it are named by their contents.

Two commits, one file between them

Put the whole seven commit history on the same footing. Twenty one file

versions appear across it and nine objects hold them, and the breakdown by file

is almost suspiciously even.

The lesson stops here

8 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 Nothingyou are here
  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 Workopening only

Read alongside