Reading — step 1 of 5
Read
Commit Objects
A tree hash pins a complete project state — but states alone aren't history. The commit object adds the two missing dimensions: where this state came from (parent commits) and who/when/why (metadata and message). It's the smallest of Git's formats — plain text! — and the one that makes the whole thing a version control system.
The format: finally, text
After binary trees, commits are a relief — a text payload, hashed and stored like every other object:
tree 8f2e5b0e6d0c8f6a1b8e9d3c4a5f6e7d8c9b0a1f
parent 3ac2e9b1d5f8c7a6e4d3b2a1c9f8e7d6c5b4a3f2
author Ada Lovelace <[email protected]> 1720900000 +0600
committer Ada Lovelace <[email protected]> 1720900000 +0600
Add RESP parser with tests
Line by line:
tree— exactly one; the snapshot this commit represents. The commit does not contain changes. Read that again: Git commits are snapshots, not diffs — the tree is the entire state, and "what changed" is always computed by comparing two commits' trees (the diff lessons ahead).parent— zero or more: none for the very first (root) commit; one for a normal commit; two or more for merges. Parents make commits a linked structure — history is these links, nothing else.authorandcommitter— usually identical, meaningfully distinct: the author wrote the change, the committer created this commit (they diverge under cherry-pick, rebase, and patches-by-email — Git's mailing-list heritage showing). Format: name, email in angle brackets, Unix timestamp, timezone offset.- Blank line, then the message — everything after the first empty line, verbatim.
Wrap in commit <size>\0, SHA-1, store. Same machinery, fourth verse.
The hash chain — accidental blockchain
Here's the consequence hiding in that little parent line. A commit's hash covers its entire text — which includes its parent's hash — which covered its parent's hash — all the way to the root. So one 40-character commit hash pins:
- the complete project state (via the tree hash),
- and the complete history behind it (via the parent chain).
Tamper with any commit anywhere in history and its hash changes, which changes its child's text (the parent line), which changes the child's hash… every descendant re-hashes. This is why commit hashes are trustworthy identifiers across machines, why "rewriting history" (rebase) necessarily creates new commits rather than editing old ones, and why your CI can pin a build to a SHA and sleep at night. Git had an immutable hash-linked ledger a few years before that structure got a hype cycle.
One practical corollary for your implementation: timestamps are inside the hashed bytes. The same tree, same parent, same message committed one second apart yields different commit hashes — determinism in tests requires fixing the timestamp, which is exactly why the grader supplies one.
Your exercise: Hash a Commit Object
Given tree, parent(s), author/committer info, and a message, produce the commit's hash. It's a formatting-precision exercise, and the bytes are unforgiving: single spaces between fields, < > around emails, timestamp then timezone, parent lines in the given order (one per parent, omitted entirely for a root commit), exactly one blank line before the message, and the message as given (don't add or strip trailing newlines the spec doesn't show). When your hash matches, you've reproduced the exact bytes git commit would write — and git log, which you build next lesson, is just following the parent lines you formatted here.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…