Charts · 22

Your commit is not gone

git reset --hard rewrote one 40-byte file and your afternoon appeared to vanish. It did not: the commits are sitting in the object database with nothing pointing at them, which is a different thing from deleted. Step through nine commands and watch the graph. Commits are immutable objects, branches are names that point at one, and the shape of the graph is the only real difference between a merge and a rebase.

git log --oneline
a1b2c3dAdd the reading listAdd the reading listb2c3d4eExtract the card componentExtract the card comp…c3d4e5fFix the empty stateFix the empty statemainHEAD

Three commits on main. A commit is an immutable object: a snapshot, its parents, and a hash of all of it. main is a file containing one commit id. HEAD is a file containing the word main.

git reflog

c3d4e5f HEAD@{0}: commit: Fix the empty state

Notes

A commit object holds a tree hash, its parent hashes, author and committer, and a message; its id is the SHA-1 (SHA-256 in newer repositories) of exactly that content, so changing any part of it, including a parent, produces a different commit. A branch is a file under .git/refs/heads/ containing one id, and HEAD is a file containing a symbolic ref to a branch, or an id directly when detached. That is why reset, switch and a fast-forward merge are all fast whatever the size of the history: they rewrite a name. Rebase replays each commit’s diff onto a new base and writes new objects, so a rebased commit is not the same commit; that is the whole reason a force-push after a rebase conflicts with everyone else’s copy. The reflog (.git/logs/HEAD and per-branch logs) records every value each ref has held for 90 days by default (gc.reflogExpire), which is what makes almost every unreachable commit recoverable; unreachable objects are kept for two weeks (gc.pruneExpire) and git fsck --lost-found lists them. The one thing that really does drop work is a reset --hard over changes that were never committed, because those were never objects in the first place. Sources: Chacon and Straub, Pro Git, chapter 10 (Git Internals); git-reflog, git-rebase and git-gc manual pages.