Setup a Home Git Server
Your own personal, private Git server in your home!

Features
A local, private, free git server.
HTTP acces to Gitweb interface in a web browser; local use of gitk and git-gui graphical interfaces that are bundled with git.
HTTP access can be either authenticated (requires username and password), or unauthenticated.
Git repositories can be stored on the server somewhere having a long path name, e.g. /mnt/my-raid/www/git/repos, but accessed with a shorter path, e.g. /git, by using a symbolic link.
The HTTP URL usage is a lot like using GitHub or GitLab.
Can also fully interact with the git server using SSH (slightly more complicated URL) without a password.
Do not need to enter the server to create new repos. Can either use passwordless-SSH, or a PHP form over HTTP in a web browser.
The PHP form sanitizes the input, returns a message if the repo name is already used on the server, and returns an error message if the wrong format has been given for the repo name.
Machines
Server
A Raspberry Pi 5 8Gb machine having:
- Raspberry Pi Lite OS (Debian Bookworm)
- RAID storage
Clients
Local machines running one of:
- Raspberry Pi OS
- Windows 11
- MacOS
Software used
- NGINX
- Git
- Gitweb
- fcgiwrap
- apache2-utils
- PHP-FPM
Minimal config
Note both sytem and global configs:
$ git config --global user.name "<user>"
$ git config --global user.email <email address>
$ git config --system init.defaultBranch master
Repo root
Create and configure a directory to house repositories.
I used /mnt/raid10/git as <repo-root>.
We make the git root directory, <repo-root>, user and group the same as needed for using the HTML interface, and give this useer and group full permissions, but others only read and execute permissions.
$ sudo mkdir <repo-root>
$ sudo chown -R www-data:www-data <repo-root>
$ sudo chmod -R 775 <repo-root>
Give it a shorter symbolic link.
$ sudo ln -s <repo-root> /git
Do NOT do mkdir /git first, just run the ln -s command.
Gitweb Config
I used:
<repo-root> = /mnt/raid10/git
In the gitweb config file
/etc/gitweb.conf
change the value of $projectroot
# path to git projects (<project>.git)
#$projectroot = "/var/lib/git";
$projectroot = "<repo-root>";
Contents of this file before our major, final reinstall:
pis@radxahost:~ $ cat /etc/gitweb.conf
# path to git projects (<project>.git)
#$projectroot = "/var/lib/git";
#$projectroot = "/var/www/html/git";
$projectroot = "/mnt/raid1/git";
# directory to use for temp files
$git_temp = "/tmp";
# target of the home link on top of all pages
#$home_link = $my_uri || "/";
# html text to include at home page
#$home_text = "indextext.html";
# file with project list; by default, simply scan the projectroot dir.
#$projects_list = $projectroot;
# stylesheet to use
#@stylesheets = ("static/gitweb.css");
#@stylesheets = ("/usr/share/gitweb/static/gitweb.css");
# javascript code for gitweb
#$javascript = "static/gitweb.js";
#$javascript = "/usr/share/gitweb/static/gitweb.js";
# logo to use
#$logo = "static/git-logo.png";
#$logo = "/usr/share/gitweb/static/git.logo.png";
# the 'favicon'
#$favicon = "static/git-favicon.png";
#$favicon = "/usr/share/gitweb/static/git-favicon.png";
# git-diff-tree(1) options to use for generated patches
#@diff_opts = ("-M");
@diff_opts = ();
Creating a new repo
I used:
<repo-root> = /mnt/raid10/git
<gitweb-root> = /usr/share/gitweb which I believe is the default for Gitweb.
<hostname> = intra
SSH without a password
Since we already setup SSH without a password, we can do this:
$ ssh user@server git init --bare <project>.git
e.g.
$ ssh [user@]<hostname> git init --bare --share /path/to/project.git
HTTP/PHP Form
Here we create a PHP form accessed at http://<hostname>.local/newrepo that can be used to create a new repo over the LAN.
Ref: https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/Nginx-PHP-FPM-config-example
If not already installed, install PHP fpm version and restart NGINX:
$ sudo apt-get install php8.2-fpm -y
$ sudo systemctl restart nginx
Create and configure the following directory:
$ mkdir <gitweb-root>/newrepo
$ chown -R www-data:www-data <gitweb-root>/newrepo
$ chmod -R 755 <gitweb-root>/newrepo
Create a bash script <gitweb-root>/newrepo/create_repo.sh with the following contents:
#!/usr/bin/env bash
mkdir /mnt/raid10/git/$1.git
cd /mnt/raid10/git/$1.git
git init --bare --share=group
git update-server-info
chgrp -R www-data .
chmod -R g+rw .
find -type d -exec chmod g+s {} +
and configure it to be executable:
$ chmod +x <gitweb-root>/newrepo/create_repo.sh
and make sure all SSH users are in the www-data group, e.g. do
$ sudo usermod -a -G www-data <git-user>
for each git user.
Create a php file <gitweb-root>/newrepo/index.php with the following contents:
<!DOCTYPE HTML>
<html>
<head>
<title>Create New Repository</title>
</head>
<body>
<?php
session_start();
?>
<a href="/">Return to Home Page</a>
<h3>Create a New Repository</h3>
<form method="post" action="/newrepo/newrepoexe.php">
Name: <input type="text" name="name">
<input type="submit" name="submit" value="Create Repo">
</form>
<?php
echo "<br><br>";
if($_SESSION['name_submitted'] != ""){
print_r($_SESSION['output']);
}
$_SESSION['name_submitted'] = "";
?>
</body>
</html>
and another php file <gitweb-root>/newrepo/newrepoexe.php with the following contents:
<?php
session_start();
function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
$name = test_input($_POST["name"]);
$_SESSION['name_submitted'] = $name;
$_SESSION['output'] = null;
$_SESSION['retval'] = null;
$correct_format = preg_match("/^[A-Za-z0-9_-]+$/", $name);
$existing_repos = file_get_contents('http://<hostname>.local/index.cgi?a=project_index');
$is_in_existing_repo_list = preg_match("/" . $name . ".git" . "/i" , $existing_repos);
if ($correct_format & !$is_in_existing_repo_list) {
exec("/usr/share/gitweb/newrepo/create_repo.sh $name 2>&1", $output, $retval);
}
if(!$correct_format){
$output = 'Name: ' . $name . '<br>Error: repo name can only include letters, numbers, underscores, and dashes.';
$retval = 1;
}
if($is_in_existing_repo_list){
$output = 'Error: a repo named ' . $name . ' already exists.';
$retval = 1;
}
$_SESSION['output'] = $output;
$_SESSION['retval'] = $retval;
header("Location: /newrepo/index.php");
?>
You should now be able to go to
http://<hostname>.local/newrepo
in a web browser on a machine on the same network and use the form to create a new repo.
Any new repos made with this form should show up on the gitweb homepage.
Finalize
After a repo has been created on the server with either method above, it is necessary to push a non-empty repo to it before doing any cloning or pulling from it.
We do this from another machine using either an existing repo or making a minimal non-empty repo:
$ mkdir ~/testproject
$ cd ~/testproject
$ git init
$ git remote add origin http://<hostname>.local/git/<project-name>.git
$ touch README.md
$ git add .
$ git commit -a -m "some message"
$ git push origin master
Using a repo
After a non-empty project has been pushed to a new repo on the server, other users can clone or pull it.
$ git clone http://<hostname>.local/git/<project>.git
$ mkdir ~/testproject
$ cd ~/testproject
$ git init
$ git remote add origin http://<hostname>.local/git/<project>.git
$ git pull origin master
Note that on Windows it might work better to use the user-URL like:
$ git clone http://<user>@<hostname>.local/git/<project-name>.git
# or
$ mkdir ~/testproject
$ cd ~/testproject
$ git init
$ git remote add origin http://<user>@<hostname>.local/git/<project>.git
$ git pull origin master
To suggest an edit or correction to this page, please click the "Edit this page" button below or at the top to access the source file on GitHub.
