Air-gapped transfer
Move the history of a branch from a machine on a restricted network to an isolated one by carrying bundle files on removable media, and update with incremental ranges.
Git · Command
git bundle packs Git objects and refs into a single file. You can move that file by USB, email or any other channel and clone or fetch from it later, with no network connection between the two repositories.
Last updated
git bundle create takes rev-list style arguments, such as branch names, --all or ranges like old..new. It writes a pack file with a header that lists the refs in the bundle. If you give exclusions such as ^old or old..new, those commits become prerequisites, and the receiving repository must already have them. On the receiving side the bundle works like a read-only remote: you can clone from it, fetch from it, or list its refs. verify checks that the file is valid and that the prerequisites are present, list-heads prints the refs, and unbundle stores the objects through index-pack.
git bundle create [-q | --quiet | --progress] [--version=<version>] <file> <git-rev-list-args>
git bundle verify [-q | --quiet] <file>
git bundle list-heads <file> [<refname>…]
git bundle unbundle [--progress] <file> [<refname>…]| Name | Type | Description |
|---|---|---|
<file> | path | The bundle file to write or read. Use - to write to stdout (create) or read from stdin (verify, list-heads, unbundle). |
<git-rev-list-args> | rev-list arguments | Arguments that git rev-parse and git rev-list accept, including at least one named ref. They choose which refs and objects go into the bundle, for example master, --all or master~10..master. |
[<refname>…] | ref names | Limits which refs list-heads and unbundle report. This is mainly used by git fetch. |
--progress | flag | Shows progress on stderr even when stderr is not a terminal. |
--version=<version> | integer (2 or 3) | Sets the bundle format version. Version 2 works only with SHA-1 repositories. Version 3 supports capabilities. The default is the oldest format that works with the repository's hash algorithm. |
-q, --quiet | flag | Turns off progress reporting on stderr. |
create writes a bundle file. verify exits with 0 if the bundle is valid and can be applied, and prints any missing prerequisite commits. list-heads and unbundle print the refs defined in the bundle.
git fetch mybundle master:localRef, or run git ls-remote on it.--all to include every ref.verify before applying a bundle that has prerequisites. It checks the file format and confirms that the commits the bundle depends on are already present in the receiving repository.git bundle verify works well as a check in scripts or CI before an import.git bundle unbundle yourself. Use git fetch or git clone, which call it for you.--all contains only refs and the commits reachable from them. Local configuration, hooks, stashes, the index and uncommitted work are not included.cp -r. Git might write to it during the copy and leave a corrupted copy. A bundle is the safer option.#!/usr/bin/env bash
set -euo pipefail
cd "$(mktemp -d)"
# Source repository
git init -q -b main src && cd src
git commit -q --allow-empty -m "first"
# Full, self-contained bundle + remember what was sent
git bundle create ../full.bundle main
git tag -f lastSent main
cd ..
# Offline side: clone straight from the file
git bundle verify full.bundle
git clone -q -b main full.bundle dst
# More work on the source, then an incremental bundle
cd src
git commit -q --allow-empty -m "second"
git bundle create ../full.bundle lastSent..main
git tag -f lastSent main
cd ..
# Receiver checks prerequisites, then pulls from the replaced file
cd dst
git bundle verify ../full.bundle
git bundle list-heads ../full.bundle
git pull -q
git log --onelineThe script sends a self-contained bundle first, then sends only the new commits in a replacement bundle, which the clone picks up with git pull because its origin remote points at the bundle file.
Move the history of a branch from a machine on a restricted network to an isolated one by carrying bundle files on removable media, and update with incremental ranges.
Run git bundle create backup.bundle --all from a scheduled job, then restore later with git clone backup.bundle <dir>.
Email a colleague a bundle of a few local commits (for example main ^origin/main), which they can verify and fetch into a separate branch.
Use - as the file name to write a bundle to stdout or read one from stdin, so you can chain it with compression, encryption or ssh.
A bundle is a pack file with a header that lists the refs it contains, created with git bundle create <file> <rev-list-args>. The receiver treats the file as a remote: they can git clone it, git fetch from it, or run git ls-remote against it. This lets you move history between repositories with no network connection or server.
Follow-up: Can you push into a bundle? No. Bundles support only reading, so to send newer history you create a new bundle file and replace the old one.
Exclude commits the receiver already has, either with a range like lastSent..main or with options like ^old, --since or -10. Those excluded commits become prerequisites that must already be in the receiving repository. To check for them, the receiver runs git bundle verify, which lists any missing commits and exits with a nonzero code if the bundle cannot be applied.
Follow-up: How can you keep track of the cut-off point between bundles? Move a tag (for example git tag -f lastR2bundle master) after each bundle and use it as the left side of the next range.
git bundle create x.bundle $(git rev-parse HEAD) fail?The bundle has to record refs. A raw commit ID does not resolve to a ref name, so Git refuses with 'Refusing to create empty bundle'. Give a branch or tag name, or use --all. The same error happens if the right side of a range is not a ref.
Follow-up: Which arguments give a bundle the same refs a normal clone would get? Use --branches --tags as the rev-list arguments.
git clonegit fetchgit ls-remotegit pack-objectsgit index-packgit rev-listgit fetch-packgitformat-bundle