Artemis | Blog | Portfolio | About & Contact

2019 / 12 / 24 - Software discoveries (raw)

Barebones git

Git is probably one of the most used collaboration tools around here.

But it's also, in its core, a very useful code versioning tool, nothing more.

We're used to Github, Gitlab, and such, but sometimes we want to have a private git setup without all the collaboration environment built around those tools.

Setting up a gitlab, or even a gitea, server is cumbersome, especially when dealing with proxies, user authentication, backups, etc.

But if you really need to have a few repositories on your local NAS, or to host a handful on a small VPS for your local project, you may instead want to go for a "bare" server git setup.

# Bare

Basically put, git provides all the capabilities required to work in a client-server fashion, without anything except sshd and git.

Yup, that's it, those two tools are the only two tools you need to host some git repositories.

In this post, I'm gonna show how to create repositories, interact with them over SSH, then set up a basic proxy server to interact with them over HTTP, too.

# Beforehand...

Git's way to resolve paths over SSH is dumb.

That's good! That means it will be dumb to set up some git server!

So, how does that actually work? When you target a ssh path (e.g., scp-style, user@server.tld:group/project.git), it will ssh to the server using the user user (and your ssh key, if you have one, which you probably do), then try to find the repository folder group/project.git from user's home folder.

What that entails is that you only have to create a "bare" git repository inside your home folder, somewhere, and using the full path, you'll be able to access it.

In a more general way, since ssh is a direct access to a remote machine, you can technically interact with any folder on your server, if you have read / write rights to it, by starting from / (the root), or by providing a path relative to the user's home folder (e.g. ../other/project.git).

For organisational purposes, I'm gonna stay with the home folder, here.

# Create a repository

To create a "server-side" repository, you'll create what's known inside of Git as a "bare" repository, which is a structure which only contains what git needs to version code (no tracked branch, etc.).

Simply put, you'll create what can usually be found inside your project's .git.

To do that, the command is git init --bare, to be run inside an empty folder.

To create a new folder and init it along the way, append the folder path at the end of the init command, e.g. git init --bare ~/project.git.

That's it.

Here's a few examples, all run from the client to the server.

# create a git repository at $HOME/project.git
ssh server.tld git init --bare project.git
# create a project group at $HOME/group, then init a repository inside
ssh server.tld `mkdir group && git init --bare group/project.git`

# Interact with them over SSH

You have two operations which you can use to interact with a remote git repository:

Both can be done using the concept of remotes in git (which I assume you're already familiar with. If it isn't the case, I invite you to read the manual on remotes).

As the documentation shows, an over-SSH repository can have the following formats.

# URL-style
ssh://[user@]host.xz[:port]/path/to/repo.git/
# SCP-style
[user@]host.xz:path/to/repo.git/

In our examples, our remotes would be:

To clone one of them, the usual git command is as simple as git clone server.tld:project.git!

# What about HTTP?

Yeah, what about HTTP?

What if I don't have SSH on my machine, or don't want to bother with it?

Well, you won't be able to create or manage your repositories over HTTP, but you'll be able to push / fetch from them, which is already a good start (and probably what you want if you want to, e.g., share a repository with someone).

Git provides a nifty CGI binary called git-http-backend.

You can invoke it using git http-backend if you wish to automate some tasks, but the binary is usually installed at /usr/lib/git-core/git-http-backend.

An example caddy configuration using its cgi extension would be as simple as the following.

git.server.tld:80 {
    cgi / /usr/lib/git-core/git-http-backend {
        env GIT_PROJECT_ROOT=/home/myuser/ GIT_HTTP_EXPORT_ALL=true
    }
}

This will allow the fetch operation for every client that can reach git.server.tld, but will only allow push operations to the "authenticated" clients.

I haven't yet been able to find a generic definition for when git-http-backend determines that a client is authenticated.

I'm still reading on this topic and trying out things, mostly with Caddy.

# Project templates

But gitea provided me with project templates.

I use them, they're really useful to quickly start a project!

Fear not, my child! Git also has you covered, as templates are a natively-supported git concept!

Again to the rescue, the documentation explains that there is an optional argument on git init, and if it points to a valid template directory, every file inside this directory that does not start with a . (dot, a.k.a. linux hidden file) will be copied in the new git repository.

You also can set default template directories for all your projects!

When you initialize a repository using git init, if you don't specify a template directory, git will check, in the following order and until it finds a directory at the requested path,

Now, this is still a very dumb template system, especially since it doesn't copy dotfiles, so you cannot have your .gitignore, .editorconfig, and such, automagically copied.

Still, if you don't have complex needs, it's a really decent feature that doesn't require you to install a template management tool.

You could also make a simple bash script to have some dotfiles, e.g. something that would rename every file starting with dot into a starting ..

If you still want some more complex templates, you may want to check out the neat tool called kickstart.

# Backups?

Since git is completely filesystem-level, it can easily be backuped, especially using incremental backup tools, such as restic!

This section will showcase an example on how to backup some git repositories using restic.

I assume that you already know restic, as I'll only showcase backing-up, not repository creation.

Let's say I'm on server.tld, and in its local network, there is a SFTP server at sftp.tld on which I have a backup repository initialized.

I have project.git and group/project.git as hosted git repositories I want to backup.

restic -r sftp.tld:backups/ backup **/*.git

This will backup every git folder (so both project.git and group/project.git) inside the backups/ repository found in sftp.tld.

# Additional notes

I found this guide to be pretty neat during my discovery on the topic, and I invite you to check it out if you have some questions.