Skip to content
Git Basics: Transfer Committed History with a Bundle

Git Basics: Transfer Committed History with a Bundle

October 27, 2026

A disconnected machine can receive a Git repository without a server. A bundle stores reachable Git objects and named references in one file. Practice a complete transfer and an incremental update using only local repositories.

Prepare a scratch directory

Tested on Linux with Git 2.52.0 and GNU Bash 5.2.37. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. The script creates a second temporary fixture directory and removes only that directory through its exit trap. Run it in a normal Bash terminal, outside any Git hook or Git wrapper. No global Git configuration is changed.

Keep using the same terminal. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-bundle.sh:

#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
fixture_dir="$(mktemp -d)"
trap 'rm -rf -- "$fixture_dir"' EXIT
export GIT_CONFIG_NOSYSTEM=1
export GIT_CONFIG_GLOBAL=/dev/null
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
git init -q -b main "$fixture_dir/repo"
cd -- "$fixture_dir/repo"
git config user.name 'Practice Author'
git config user.email 'practice@example.invalid'
printf 'first\n' > notes.txt
git add notes.txt
git commit -qm 'First note'
git bundle create -q ../complete.bundle main
git -c init.defaultBranch=main clone -q -b main ../complete.bundle ../receiver
test "$(git -C ../receiver log -1 --format=%s)" = 'First note'
printf 'Initial receiver: %s\n' "$(git -C ../receiver log -1 --format=%s)"
git tag transferred
printf 'second\n' >> notes.txt
git commit -qam 'Second note'
git bundle create -q ../update.bundle transferred..main
git -C ../receiver fetch -q ../update.bundle main
git -C ../receiver merge -q --ff-only FETCH_HEAD
cmp notes.txt ../receiver/notes.txt
printf 'Updated receiver: %s\n' "$(git -C ../receiver log -1 --format=%s)"
printf 'File contents:\n'
cat ../receiver/notes.txt

Run it:

bash demonstrate-bundle.sh

Observed output:

Initial receiver: First note
Updated receiver: Second note
File contents:
first
second

Read the result

The complete bundle contains the history reachable from main, which makes it suitable for this clone. The transferred tag records the boundary already received. The second bundle excludes that boundary and its ancestors. Fetching it supplies the newer commit; --ff-only refuses a merge that would need a divergent-history merge commit. The byte comparison checks that both working trees end with the same file.

Apply it carefully

An incremental bundle needs prerequisite objects already present at the destination. Use git bundle verify path/to/update.bundle in the receiving repository before an actual transfer. A bundle is not a working-directory backup: unstaged edits, untracked files, hooks, and repository configuration are not included. This example transfers only main; decide which refs a real transfer needs. Treat bundles as repository data from a source you trust.

The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.

Clean up

Remove the source and then the empty practice directory:

rm -- demonstrate-bundle.sh
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect the remaining files rather than deleting unknown contents.

Acknowledgements

This post was created with assistance from AI.

Last updated on