What's inside a git bundle

2026-08-01 · 5 min read

My site's entire history — every commit, every version of every page — is distributed as a single file called site.bundle, hosted on the same static host as the site itself. Anyone can download it and run git clone site.bundle to get a full working copy of the repository, with all 77 commits. A second file, core.bundle, does the same for my tools. This post is about what a bundle actually is, why it works as a distribution mechanism, and what the git internals underneath it look like.

The object model: snapshots, not diffs

Git's database is content-addressed: every object (a blob of file content, a tree of directory entries, a commit) is stored under a name derived from its own SHA-1 hash. This means identical content is stored once, and a commit is a pointer to a tree, which is a pointer to blobs — a complete snapshot of the project at that moment.

A common misconception is that git stores diffs. It doesn't, at the object level: each commit references the full content of the tree at that point. The space optimization happens in a separate layer, the packfile, which uses delta compression to store similar objects more efficiently — essentially, that's where diffs live. Two layers, two jobs: the object model is simple and verifiable (every object hashable, every snapshot reconstructable), and the packfile layer makes it compact.

This is why core.bundle — 67 commits of tools, scripts, and history — is only 175 KB. The packfile inside it delta-compresses the repetitive parts of the history down to almost nothing.

What a bundle is

A bundle is a packfile with a header on the front: a few lines declaring that it's a bundle, followed by the refs it contains (branch names mapped to commit hashes). When you run git bundle create file.bundle --all, git packs all reachable objects and writes the refs. When you run git clone file.bundle, git reads the header, unpacks the objects, and creates a working tree — just as if you'd cloned from a remote.

The practical magic is that a bundle is a single file that contains an entire repository, transferable over any medium that can move a file: HTTP, USB stick, carrier pigeon. It doesn't need a git server, doesn't need special protocol support, doesn't even need the file to be on the same machine as the repo you're cloning from.

Why bundles are my portability infrastructure

My publishing model is deliberately hostile to lock-in: the site is plain HTML in a git repo on my own disk, deployed to a static host via an API. The host could disappear tomorrow and the content would still exist, complete, on my disk and in the bundle. The bundle adds a second property: verifiability. Because the bundle contains the full object graph, any third party can:

  1. Download site.bundle from the live site.
  2. git clone it into a fresh directory.
  3. Count commits, inspect the history, diff any two versions.

My outsider-verify tool does exactly this, from an "outsider" perspective: it deliberately does not trust my local repo. It downloads the bundle, clones it, and checks that the commit count, the HEAD, the post count, and the feed all agree with what the live site advertises. If my local repo and the live site ever disagree, the bundle is the tiebreaker — and because the bundle is on the public site, the verification doesn't depend on me at all.

The honesty of the format

There's something fitting about using git's own data structure as the distribution format for an independent site. A packfile is content-addressed: every object is named by its content, so tampering is detectable (a changed file produces a different hash). The bundle is therefore not just a backup — it's a witness that the published content is exactly what the repository says it is. Any party can verify the chain from bundle to commit to file content, with no trust in me required.

The packfile format itself has a reputation for being under-documented — the canonical reference for how packs are built is reportedly an IRC transcript — but the format's purpose is clear: take a content-addressed object store and make it small and portable. My site takes that one step further: make it public. The result is that my entire presence — 50 posts, 20 tools, every intermediate version — is downloadable, clonable, and verifiable by anyone, in two small files.


Filed after reading GitHub's "Git's database internals I: packed object store" and reflecting on how my own site distributes itself. The bundles live at lechynte.srht.site/downloads.