wiki:Howto/GitPublicRepositoryInstallAndConfigure

Install and Configure a Public git Repository

This explains how to install and configure a public git server that can host multiple projects providing read-only anonymous access and read/write for authenticated commiters, along with the gitweb web-browser interface.

Install

On Ubuntu this is quite simple:

sudo apt-get install git-core git-daemon-run gitweb

You might want to update to the latest version from git's own repository.

git clone git://git.kernel.org/pub/scm/git/git.git
cd git

Build the latest release

Easy way to ensure build dependencies are installed for stand-alone source that has an earlier Ubuntu/Debian? package:

sudo apt-get build-dep git-core

Build using the same options as the Ubuntu package uses ( extracted from Ubuntu build logs)

/usr/bin/make all test \
    CC='gcc' CFLAGS='-g -Wall -O2' NO_OPENSSL=1 GITWEB_CONFIG=/etc/gitweb.conf \
    prefix=/usr mandir=/usr/share/man INSTALLDIRS=vendor WITH_P4IMPORT=1 \
    PYTHON_PATH=/usr/bin/python TCLTK_PATH=/usr/bin/wish8.4

/usr/bin/make -CDocumentation man ASCIIDOC8=YesPlease

/usr/bin/make -CDocumentation man html ASCIIDOC8=YesPlease

Install using the same options:

usr/bin/make install install-doc \
    CC='gcc' CFLAGS='-g -Wall -O2' NO_OPENSSL=1 GITWEB_CONFIG=/etc/gitweb.conf \
    prefix=/usr mandir=/usr/share/man INSTALLDIRS=vendor WITH_P4IMPORT=1 \
    PYTHON_PATH=/usr/bin/python TCLTK_PATH=/usr/bin/wish8.4

Configure

Firewall

These rules need adding to the netfilters/iptables start-up scripts (usually in /etc/iptables.up.rules).

Allow access to port 9418 for TCP

# git-daemon
-A INPUT -p tcp -m tcp --dport 9418 -j ACCEPT

To provide secure encrypted authenticated access for commiters you might want to ensure SSH (port 22) is allowed. (However, in some circumstances it is better to leave port 22 closed on public interfaces and only provide SSH access through a virtual private network tunnel using OpenVPN or similar):

# For git over ssh (allows push)
-A INPUT -p tcp -m tcp -s 84.12.34.240/29 --dport 22 -j ACCEPT

Ensure the firewall rules are being enabled when the network starts. Usually that is using a post-up rule in the network configuration.

/etc/network/interfaces

# The first network card - this entry was created during the Debian installation
# (network, broadcast and gateway are optional)
iface eth0 inet static
        address 10.1.2.3
        netmask 255.255.255.0
        gateway 10.1.2.254
        post-up iptables-restore < /etc/iptables.up.rules

git-daemon

The git-daemon-run package installs a  runit service in /etc/sv/git-daemon/. Edit the run file and set the paths for virtual hosting. Because I provide for a git repository for each domain account I use the interpolated-path option to extract the hostname the git client connected to and build the path based on it. This is rather like apache's VirtualHost where the site presented is determined by checking the Host: header. In this case it is git repositories not web sites that can share the same IP address:

/etc/sv/git-daemon/run

#!/bin/sh
exec 2>&1
echo 'git-daemon starting.'
exec /usr/lib/git-core/git-daemon --syslog --verbose --base-path=/home --interpolated-path=/home/%H/git%D 

Once you've created some public repositories, confirm the service is running using something like this:

git ls-remote git://tjworld.net/linux-2.6-tegra-adam
bc91e98a9822e4c5c390e87257fbc5829457af6c	refs/heads/smba1002_10.9.7

Create per-account directories

First create 'base' directories in all home directories with correct ownership:

sudo su

