How to git when you're offline: proxying through bare repositories
Tags: git, offline
Published: 2019-10-31 13:48:38

As of recent I've been working a lot on remote machines without an internet connection. This can be a real annoyance for many reasons, but it also forces you to really learn your tools and some of their often overlooked features.
Through the following rather peculiar scenario, I will demonstrate how one can use bare git repositories as intermediate remotes to "proxy" to a remote git server.

## Problem
Following scenario:

Machine A:
* running Windows in a VM :(
* connected to a git server
* can login to machine B with ssh client

Machine B:
* running Linux :)
* cannot reach the git server

**How do you get a copy of the git repository from the git server to the Linux machine while maintaining a reasonably usable workflow?**

One could `scp` files (or git bundles) between the machines, but who really wants to do that all the time?

## Using bare repositories

_"Bare" repositories are git repositories without a working tree. Git servers use them behind the scenes, since they don't need to edit files and stage changes._

This is what I came up with:

####Step 1
On Windows I clone the repository in question into a bare repository:
```bash
git clone git@git-server:squ94wk/sample.git --bare
```

This creates a directory <code>./sample.git</code> with basically the contents of the <code>.git</code> directory that you normally get with <code>clone</code>.

####Step 2
Then I create an empty bare repository on Linux:
```bash
git init --bare remote/sample.git
```
I created this in the <code>./remote</code> directory, because this is not actually the repository I will be working with; rather, it acts as a "fake" remote in the sense that it isn't on another machine.

####Step 3
I can now set up the bare repository on Linux (with ssh host alias "linuxvm") as a remote on the Windows machine and push to it:
```bash
git remote add linux linuxvm:remote/sample.git
git push linux master:master
```
The contents of the master branch on Linux are now the same as on Windows and the git server alike.

####Step 4
There is still no working tree though. To fix that I just clone the bare repository that exists locally on Linux:
```bash
git clone ./remote/sample.git sample
```

## Workflow
I can now make changes to my working tree, commit and push them:
```bash
# cd sample
echo "a change" >> file
git add file
git commit -m "add 'a change' to file"
git push # origin master:master
```

The commit and the updated reference still needs to go to the Windows machine and to the git server.

If Windows was running a ssh server I could push directly from the Linux machine to Windows (that would come with other benefits as well, like automating the next step with a `post-update` hook). But since it only works the other way around, I fetch the changes from the Linux machine and push them to the git server manually:
```bash
git fetch linux
git push origin linux/master:master
```

Note that `linux/master` is tracking the remote master branch of the bare repository on the Linux machine.

It works the other way around, too. I fetch changes from the git server and push them to the bare repo on Linux:
```bash
git fetch origin
git push linux origin/master:master
```

There I can then fetch/pull the changes, hence updating my remote tracking branches and my working tree:
```bash
git fetch
git merge
```

_Done._

## Closing

Now although this workaround is quite a stretch, it comes closest to the familiar "fetch-commit-push" workflow and is quite robust.
Using `--bare` saves you from having working trees everywhere that you don't use and would otherwise only get in the way.

You might never have to use bare repositories yourself (or frankly even work in such an atrocious environment), but I hope you can still take something away.