Originally written and published elsewhere in June, 2011.
As more and more developers are moving to Git for their version control needs, one important use case is being left behind: that of the development team who wants a single official repository but prefers not to rely on a third party such as GitHub to host it. Unlike Subversion, the distributed nature of Git means that maintaining a single authoritative copy isn’t as easy as it should be. GitHub’s paid plans ease this pain but third-party hosting isn’t always the best answer, or even possible in some cases. Here’s how you can do it yourself.
First, a few assumptions
This article assumes you have a dedicated Linux server, physical or virtual, with root or sudo access. These instructions will not work on shared hosting or Windows servers, but they will work on BSD or other Unix variants with very little modification. I’m using Ubuntu for these examples. It also assumes a basic level of familiarity with managing user accounts and using SSH keys, as well as familiarity with Git itself.
A note about terminology
When referring to your server, I use the hostname server.example.com. You’ll want to replace this with your own server’s hostname. Likewise, when I refer to GitHub URLs, I use username in place of the account name. Be sure to use your own here as well. Finally, repository.git should always be changed to the actual name of your own Git repository.
Sound good? Ok, let’s get started.
I shouldn’t need to mention this step, but just in case:
$ sudo apt-get install git
We’re going to use the GitHub model for our Git server — a single user account that’s used to access all the repositories on the server. Logins are allowed with SSH keys only, and even then only to run Git commands. For consistency, we’ll call this account git and give it a home directory of
We’re giving this account a Bash shell initially so we can more easily run the setup commands as this user. When we’re finished we’ll change it to the more restrictive
git-shell, a shell included in the Git package that restricts commands to those used for Git interaction.
$ sudo useradd -m -d /var/git -s /bin/bash -c 'Git' git
Now become the git user. Unless otherwise noted, the rest of these commands will be run as this user.
$ sudo su - git
The next step is to collect SSH keys from your developers and place them in a single
authorized_keys file in the git user’s
~/.ssh directory. Note that this directory must be readable only by this user.
$ mkdir /var/git/.ssh $ touch /var/git/.ssh/authorized_keys $ chmod 700 /var/git/.ssh
Now add the SSH keys you collected to this new authorized_keys file, one key per line.
Now that you have a Git account and your developers can access it, it’s time to give them something to work with. We’ll cover copying existing repositories from GitHub and also creating new, empty ones and pushing your own code into it.
But first, we need to actually create a repository. The initial steps are the same regardless of how you’ll be adding your code to it. So create the directory to work with, initialize it as a bare Git repository, and set the appropriate ownership. You’ll naturally want to use your own repository name instead of repository.git, as I use here.
$ mkdir /var/git/repository.git $ cd /var/git/repository.git $ git --bare init
Now, where is your code coming from?
Scenario 1: Copy an existing repository from GitHub
In this first example, we’re going to migrate an existing repository from GitHub to our own server. In this example, replace username with your own GitHub username and repository.git with the name of your own repository.
$ git --bare fetch https://email@example.com/username/repository.git master:master
The output of the above should look something like this:
$ git --bare fetch https://firstname.lastname@example.org/kennwilson/asset-helper.git master:master Password: remote: Counting objects: 20, done. remote: Compressing objects: 100% (19/19), done. remote: Total 20 (delta 7), reused 0 (delta 0) Unpacking objects: 100% (20/20), done. From github.com:kennwilson/asset-helper * [new branch] master -> master
If that looks like what you got, you’re done and ready to move on.
Scenario 2: Create a new repository
In our second example, we’re creating a remote repository for some code that only exists on your local development machine. This is also very easy, but it requires some steps to be done locally. Open another terminal window and move into your local Git repository directory, then run the following lines to add the new remote repository URL and push up your code. In this example, dev.example.com will be replaced with the hostname of your own development server.
$ git remote add origin email@example.com:repository.git $ git push origin master
When pushing your code, you’ll see something similar to this:
$ git push origin master Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (5/5), 4.49 KiB, done. Total 5 (delta 0), reused 0 (delta 0) To firstname.lastname@example.org:repository.git * [new branch] master -> master
Look good? Let’s move on.
You now have a fully functional Git repository on your server, and a dedicated account your developers can use to access it. Our final step is to log out of the git account and change its shell to
git-shell. This will ensure that users can’t run arbitrary commands with this shared user account.
$ logout $ sudo chsh -s /usr/bin/git-shell git
To begin working, just clone your new repository and get started:
$ git clone email@example.com:repository.git
If you’ve been using GitHub, you already know what to do. But if remote Git repositories are new to you, you’ll need to adjust your workflow a bit to push your changes to the server and pull down changes made by the other developers on the team.
As we saw in the previous step, pushing up your code is as easy as:
$ git push origin master
Syncing changes committed by other developers is similarly easy: you’ll use either
git fetch or
git pull. The two are similar except that
git pull essentially does
git fetch && git merge, so use
git fetch if you prefer to handle merging yourself. Explaining that in more detail is beyond the scope of this article but there are a lot of good tutorials out there for basic Git usage and workflows.