Basis Lab

How a Repository Remembers

Why Your Commits Get New Names When You Rebase

Last timeJoining Two Histories

A commit's name is computed over its parent as much as over its files, so replaying history cannot edit a commit. It writes a new one and leaves the old one behind.

What a commit's name is computed over

The repository worked here holds three files and thirteen commits. Seven of

them are on the main line, ending at fbdccf5. Three more are a side line named

proofs, which parted from the main line at its fifth commit, ea21ea3, and ends

at 3f7bf69, and the last three sit on an older side line nothing on this page

touches. All of these are the first seven characters of real names,

and they are reproducible: a name is computed from the thing it names, so

building the same files with the same messages, the same author and the same

dates produces the same names on anybody's machine.

Start by reading a commit out rather than describing it, because every

consequence on this page is sitting in plain sight in it.

FIG 1A commit object, printed exactly as it is stored
plaintext
tree fbfeaf2d9f911b5f562dbef5a83aa71b34701076
parent fa354297f7f995affe6091bcc4600074acd62776
author A Writer <[email protected]> 1770541200 +0000
committer A Writer <[email protected]> 1770541200 +0000

Point the readme at the proof settings
This is the whole of commit 3f7bf69, and its name is computed over exactly these bytes. Five things are in it: the tree, which names the files, the parent, which names the commit before it, two identity lines each carrying a timestamp as seconds since 1970, and the message. The first line is the only one that has anything to do with your files, and the second line is the one that makes rewriting history interesting.

The name of that commit is the fingerprint of those bytes. So there are five

separate ways to arrive at a different name, and only one of them involves

editing a file. Change the tree and the name changes. Change the parent and the

name changes. Change the author line, change the committer line, change a

single character of the message, and the name changes.

Read that as an impossibility result, because that is what it is. There is no

operation anywhere that edits a commit, and there could not be one, because the

name is a function of the bytes and the bytes are the commit. Anything called

rewriting history is therefore writing a second commit and pointing a name at

it instead. Every surprise in this subject follows from that one sentence.

Amending writes a second commit

The smallest case is amending the last commit, and it is worth running twice,

because the two runs disagree in a way that settles what the name is a name

for.

The lesson stops here

6 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 Rebaseyou are here
  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