Transferring repositories offline without a server
Not every environment has direct network access to a Git server, whether due to security constraints inside isolated networks or simply the lack of an internet connection. Git bundle packs commits, trees, and blobs into a single file that can be passed along via USB stick, email, or any other transport channel and hooked up like a regular remote.
Table of Contents
- 1. When there is no direct access to the Git server
- 2. Creating and importing a complete bundle
- 3. Verifying bundle integrity before handing it over
- 4. Incremental bundles for repeated transfers
- 5. Packing several branches and tags deliberately into one bundle
- 6. Use in air gapped and heavily regulated environments
- 7. Bundles versus patch files and git format-patch
- 8. Best practices for using bundles in production
- 9. Common pitfalls when working with bundles
- 10. Summary
- 11. FAQ
1. When there is no direct access to the Git server
In isolated networks with no internet connection, at clients with strict security policies, or while traveling without a stable connection, a normal git push or git pull against a central server simply is not possible. Yet code still often needs to move between two environments, for instance between an isolated development environment and a production system with no outbound network access at all.
Git bundle was built precisely for such scenarios: the command packs a defined slice of history, including every needed commit, tree, and blob object, into a single binary file that can be transported over any channel and, on the receiving end, hooked up like a regular remote without any network access.
2. Creating and importing a complete bundle
The simplest case is a bundle containing the full history of one or more branches. The command git bundle create expects a file name and a revision specification that works exactly like it does for git log or git rev-list, so either a single branch name for that branch's complete history, or --all for every reference in the repository.
On the receiving side, the bundle can be cloned directly like a regular repository, or hooked into an already existing repository as an additional remote. Git treats the bundle file transparently as a network source, so all the usual commands like git fetch and git pull work without any change.
# Create a complete bundle with all branches and tags
git bundle create repo-full.bundle --all
# Clone the bundle like a normal repository
git clone repo-full.bundle project-local
# Hook the bundle into an existing repository as an additional remote
git remote add offline-source repo-full.bundle
git fetch offline-source
3. Verifying bundle integrity before handing it over
Before a bundle gets handed off, it is worth verifying it with git bundle verify, which checks whether the contained objects are consistent and whether every referenced ancestor is either included in the bundle itself or already present in the target repository. If a needed ancestor is missing, the command reports that exact missing commit instead of failing with a cryptic error only during import on the receiving end.
This check matters especially for incremental bundles built on a shared base commit, because an incorrectly chosen base commit would otherwise only surface for the recipient once the transfer itself is already complete and the physical medium may no longer be readily available.
# Check a bundle file for consistency and required ancestors
git bundle verify repo-full.bundle
# List contained references and commits without importing the bundle
git bundle list-heads repo-full.bundle
4. Incremental bundles for repeated transfers
For regular data exchange with an isolated environment, a full bundle on every transfer is wasteful, since already transferred commits get sent again each time. Git bundle therefore supports incremental transfers: with a revision specification such as last-sync..main, the bundle only contains the commits added since the last sync.
This requires the base commit, meaning the state of the last successful sync, to already exist in the target repository. A proven pattern is a permanently maintained tag or branch that marks the most recently transferred state and gets updated after every successful transfer.
# Pack only the commits since the marked last sync state into a bundle
git bundle create update-week-32.bundle last-sync..main
# Update the sync marker after a successful transfer
git tag -f last-sync main
# Import the incremental bundle on the receiving side
git fetch update-week-32.bundle main:main
5. Packing several branches and tags deliberately into one bundle
A bundle is not limited to a single branch. Several revision specifications can be combined in one call to transfer exactly the branches and tags that are actually needed on the receiving end, without dragging along unnecessary weight from feature branches that are not relevant there.
When importing such a multi reference bundle, git bundle list-heads shows in advance which branches and tags it contains, so the actually desired references can be picked deliberately with git fetch bundle-file ref:ref instead of automatically pulling in every reference the bundle carries.
# Create a bundle with main, a release branch, and all tags
git bundle create release-package.bundle main release/2.4 --tags
# Only pull main from the bundle into the local main branch
git fetch release-package.bundle main:main
6. Use in air gapped and heavily regulated environments
In regulated industries such as finance or critical infrastructure, production systems are often deliberately disconnected from the internet, so called air gapped environments. Code changes have to be brought in through a controlled, often manually approved channel there, which git bundle maps well when combined with an approval process and signed commits, since the bundle's integrity can be verified before it gets adopted.
A proven pattern is a two stage process: a bundle gets created in the open environment, digitally signed or at least given a checksum, moved into the air gapped environment through an approved transport path, and only imported there after successful verification with git bundle verify.
# Generate a checksum for the bundle for transport
sha256sum release-package.bundle > release-package.bundle.sha256
# Verify the checksum on the target side before importing the bundle
sha256sum -c release-package.bundle.sha256
7. Bundles versus patch files and git format-patch
For a single, small change, a bundle is often overkill. The command git format-patch turns one or more commits into classic patch files in mailbox format, which can be sent by email and applied on the receiving end with git am, without needing a full bundle for that. For individual commits or a short series, this route is lighter weight and easier for reviewers to read in plain text than a binary bundle file.
Once several branches, tags, or a longer commit series with a complex merge history need to be transferred, though, format-patch reaches its limits, since merge commits and tree structure can get lost along the way. Bundles represent the full object structure including merges without any loss, and are therefore the more robust choice whenever more than a single linear commit chain needs to move.
# Turn individual commits into patch files for sending by email
git format-patch -3 --stdout > last-three-commits.patch
# Apply the patch file on the receiving end
git am last-three-commits.patch
8. Best practices for using bundles in production
A clear naming convention for bundle files, for example including the date and the contained branches in the file name, prevents mix ups whenever several bundles are in circulation. For recurring transfers, a script that wraps bundle creation, checksum generation, and sync marker updates into a single step is worth building instead of running every step manually.
Bundles should generally be verified with git bundle verify before being handed off, because a broken or incomplete bundle often only surfaces on the receiving end after a failed import, at which point the original transport path may no longer be available anymore.
9. Common pitfalls when working with bundles
The most common mistake with incremental bundles is an incorrect or outdated base commit, which leads the target side to expect commits that are not actually included in the bundle. The error message on import is often not very informative in such cases, which is why a prior check with git bundle verify is worth doing every time.
Another pitfall is assuming a bundle automatically includes all tags or all branches: without explicitly specifying --all or concrete references, git bundle create only packs the specified revisions, which quickly leads to a bundle missing important references if the revision specification is incomplete.
| Approach | Network required | Includes history | Typical use |
|---|---|---|---|
| git push/pull over network | Yes, continuously | Yes, full or incremental | Everyday team workflow |
| Full bundle | No, only to transport the file | Yes, the entire specified range | Initial transfer into an isolated environment |
| Incremental bundle | No, only to transport the file | Only new commits since the base | Recurring sync with an air gapped system |
| Patch files via email | No | Only individual commits as patches | Individual changes without repository context |
Mironsoft
Git workflows, branching strategies, and CI hooks
Chaotic Git history and unclear branching rules across the team?
We set up clean Git workflows, clarify branching strategies for the team, and automate quality checks via Git hooks and CI pipelines so the history stays traceable.
Workflow Audit
Review the existing branching strategy and merge practice for weak spots.
Hook Automation
Set up pre-commit and pre-push hooks for linting, tests, and commit conventions.
Team Training
Teach rebase, cherry-pick, and conflict resolution hands-on across the team.
10. Summary
Git Bundle
Core command
git bundle create file.bundle
Verify before handoff
git bundle verify file.bundle
Importing
git clone or git fetch like a normal remote
Typical use
Air gapped systems, missing network connectivity