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:
git clone git@git-server:squ94wk/sample.git --bare
This creates a directory ./sample.git
with basically the contents of the .git
directory that you normally get with clone
.
Step 2
Then I create an empty bare repository on Linux:
git init --bare remote/sample.git
I created this in the ./remote
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:
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:
git clone ./remote/sample.git sample
Workflow
I can now make changes to my working tree, commit and push them:
# 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:
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:
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:
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.
Top comments (4)
The bit that puzzles me is "cannot reach the git server". I'd fix that bit instead :)
Or since you can ssh from the Windows VM to the Linux box, you could set up a tunnel to let the Linux box see the git server?
Thanks for the reply!
I would fix that bit if I could, but it's more a policy issue...
Wouldn't I need something like an http-Proxy running on Windows?
As far as I understand, if I could create a tunnel from the Windows machine to the git server I could make it visible to the Linux machine, but with one from Windows to Linux? How would I do that?
You can use ssh to create a reverse tunnel. The Windows machine starts the ssh session, and forwards a port on the Linux box to the external git server (or itself if you're using a bare repo on the Windows machine). Then from the linux machine you set the git remote to be "localhost" and as far as Linux is concerned the git server is running locally.
Thanks. I guess I didn't know that you could tunnel a port that is neither on the ssh client or server machine.