Skip to content
Lesson 2 of 18

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:

TypePayload
blobfile bytes, whatever the user committed (a JPEG qualifies)
treea directory listing with raw binary hashes inside
committext: tree, parents, author, committer, message
tagtext: 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:

  1. Find the first zero byte. If there is none, the object has no header end.
  2. Everything before it is the header and everything after it is the payload. The zero byte belongs to neither.
  3. The header must hold exactly one space, giving a type word and a size word.
  4. The type must be one of the four words, spelled exactly. Blob is not a type.
  5. The size must be plain decimal digits with no sign, no spaces and no leading zeros (0 alone is fine). 05 and +5 are corrupt.
  6. 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\0hello or blob +5\0hello because the language's integer parser forgives them.
  • Reading the object as a string. A tree hash byte of 0xff is 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.

Up nextCompressed On-Disk StorageObject Storage

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…