Step 1 of 5 · Reading · ~3 min
Read
Object Storage
Object Format: Reading and Validating Raw Objects
Last lesson you wrote the front of every git object. This lesson is the other half of any storage contract: reading one back and refusing it when it is wrong. After it, your program takes the bytes of an object, tells you its type and size, and rejects anything that real git would call corrupt.
One format, four types
Every object in every repository is stored as
<type> SP <size in decimal> NUL <payload>
where SP is one space byte (0x20) and NUL is one zero byte (0x00). The type is one of four lowercase words:
| Type | Payload |
|---|---|
blob | file bytes, whatever the user committed (a JPEG qualifies) |
tree | a directory listing with raw binary hashes inside |
commit | text: tree, parents, author, committer, message |
tag | text: an annotated label on another object |
Git names an object by hashing exactly these bytes, so a parser that is slightly forgiving accepts objects that real git would never have written.
Parsing: trust nothing, find the first NUL
Here is the whole parse on a small object, shown as hex with the fields labelled:
74 72 65 65 20 30 00 tree SP 0 NUL (empty directory, payload is 0 bytes)
62 6c 6f 62 20 33 00 61 62 63 blob SP 3 NUL "abc"
The steps, in an order that matters because each error message names the first thing wrong:
- Find the first zero byte. If there is none, the object has no header end.
- Everything before it is the header and everything after it is the payload. The zero byte belongs to neither.
- The header must hold exactly one space, giving a type word and a size word.
- The type must be one of the four words, spelled exactly.
Blobis not a type. - The size must be plain decimal digits with no sign, no spaces and no leading zeros (
0alone is fine).05and+5are corrupt. - The declared size must equal the number of payload bytes. Real git refuses a mismatch with
object corrupt.
Why "first NUL" and "bytes" are not details
A tree's payload contains raw 20-byte hashes, and any of those 20 bytes may be zero or not valid text. A blob can contain zero bytes anywhere. A parser that splits on every zero byte, or decodes the object as text before finding the header, will pass the easy cases and break on a real repository. Treat the whole object as bytes. Only the header is text, and only because the format says so.
Common mistakes
- Splitting at the last zero byte, or at every zero byte, instead of the first.
- Comparing the declared size to the whole object length (header included) instead of the payload length.
- Accepting
blob 05\0helloorblob +5\0hellobecause the language's integer parser forgives them. - Reading the object as a string. A tree hash byte of
0xffis not valid UTF-8, and some languages will fail or alter it silently. - Checking the size before the type, or the type before the header shape. The error you print depends on the order.
Your exercise
Each input line is the hex encoding of one inflated object. For every line print either <type> <size> or one of the ERR lines listed in the exercise, in the order of the six steps above. The starter reads and decodes the hex for you and calls check with the raw bytes. You write check. The test objects come from real repositories, including a tree whose hash contains a zero byte, so a text-based parser will not survive them.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…