Git Packfile Inspector (.pack / .idx)
Read a .pack and .idx pair in the browser: every object with its type, size, offset, delta depth and base, totals by type and the biggest objects.
How to use
- Find the pair in .git/objects/pack/ — two files that share a name, pack-<hash>.pack and pack-<hash>.idx.
- Drop both onto the box above, or select them together; they are paired by base name and read inside this tab.
- Start with the summary: the object count, how much of the pack is deltas, the deepest chain, and whether the checksum stored in the .idx matches the one at the end of the .pack.
- Use "Objects by type" and "Largest objects" to see where the space went. Copy an object id from the table and run git cat-file -p on it back in the repository to see what it is.
- If the pack is too large to hand a browser, drop the .idx alone: the ids, CRC32s and offsets are all in the index, at roughly 26 bytes per object.
About this tool
ShowHide
A packfile is how git keeps thousands of objects in one file, and the .idx next to it is the map into it: a 256-entry fan-out table, every object id in ascending order, a CRC32 for each and the offset where each one begins. Drop both here and this page reads them the way git verify-pack -v does — every object with its id, its type, the size stored for it, the number of bytes it occupies in the pack, its offset, its delta depth and the id of the object its delta was written against — and then adds the totals that answer why a repository is so large: how many objects of each type, how many bytes each type costs after compression, how much of the pack is deltas, how deep the chains run, and which objects are the biggest.
Nothing is decompressed, and nothing needs to be. The header at each offset carries the type and the stored size in plaintext, a delta names its base in plaintext right after it, and the bytes an object occupies are simply the distance to the next offset. One consequence is worth knowing before you read the table: for a deltified object the size shown is the size of the delta, not of the object it rebuilds. Git prints the same number for the same reason, and the blob a small delta reconstructs can be enormous.
Two things neither file states have to be recovered from the layout. Nothing marks a SHA-256 repository, so both 20-byte and 32-byte ids are tried and the width whose sections land exactly on the two trailing checksums is the one used. And bit 31 of a 32-bit offset does not mean "past 2 GiB": it means the value is an index into a table of 64-bit offsets that follows, which is how a pack larger than 2 GiB is addressed at all. Ignore that bit and you get offsets that look plausible and are wrong.
Either file alone still says something. The .idx by itself gives the ids, the CRC32s and the offsets, which is often all you need and is a fraction of the size of the pack. The .pack by itself gives its signature, version and object count and then stops, because without the offsets there is no way to know where one object ends. With both files present, the one check an inspector can make without inflating anything is that the checksum the .idx stores for its pack is the checksum at the end of the .pack — that pairing is reported plainly, along with a truncated pack, a fan-out that decreases, an unknown index version, and a delta whose base lands outside the file.
What it cannot tell you: a pack holds no path names, so nothing here says which file a big blob belongs to — the names live inside tree objects and only come out after decompression. Nor does it prove the objects are intact; that needs the full inflate-and-hash that git verify-pack itself performs. Version 1 indexes are read too, and they simply have no CRC32 column, because git did not store one until version 2.
Frequently asked questions
Why is the size of a deltified object so small?
Because it is the size of the delta, not of the object the delta rebuilds. A deltified object stores only the instructions to turn some other object into this one — copy this range from the base, insert these literal bytes — and the size in its header describes that instruction stream. git verify-pack -v prints exactly the same number, for exactly the same reason. The size of the reconstructed object is written inside the compressed delta data, so it cannot be reported without decompressing, which this page deliberately never does.
What is the difference between an ofs-delta and a ref-delta?
Both store a delta; they differ in how they point at the base. An ofs-delta gives a backwards distance in bytes from its own offset, which is compact and self-contained; a ref-delta gives the base's full object id, which costs 20 or 32 bytes and requires a lookup. Git has written ofs-deltas by default since 1.5, and only falls back to ref-deltas when told to — repack.useDeltaBaseOffset set to false, for the benefit of very old clients — or when the base is not in this pack at all, which is what happens in the "thin" packs sent over the wire during a fetch.
Is a long delta chain a problem?
It is a trade. Chain length is how many deltas have to be applied, one after another, before an object exists again, so a depth of 50 means reading that one object touches 51 objects. Deep chains pack smaller and read slower, and git bounds them: repack takes --depth, whose default is 50. If a pack shows most of its objects at depth zero, delta compression barely helped — usually because the content is already compressed, like images, archives or encrypted blobs.
Can this tell me which large file is filling my repository?
Not by name. A pack stores objects, not paths: a blob is anonymous, and the file names live inside tree objects, which are themselves compressed. What this page gives you is the id of the biggest object, and from there git rev-list --objects --all | grep <id> in the repository names the path, or git cat-file -p <id> shows the content. The reverse direction — id to size to path — is exactly the workflow behind most "why is my clone 2 GB" investigations.
Why does it need the .idx? Could it not just walk the pack?
Not without decompressing. Objects sit back to back with no length field for the compressed data: the only way to find where one ends is to inflate it until the zlib stream terminates. The .idx already knows every offset, so with it the size in the pack is the distance to the next offset and no decompression is needed anywhere. Given a pack with no index at all, git itself rebuilds one with git index-pack, and that command does inflate every object.
My .idx does not match the .pack — what does that mean?
The last two hashes of an index are the checksum of the pack it describes and then the index's own checksum, so a mismatch means the two files were not written together. Usually they were copied from different places or one was regenerated; the offsets in the index then point into the wrong bytes and every type and size after that is nonsense. The fix is not to trust the pair: fetch the pack again, or rebuild the index from the pack with git index-pack.
Related tools
WebAssembly Module Inspector
Inspect a .wasm file in your browser: section sizes, imports and exports with full types, memory pages, the start function, name and producers sections.
Developer
GIF Structure Inspector
Open a .gif and read its blocks: frame delays and disposal, loop behaviour, palettes, transparency, and how many bytes each frame really costs.
Image
PKCS#12 Keystore Inspector
Open a .p12 or .pfx in your browser: MAC and ciphers before the password, then the certificate chain, the private key, and whether they match.
Developer
.gitignore Tester
Paste a .gitignore and a list of paths to see which are ignored and exactly which line decided — the answer git check-ignore -v gives.
Developer
.gitattributes Tester
Paste a .gitattributes and a list of paths to see every attribute git ends up setting on each path, and which line — or which macro — set it.
Developer
CRL Decoder (X.509)
Read a certificate revocation list in your browser: issuer, CRL number, validity window, every revoked serial with its reason, and the signature.
Developer