SeriesPart 6 of 8 // Git Quick Hits
gitWriting
Sep 14, 2026
3 min read
git-quick-hits

Shipping a Repo as One File with `git bundle`

No network access, no shared server, no GitHub — just a USB stick and a deadline. git bundle packs an entire repository, branches, tags, and full history, into one file you can clone straight from.

A commit graph being compressed and sealed into a single labeled parcel, ready to be carried through a doorway with no network cable in sight.

An air-gapped environment needs your code — a customer site with no internet access, a security-cleared machine that can't reach GitHub, a handoff to a contractor who isn't on your org's network yet. Zipping the working directory throws away every commit, every branch, every tag. What you actually want to hand over is the repository itself.

git bundle packages a full repository — history, branches, tags, everything git clone would give you — into one ordinary file. Copy it over USB, email it, drop it in a shared folder; on the other end, it clones exactly like a remote would.

Pack it up

A small repo with two commits and a tag:

git log --oneline
# c5ea3ce second commit
# 5f83d23 initial commit
git tag
# v1.0

Bundle everything reachable from every ref:

git bundle create project.bundle --all
git bundle verify project.bundle
project.bundle is okay
The bundle contains these 3 refs:
c5ea3ce refs/heads/main
5f83d23 refs/tags/v1.0
c5ea3ce HEAD
The bundle records a complete history.

"The bundle records a complete history" is the important line — this isn't a diff or a patch series, it's every commit needed to reconstruct the repository from nothing, self-contained in a single file. On a normal repo it's typically a similar size to .git itself, since it's the same packed objects.

Clone straight from it

The receiving end doesn't need a server, a URL, or even network access — a bundle is a valid clone source:

git clone project.bundle myapp
cd myapp
git log --oneline
# c5ea3ce second commit
# 5f83d23 initial commit
git tag
# v1.0
git remote -v
# origin  /path/to/project.bundle (fetch)
# origin  /path/to/project.bundle (push)

Full history, the tag, everything intact. Git even sets the bundle itself as origin, though in practice you'd repoint that at a real remote once one exists on the receiving side.

Handing off only what's new

Bundling --all every time works, but for a repository that already exists on the other end, you can bundle just the commits it's missing. Say the far side already has v1.0:

git bundle create update.bundle v1.0..main

That produces a bundle containing only the commits between v1.0 and the tip of main — useful for periodic drops into an environment you can't push to directly, where re-shipping the entire history every time would be wasteful.

Checking a bundle before you trust it

git bundle verify isn't just a sanity check on the file — it also tells you what the bundle assumes the receiving repository already has. An incremental bundle built with v1.0..main needs v1.0 to already exist on the far side, and verify will say so explicitly if you run it against a repo that's missing that prerequisite. Run it before you ship the file, not after someone on the other end reports a confusing error.

Where this actually gets used

  • Air-gapped or offline handoffs — the case above. No shared server required on either side.
  • Backups outside your normal remote. A bundle is a complete, restorable snapshot; git clone from it works even if the original remote is long gone.
  • Getting a repo through a network you don't control. If corporate policy blocks git protocols but allows file transfer, a bundle sidesteps the restriction entirely — it's just a file.
  • Sending a branch for review without pushing it anywhere. git bundle create review.bundle main..feature-branch hands someone exactly the commits in question, nothing more.

A bundle isn't a live connection — it's a snapshot at the moment you created it. Fetching from one later won't pick up commits made afterward; you'd generate a new incremental bundle for that, the same way you'd git fetch from a real remote. Once the destination has its own proper remote set up, there's no reason to keep treating the bundle file as origin — it did its one job of getting the history there.

Next time "how do I get this repository onto a machine with no network access" comes up, skip the zip file. git bundle create gives you the actual repository, portable as a single file, cloneable the moment it arrives.

Series contents

01
Automating Regression Hunts with `git bisect run`
Read
02
Parallel Branches Without Stashing: `git worktree`
Read
03
The Undo Button Nobody Uses: `git reflog`
Read
04
`git rerere`: Stop Resolving the Same Conflict Twice
Read
05
`.gitignore` vs `.git/info/exclude` vs Global Excludes
Read
06
Shipping a Repo as One File with `git bundle`
Current
07
Attaching Build Metadata with `git notes`
Coming soon // Sep 21, 2026
08
Why Shallow Clones Break Your Changelog
Coming soon // Sep 28, 2026