How to use git-bundle for keeping development in sync?
Asked Answered
K

2

76

I need to keep my development trees in sync on different computers, with no network connection between them.

We have a central git repository, and I normally work on my own clone on my office computer. Sometimes I need to do some development on another computer, which is never connected to the office network. None of the computers are ever connected to Internet. Development may be performed on both computers between synchronizations.

I have read the help pages for git-bundle, which seems like the best tool, but I am not really sure how a good workflow could be set up.

Can you give me some advice or pointers?

Kanya answered 3/9, 2010 at 12:59 Comment(2)
If they are connected to internet, you could use them with DopBox: see https://mcmap.net/q/13033/-how-to-configure-git-for-using-it-with-dropbox/…Plasia
Sorry, none of them will ever be connected to internet. Added that to the question.Kanya
A
126

Bundles!

The workflow with git bundle is going to be essentially the same as any other workflow. This may not seem like terribly helpful advice, but here it is: use whatever workflow you would normally use, and replace "push/pull" with "carry a bundle here to there on a flash drive, then pull".

The man page actually has a pretty good walkthrough for getting going with this, although it's more of a one-way example. For the sake of completeness, here's a slightly modified version of it, showing how to move information both ways:

# on hostA, the initial home of the repo
hostA$ git bundle create hostA.bundle --branches --tags

# transfer the bundle to hostB, and continue:
hostB$ git clone /path/to/hostA.bundle my-repo
# you now have a clone, complete with remote branches and tags
# just to make it a little more obvious, rename the remote:
hostB$ git remote rename origin hostA

# make some commits on hostB; time to transfer back to hostA
# use the known master branch of hostA as a basis
hostB$ git bundle create hostB.bundle ^hostA/master --branches --tags

# copy the bundle back over to hostA and continue:
hostA$ git remote add hostB /path/to/hostB.bundle
# fetch all the refs from the remote (creating remote branches like hostB/master)
hostA$ git fetch hostB
# pull from hostB's master, for example
hostA$ git pull

# make some commits on hostA; time to transfer to hostB
# again, use the known master branch as a basis
hostA$ git bundle create hostA.bundle ^hostB/master --branches --tags
# copy the bundle to hostB, **replacing** the original bundle
# update all the refs
hostB$ git fetch hostA

# and so on and so on

The key thing to notice is that you can add a bundle as a remote, and interact with it just as you would with any other remote. To update that remote, just drop in a new bundle, replacing the previous one.

I've also taken a slightly different approach to picking a basis. The man page uses tags, always kept up to date with the last refs which were transferred to the other host. I've simply used the remote branches, which will refer to the last refs transferred from the other host. It's a little bit inefficient; you'll end up bundling more than you need to, since it's one step behind. But flash drives are big, bundles are small, and using the refs you already have instead of having to take an extra step and be careful about tags saves a lot of effort.

The one thing that makes bundles a bit of trouble is that you can't push to them, and you can't "rebase" them. If you want the bundle based on a new basis, you have to recreate it. If you want new commits in it, you have to recreate it. This hassle gives rise to my next suggestion...

Repo on a thumb drive

Honestly, unless your repo is really big, this might be just as easy. Put a bare clone on a thumb drive, and you can push to and pull from it from both computers. Treat it like your network connection. Need to transfer to the central repo? Plug it in!

Ambrosio answered 3/9, 2010 at 19:58 Comment(9)
kudos for the thumb drive mention also from me. Might start using that.Amos
The correct order of arguments for git remote add is name and only after — url.Earthling
BTW, this works nicely only if the commits done on hostB are close to hostA/master in the commit graph. For example if you were to work on a branch experimental that was forked from master very long ago, the command git bundle create hostB.bundle ^hostA/master --branches --tags would make a big bundle with all of the history of experimental. This isn't a fundamental problem in this approach, just something to be careful about. PS. For other wondering about the ^commit syntax, it means "all commits that aren't ancestors of commit. (See git-rev-parse, SPECIFYING REVISIONS)Kandis
How could I not find that 2 months ago searching for offline git syncing... I've been using format-patch and am (with all the headaches it implies with CRLF vs LF and everyone on my team insisting to have its own policy regarding eol) Kudos. You saved me hours of work.Quarter
git bundle create hostA.bundle --branches --tags resulted in a bundle that when I tried to clone from it would give an error, "remote HEAD refers to nonexistent ref, unable to checkout". Using git bundle create hostA.bundle --all seems to work for me.Frutescent
I wrote a (hopefully) user-friendly bash implementation of this technique - see github.com/cxw42/git-tools/blob/master/wormhole . Comments welcome - YMMV - no warranty :) .Relucent
except, you cannot email a thumb drive. this other use case is for bundle.Withal
@Withal but you can zip a bare repository and email that. Bundle files have the advantage of having already been zipped, but more importantly being able to specify a baseline commit to exclude, so that you only include new commits; e.g. git bundle create foo.bundle master.. --all will exclude the master branch.Macleod
How to add the bundle instead of using clone, if I already have a cloned repo and want to add bundle as new remote. (Edit: first clone it in another dir, then add it as remote as directory, it will be able to do so because it now contains .git file)Timework
Y
16

@Jefromi answer was great - 10x better than the git docs, which go on at length about incomprehensible requirements and actions.

It's still a bit complicated, so here's the simplest case synching once (in my case: FROM: an offline laptop with broken wifi card, TO: a desktop with net access). Based on @Jefromi's answer, this seems to work fine:

AHEAD = machine that is ahead by some number of commits. BEHIND = machine you want to copy the commits to

1. AHEAD: git-bundle create myBundleName.bundle --branches --tags

BOTH: copy myBundleName.bundle (using email, USB, whatever)

BEHIND: (place the file myBundName.bundle anywhere you want outside the project folder)

2. BEHIND: cd [the project folder]
3. BEHIND: git pull [path to the bundle file]/myBundleName.bundle master

So long as you include the branch-name on the end (by default, if you're not using branches, "master"), this seems to work fine, and doesn't replace any of the internal references on BEHIND - so you can still synch to/from the origin master.

i.e. if BEHIND has internet access, it's still safe to do:

(OPTIONAL) 4. BEHIND: git push

...and it'll update the main repository, as if your changes had been done locally, as normal, on BEHIND.

Yaker answered 14/10, 2012 at 16:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.