Barebones git Published the 2019-12-24 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" [sshd]: https://www.mankier.com/8/sshd [git]: https://www.mankier.com/1/git Basically put, git provides all the capabilities required to work in a client-server fashion, without *anything* except [`sshd`][sshd] and [`git`][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: - fetch: retrieve changes to your local `.git` - push: push changes to the remote server 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`](https://www.mankier.com/1/git-remote)). As [the documentation](https://www.mankier.com/1/git-push#Git_Urls) 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: - For `$HOME/project.git`, `server.tld:project.git` (or `server.tld:project`) - For `$HOME/group/project.git`, `server.tld:group/project.git` (or `server.tld:group/project`) 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). [smart]: https://git-scm.com/book/en/v2/Git-on-the-Server-Smart-HTTP Git provides a nifty [CGI binary][smart] 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](https://jung-kurt.github.io/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](https://www.mankier.com/1/git-init#Template_Directory) 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, - `$GIT_TEMPLATE_DIR`: The environment variable, if set - `init.templateDir`: The git configuration variable, if set - `/usr/share/git-core/templates`: The default template directory, if found 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](https://artemix.org/blog/kickstart.html). ## Backups? Since `git` is completely filesystem-level, it can easily be backuped, especially using incremental backup tools, such as [`restic`](https://restic.readthedocs.io/en/latest/)! 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](https://linuxize.com/post/how-to-setup-a-git-server/) to be pretty neat during my discovery on the topic, and I invite you to check it out if you have some questions. I found [this article showcasing a barebones CI system based on a barebones git repository](https://www.0chris.com/tiny-ci-system.html), which is not only definitely worth checking out, but "good enough" and a decent base to start building on.