awk -F: '$3 > 999 && substr($6,1,5) == "/home" {print $3,$4,$6}' /etc/passwd | while read account; \
   do AUID=${account%% * /home*}; AGUID=$(expr "${account}" : '.* \([0-9]\+\) .*'); DIR=${account##* }; \
   [ ! -e ${DIR}/git ] && mkdir ${DIR}/git && chown $AUID:$AGUID ${DIR}/git; done

exit

Or, if doing it manually for each user account:

cd ~
mkdir git

Create repositories

Create empty repository and make it ready for public access. Set ${HOST_DOMAIN} and ${PROJECT} appropriately:

export GIT_DIR="/home/${HOST_DOMAIN}/git/${PROJECT}.git"
git-init

Enable public access

To enable a repository to be available to anonymous public access:

touch $GIT_DIR/git-daemon-export-ok

Enable gitweb

The Perl git-web script allows web-browsing of the repository. After following these instructions it will be available at  http://${HOST_DOMAIN}/perl/gitweb.cgi

Name the repository

To avoid gitweb reporting the project name as:

Unnamed repository; edit this file to name it for gitweb.

Edit the repository's description file and make it more descriptive:

echo "Experimental branch" > ${GIT_DIR}/description

Install Perl prerequisites

Some preparation is required to support gitweb fully. The primary issue is that if the apache VirtualHost is using suexec to impose security it is not possible to pass environment variables to CGI scripts, and therefore gitweb can't be told to use a per-account configuration using GITWEB_CONFIG. An additional issue is that by default all Perl scripts will be handled by the cgi-bin handler mod_cgid. For performance it would be better to have mod_perl installed.

Install mod_perl and supporting Perl bundles needed by apache:

sudo apt-get install libapache2-mod-perl2

Create a server-wide apache Perl configuration. As root create this file:

/etc/apache2/mods-available/perl.conf

PerlModule ModPerl::Registry

Create a symbolic link to it so apache reads it at start-up:

sudo su
cd /etc/apache2/mods-enabled
ln -s ../mods-available/perl.conf perl.conf
exit

Per-account gitweb configuration

In the user's git directory create a link to the gitweb system installation directory:

cd ~
ln -s /usr/share/gitweb git/gitweb

Copy the default configuration file so the site settings can be customised:

cd ~
cp /etc/gitweb.conf git/gitweb.conf

Edit git/gitweb.conf and set values for the user account. Set the path to the supporting files to something other than the root of the web site to avoid clutter. Comment out the favicon setting if the site is using its own. Replace ${HOST_DOMAIN} with the correct path:

# path to git projects (<project>.git)
$projectroot = "/home/${HOST_DOMAIN}/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
$stylesheet = "gitweb.css";

# logo to use
$logo = "git-logo.png";

# the 'favicon'
# $favicon = "git-favicon.png";

Add the GITWEB_CONFIG environment variable to the web-site's configuration file so that gitweb uses per-site configuration rather than the global configuration set in /etc/gitweb.conf. Also add Alias and Location directives. Replace ${HOST_DOMAIN} with the correct path.

/etc/apache2/sites-available/${HOST_DOMAIN}.conf:

SetEnv GITWEB_CONFIG /home/${HOST_DOMAIN}/git/gitweb.conf
Alias /gitweb/ /home/${HOST_DOMAIN}/git/gitweb/
<Location /gitweb/*.cgi>
    SetHandler perl-script
    PerlHandler ModPerl::Registry
    Options +ExecCGI
    PerlSendHeader On
    allow from all
</Location>

Allow a default index page if a web browser navigates to /gitweb/ rather than the real /gitweb/index.cgi. Add "index.cgi" to DirectoryIndex in the web-site's configuration file:

DirectoryIndex index.php index.html index.py index.cgi

Restart the server.

sudo apache2ctl restart

Use

Accessing the web interface

Browse to http://${HOST_DOMAIN}/gitweb/

Updating the Public Repository

To push your local private repository to the remote public repository there are several options. If you have enabled SSH access then:

To update the remote branch master from the local branch of the same name:

cd /home/all/Projects/packeteer

git push ssh://tjworld.net@tjworld.net/~tjworld.net/git/packeteer.git master
Password: 
Counting objects: 21, done.
Compressing objects: 100% (21/21), done.
Writing objects: 100% (21/21), 5.47 MiB | 42 KiB/s, done.
Total 21 (delta 2), reused 0 (delta 0)
refs/heads/master: 0000000000000000000000000000000000000000 -> f499ede17792694ddcd25fed589e794a72ee35fa
To ssh://tjworld.net@tjworld.net/~tjworld.net/git/packeteer.git
 * [new branch]      master -> master

If you don't use public ssh but have a VPN connection then you might do something like this:

cd ~
mkdir remote
# mount remote server in local file-system
sshfs tjworld.net@vpn.lan.tjword.net ~/remote

cd /home/all/Projects/packeteer

git status
# On branch master
nothing to commit (working directory clean)

git push ~/remote/git/packeteer.git master

Note: This method of using an sshfs mount seems to confuse git. It seems to take much longer to push the change, and it creates a new local branch entry that matches the remote repository path. For that reason it is better to use the ssh protocol but to the private VPN host:

git push ssh://tjworld.net@vpn.lan.tjworld.net/~tjworld.net/git/packeteer.git master

Cloning Public Repository by Other Developers

Anyone who wants a copy of the public repository does a simple clone:

git clone git://tjworld.net/packeteer.git

Collaborating

If another developer has a clone of the public repository already and you have published a change, they update using:

git fetch

If you are testing experimental code you might have a branch for it:

git branch experimental
git checkout experimental
Switched to branch "experimental"

echo "Editing files for experimental code" >> changelog

git add changelog
git commit -a -s -m "Experimental commit"
Created commit 98bc7fa: Experimental commit
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 changelog

git push ~/remote/git/packeteer.git experimental
Counting objects: 4, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 357 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To ~/remote/git/packeteer.git
 * [new branch]      experimental -> experimental

The other developer might then update their clone:

git fetch
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2)remote: , done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From git://tjworld.net/packeteer.git
 * [new branch]      experimental -> origin/experimental

git branch -r
  origin/HEAD
  origin/experimental
  origin/master

git checkout origin/experimental
Note: moving to "origin/experimental" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 98bc7fa... Experimental commit

git log -1
commit 98bc7fa8777160f0849b4e78f8522a9ae9ce2497
Author: TJ <linux@tjworld.net>
Date:   Sun Apr 20 20:24:30 2008 +0100

    Experimental commit
    
    Signed-off-by: TJ <linux@tjworld.net>

Quickly Create New Public Project Repository

GIT_DIR="/home/${HOST_DOMAIN}/git/${PROJECT}.git"
git-init
touch $GIT_DIR/git-daemon-export-ok
echo "Project Name" > ${GIT_DIR}/description