Git's Little Bundle of Joy
The scenario is thus: you need to sneakernet a
git push. Maybe your network
is down and you want to send changes to your co-workers. Perhaps you're working
somewhere onsite and don't have access to the local network for security reasons.
Maybe your wireless/ethernet card just broke. Maybe you don't have access to
a shared server for the moment, you want to email someone updates and you don't
want to transfer 40 commits via
git bundle. The
bundle command will package up everything that would
normally be pushed over the wire with a
git push command into a binary file
that you can email or sneakernet around, then unbundle into another repository.
Let's see a simple example. Let's say you have a repository with two commits:
$ git log commit 9a466c572fe88b195efd356c3f2bbeccdb504102 Author: Scott Chacon <firstname.lastname@example.org> Date: Wed Mar 10 07:34:10 2010 -0800 second commit commit b1ec3248f39900d2a406049d762aa68e9641be25 Author: Scott Chacon <email@example.com> Date: Wed Mar 10 07:34:01 2010 -0800 first commit
If you want to send that repository to someone and you don't have access to a repository to push to, or simply don't want to set one up, you can bundle it.
$ git bundle create repo.bundle master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (6/6), 441 bytes, done. Total 6 (delta 0), reused 0 (delta 0)
Now you have a file named
repo.bundle that has all the data needed to re-create
the repository. You can email that to someone else, or put it on a USB drive
and walk it over.
Now on the other side, say you are sent this
repo.bundle file and want to work
on the project.
$ git clone repo.bundle -b master repo Initialized empty Git repository in /private/tmp/bundle/repo/.git/ $ cd repo $ git log --oneline 9a466c5 second commit b1ec324 first commit
I had to specify
-b master because otherwise it couldn't find the HEAD
reference for some reason, but you may not need to do that. The point is, you
have now cloned directly from a file, rather than from a remote server.
Now let's say you do three commits on it and want to send the new commits back via a bundle on a usb stick or email.
$ git log --oneline 71b84da last commit - second repo c99cf5b fourth commit - second repo 7011d3d third commit - second repo 9a466c5 second commit b1ec324 first commit
First we need to determine the range of commits we want to include in the bundle.
The easiest way would have been to drop a branch when we started, so we could
master ^start_branch, but if we didn't we can
just list the starting SHA explicitly:
$ git log --oneline master ^9a466c5 71b84da last commit - second repo c99cf5b fourth commit - second repo 7011d3d third commit - second repo
So we have the list of commits we want to include in the bundle, let's bundle
em up. We do that with the
git bundle create command, giving it a filename
we want our bundle to be and the range of commits we want to go into it.
$ git bundle create commits.bundle master ^9a466c5 Counting objects: 11, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (9/9), 775 bytes, done. Total 9 (delta 0), reused 0 (delta 0)
Now we will have a
commits.bundle file in our directory. If we take that and
send it to our partner, she can then import it into the original repository,
even if more work has been done there in the meantime.
When she gets the bundle, she can inspect it to see what it contains before she
imports it into her repository. The first command is the
bundle verify command
that will make sure the file is actually a valid Git bundle and that you have
all the neccesary ancestors to reconstitute it properly.
$ git bundle verify ../commits.bundle The bundle contains 1 ref 71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master The bundle requires these 1 ref 9a466c572fe88b195efd356c3f2bbeccdb504102 second commit ../commits.bundle is okay
If the bundler had created a bundle of just the last two commits they had done,
rather than all three, the original repository would not be able to import it,
since it is missing requisite history. The
verify command would have looked
like this instead:
$ git bundle verify ../commits-bad.bundle error: Repository lacks these prerequisite commits: error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 third commit - second repo
However, our first bundle is valid, so we can fetch in commits from it. If you want to see what branches are in the bundle that can be imported, there is also a command to just list the heads:
$ git bundle list-heads ../commits.bundle 71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master
verify sub-command will tell you the heads, too, as will a normal
git ls-remote command, which you may have used for debugging before. The point
is to see what can be pulled in, so you can use the
to import commits from this bundle. Here we'll fetch the 'master' branch of
the bundle to a branch named 'other-master' in our repository:
$ git fetch ../commits.bundle master:other-master From ../commits.bundle * [new branch] master -> other-master
Now we can see that we have the imported commits on the 'other-master' branch as well as any commits we've done in the meantime in our own 'master' branch.
$ git log --oneline --decorate --graph --all * 8255d41 (HEAD, master) third commit - first repo | * 71b84da (other-master) last commit - second repo | * c99cf5b fourth commit - second repo | * 7011d3d third commit - second repo |/ * 9a466c5 second commit * b1ec324 first commit
git bundle can be really useful for doing network-y, share-y operations
when you don't have the proper network or shared repository to do so.