Contents
Regaining your Git independence
This blog post is about digital freedom. You don't need a megacorporation to store your code for you, or to share it with the world. Hosting your projects is very simple. All you need is git, and a computer to run it on.

Yes, that image is AI generated. I just though it illustrates my point quite well.
Git vs Github
Many people who are new to the world of programming have heard about Git and Github, but do not necessarily know exactly what they are and how they are related. These days, I find that many beginners have only heard of Github and don't realize that Git is a separate thing.
In this blog post I hope to inspire you to take back control and ownership of your work. You don't need corporate services like Github to host your projects! And you don't need to install bloated, complex services like Gitlab, or Gitea. There's a much simpler solution. Let me show you.
Git is a version control system
is not
Git is the actual software that people use to manage their projects. It helps to keep track of changes, share code between people, handle conflicting changes, and much more. It was written by Linus Torvalds, the creator of Linux, in 2005, and quickly became the de-facto standard tool for version control.
Github is an online service
Github, which is owned by Microsoft these days, is an online service for storing your repo - the project files that you are managing with Git. In other words, this makes your project available online, so that there is always a central repo you can clone from, or push your changes to.
Apart from that, services like Github add extra functionality like issue tracking, browsing your source code, CI/CD, and much more. And all of that is of course very convenient, but remember: "When the product is free, you are the product." In return, Microsoft gets your data, and they train their AI on your code.
Personally, I moved all my personal projects away from Github and onto my own server years ago. I now use Github for public projects only.
Doing it yourself
The first thing to realize here is: it's all just files on a filesystem. If Git can read it, it can clone and pull.
Let's see how that works. I can create a repo on my machine and add some work:
$ mkdir ~/my_git_repo
$ cd ~/my_git_repo
$ git init .Initialized empty Git repository in /Users/reindert/my_git_repo/.git/
This creates a git repo locally, on my computer. Git creates a hidden
folder inside the project called .git, and that is where it stores
the version information for your files. If you create a repo on
Github, the .git folder will be created on there. But in this case
we do it ourselves.
We can now start creating files and commit them, so that the newly
added revision will be stored in the .git folder inside my project:
$ echo "Just some example text" > demo.txt
$ git add .
$ git commit -m "Initial commit"
[main (root-commit) 718900f] Initial commit
1 file changed, 1 insertion(+)
create mode 100644 demo.txtCloning a local repo
Like I said above: if git can read it, we can clone it. So in another folder on the same machine, I can clone the repo:
$ mkdir ~/another_location
$ cd ~/another_location
$ git clone ~/my_git_repoAll this does is to create a folder called my_git_repo, and
duplicate the .git folder in there. Then it checks out the main
branch. So you do not need Github to share your code or to clone it.
We now find a .git folder and our demo.txt file in the new location:
$ cd ~/another_location/my_git_repo
$ ls -la
total 8
drwxr-xr-x@ 4 reindert staff 128 Jun 2 23:13 .
drwxr-xr-x@ 3 reindert staff 96 Jun 2 23:13 ..
drwxr-xr-x@ 12 reindert staff 384 Jun 2 23:13 .git
-rw-r--r--@ 1 reindert staff 23 Jun 2 23:13 demo.txtInterestingly, if we look at the file .git/config in our cloned
project:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = /Users/reindert/my_git_repo
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/mainNote how this references the original repo location as "origin". This is exactly the same behaviour you get when you clone from Github. This will allow you to pull changes from the original repo just like you're used to. I encourage you to try this for yourself: create a local repo, clone it, make a commit in the original repo, then pull it.
Pushing the other way (from the clone back to the original) is a different story. Git refuses this by default, because the original has a branch checked out, and pushing to that branch would overwrite that. We'll see how to solve that in a moment.
But this already proves my point: we don't need a third-party service to be able to exchange project revisions. Git just needs access to the repo files, that's all. So let's see how to make this work over the internet.
Serving a Git repo over SSH
The easiest way to make a git repo available online for pushing and pulling is using SSH. The only thing you need is a machine accessible via SSH where you can store your code. So let's assume we have a Linux machine in the cloud - through a service like digitalocean or something similar. These usually come with SSH already running.
Alternatively you can run SSH on your Macbook, or a Windows PC, or even on a raspberry Pi. The downside is that you don't usually want to expose devices from within your home network to the open internet. But if you do want to try that, you can look into something like tailscale to connect safely to your hardware at home.
Anyway, let's assume you have some machine running an SSH server. How do we use this to share our work?
Creating the Repo
We start by creating the repository on the server. In practice, this will be the "origin" - the version that you clone, pull from and push your changes to. The best practice is to create a bare repo:
# log into your server
$ ssh user@myserver.org
myserver:~ $ mkdir -p ~/repos/my_project
myserver:~ $ cd ~/repos/my_project
myserver:~ $ git init --bareInitialized empty Git repository in /home/user/repos/my_project/
A bare repository contains only the version information (the .git
folder) and does not have a working directory. In other words, it does
not checkout any of the files for you to work in. This is the typical
setup when hosting a central repo. Git will now also accept pushing to
any branch in this repo, which solves the problem we had with pushing
earlier.
And that is all! You now have an empty, self-hosted repo.
Cloning the Repo
So how do we clone this repo? We will ask git to do this over ssh. So
we take the ssh argument to log into the server: user@myserver.org,
and add the path to the git repo:
# Run this on your local machine to clone the repo
$ git clone user@myserver.org:repos/my_project
Cloning into 'my_project'...
warning: You appear to have cloned an empty repository.This is just like setting up a new repo on Github and cloning it! But this time everything is under our own control. From here on, you can make changes, push and pull, just like always. So there you have it: your own git repo. Enjoy your freedom!
Other Github features
Obviously, with this approach there are a lot of Github features we cannot replicate. For example, there is no fancy web UI for git. If you want a graphical user interface, you might use something like Fork instead. If you need automatic builds or other CI/CD stuff, there are many alternatives as well, like running your own Jenkins. If you need an issue tracker, there are many open source options available.
So in my opinion, there is really no reason to use Github except convenience and popularity. But there is a really good reason NOT to use Github: don't give your data to an evil megacorporation! (And yes, I do also use Github, but not for anything personal).
About pull requests
There is one thing that we cannot do with this setup: pull requests, because pull requests are not a native Git feature. They are an extra feature offered by platforms like Github and Gitlab.
Git’s own mechanism for collaborating is branching, merging, and reviewing patches. You can still have a Git-based code review process without a special "pull request" button. Just use branches and do a manual merge or cherry-pick after reviewing.
Or do it the way Linus himself does on the Linux kernel: use git
format-patch and git send-email to share and review patches by
email. This is the original Git collaboration workflow, it's a bit old school but it does work!
Some other pointers
If you decide to try this out for yourself, here are some additional pointers:
Security best practices
SSH in general is pretty safe, but make sure you use a strong password to prevent anyone from guessing it and getting access to your server!
Or even better: learn about key-based login. This is MUCH safer than passwords, and once you do this, you can disable password-based login altogether.
Teamwork
The approach above uses your own login on the server to host your git
repo. That does not work well if you want to share your work with
others. The simplest approach would be to create a separate git user
on your server, and move the repo to that user's home directory. The
url to clone from would then become
git@myserver.org:repos/my_project.
You then ask each team mate for their public SSH key, and add those to
.ssh/authorized_keys for the git user. That way, all your team mates
will be able to push and pull to the repo.
As an extra precaution, set the git user's login shell to
/usr/bin/git-shell. This restricts your team mates to git
operations only, instead of giving them full shell access to your
server.
Growing
Of course the ssh approach to serving git is quite simplistic and it will not fit everybody's needs. At some point, you may outgrow this simple setup. You might have many repos, or many users, or need more fine-grained permissions per user. I would suggest gitolite or soft-serve as nice light-weight soltuions for that. Or you can always reach for some of the more full-featured platforms (forgejo, gitea, gitlab), but I just think those are generally bloated and quite crappy.
Conclusion
Hosting your own Git repos is not complicated, and you don’t need a third-party service at all. There's no need to share your data with a big, soulless corporation because you can host it yourself!
Reindert-Jan Ekker