Reading — step 1 of 5
Read
Object Format: Inspecting Raw Objects
Last lesson you wrote the object format; this lesson you parse it — the other half of any storage contract. Git's object encoding is minimal enough to fit in one line, and strict enough that every byte matters:
<type> <size>\0<payload>
The four types
blob— file contents (you know this one)tree— a directory listing: names and modes pointing at blobs and other treescommit— a snapshot pointer plus history metadatatag— an annotated label on another object (rare in this course, real in the wild)
Every object in every Git repository on earth is one of these four, encoded exactly as above, named by the SHA-1 of exactly those bytes. The uniformity is the design: one store, one format, four payloads.
Parsing: find the NUL, trust nothing
Given inflated object bytes, the algorithm is short but has teeth:
- Scan for the first
\0byte. Everything before it is the header; everything after is payload. - Split the header at the space:
<type>and<size>as ASCII text. - Validate the type is one of the four known strings.
- Validate the size: parse it as a decimal integer and check it equals the payload's actual byte count. A mismatch means corruption or a bug — real Git refuses the object with
fatal: object corrupt.
The classic implementation mistakes, so you can skip having them: searching for \0 in a string (payloads are binary — trees contain raw 20-byte hashes that can include any byte, including \0; parse on bytes, and only split at the first NUL); assuming the payload is text (blob payloads are whatever the user committed — JPEGs qualify); and off-by-one on the payload slice (the NUL belongs to neither side).
That binary-safety point deserves its own paragraph, because it's the difference between a parser that passes this exercise and one that survives the tree lesson: treat object payloads as bytes end to end. Decode to text only the pieces the format defines as text (the header; commit payloads), only when needed.
Why self-describing storage wins
Notice what the header buys the reader: given a bag of object files with hash names, you can inventory the entire repository — what's a commit, what's a tree, how big everything is — without any index or manifest. Every object carries its own label. When Git prints error: object file .git/objects/ab/cd... is empty, or git fsck audits a repository's health, this parse-and-validate loop is what's running. You're not building a toy version of it; you're building the version — real Git's loose-object reader does precisely these four steps.
Your exercise: Parse an Inflated Object
Given an object's inflated bytes, report its type, declared size, and payload (per the output format in the spec) — and reject malformed input: unknown type, non-numeric size, size/payload mismatch, missing NUL. Write it as the two functions a real implementation wants — parse_header(bytes) → (type, size, payload) and validation around it — because next lesson wraps this exact parser under zlib decompression, and two lessons from now it's the front door to reading trees. This little function is about to become the most-called code in your Git.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…