When it comes to Git hosting solutions, nothing beats the control and flexibility that comes with hosting Git on your own server and domain.
This guide will document how to create a personal self-hosted repository portfolio by installing and configuring cgit on top of an existing Apache installation. Many projects use cgit for their code management, including the Linux kernel and Gentoo, and Apache is the most popular web server in the world, making this a highly accessible self-hosted Git solution.
Prior technical knowledge will be very helpful for following this guide’s procedures, especially knowledge of Git and Apache. In spite of this, the guide attempts to explain complex technical subjects in plain language to make it as accessible as possible for the general public.
The guide will provide brief summaries of the many technical concepts related to its setup procedures, but will not distract from the focus of the guide with lengthy explanations of these subjects. In those cases, the guide will link to external resources that provide a deeper background on those concepts.
This guide assumes the existence of a working Apache installation with support
for both CGI and the
mod_rewrite
module. It also requires
SSH access to the server running
Apache. Permissions for modifying Apache’s configuration and the web server’s
file system will also be essential. Lastly, since cgit is only distributed as
source code, the server must have a C build environment and the make
utility
or one of its derivatives (gmake
, etc.).
Setting up these requirements is beyond the scope of the guide, but if you already have a web server, it’s likely that you already meet all of these requirements. Consult your provider’s documentation to see if your environment is prepared to implement the procedures documented in this guide.
This guide was written against Apache 2.4 and cgit v1.2.1. Other version combinations may work as well.
Log into the Web server over SSH:
local ~ $ ssh remoteuser@example.com
remote ~ $ hostname
example.com
remote ~ $
While not strictly necessary, configuring Apache before installing cgit is the best course of action. Not only does the Apache configuration declare the location of the cgit application on the server, but it also serves to implement a layer of security around the program before allowing any public access, which is always a good idea.
Configuration keywords in Apache are called directives. Each configuration setting in this guide will receive a brief explanation, but you are strongly encouraged to cross-check the settings listed here against the full list of Apache directives instead of blindly trusting the examples as they are listed. This will help bolster your comprehension of the source material and enable you to innovate upon the examples in this guide for your own specific needs.
cgit should receive a dedicated directory on your site. This will distinguish it from the rest of the site’s contents and lead to easier configuration and management through Apache’s directory-specific configuration options.
This guide will assume your website is https://example.com
, that the site’s
root directory is /srv/web/mysite
, and install cgit in a subdirectory of this
path called code
. Therefore, cgit’s full installation path in this guide will
be /srv/web/mysite/code
, accessible over the Web at
https://example.com/code
. Be sure to replace these example names as they
appear throughout the guide with whatever names your setup will use.
After deciding upon a directory, go ahead and create the directory on the server:
remote ~ $ cd /srv/web/mysite
remote /srv/web/mysite $ ls -la
drwxr-xr-x 13 remoteuser remoteuser 32 May 17 07:24 .
drwxr-xr-x 8 remoteuser remoteuser 12 Aug 16 13:19 ..
-rw-r--r-- 1 remoteuser remoteuser 45 Jan 14 08:41 index.html
remote /srv/web/mysite $ mkdir code
remote /srv/web/mysite $ ls -la
drwxr-xr-x 13 remoteuser remoteuser 32 May 17 07:24 .
drwxr-xr-x 8 remoteuser remoteuser 12 Apr 16 13:19 ..
-rw-r--r-- 1 remoteuser remoteuser 45 Jan 14 08:41 index.html
drwxr-xr-x 2 remoteuser remoteuser 20 Jun 11 20:42 code
remote /srv/web/mysite $
Notice the use of the -la
flags with the ls
command. The -l
flag provides
a long listing that includes additional information about each file, such as
the file’s ownership and permissions settings. The -a
flag ensures that
normally-hidden files starting with the .
character get included in the
listing. The two listed files, .
and ..
, are not really files but default
locations available for every directory. They represent the current directory
(.
) and the parent directory (..
).
It is essential to make sure the file ownership and permissions settings are accurate before proceeding. If these settings are too loose, they will allow unwanted access to the website, and if they are too strict, the site will become inaccessible.
Take a look at the long listing for the new directory along with a legend explaining the settings related to file ownership and permissions:
permissions user group
drwxr-xr-x 2 remoteuser remoteuser 20 Jun 11 20:42 code
The user and group settings indicate who owns the file. A user represents an individual account on the system, and a group can contain any number of users sharing common permissions on files owned by that group.
Many systems, including this one, will create a one-user group for each user,
with the group’s name being the same as that one user. Your system may not do
this, in which case you user may be part of a group called users
which appears
in this column instead.
You can use the whoami
command to verify your user name and the groups
command to show which groups your user belongs to:
remote /srv/web/mysite $ whoami
remoteuser
remote /srv/web/mysite $ groups
remoteuser ssh www
remote /srv/web/mysite $
With these ownership settings explained, take a look at the block of ten characters at the start of the line.
The first character indicates the type of file, which in this case is d
for
directory. A regular file like index.html
will list a -
here instead. There
are many other types of special files not featured in this guide, such as
sockets and links. Each of these special file types will show a different
character here.
The next nine characters define the file’s permissions. It contains three
sections of three characters each. In order, these sections define permissions
for the file’s user, the file’s group, and everyone else on the system (called
“others”). Each section defines the r
ead, w
rite, and ex
ecute permissions
for that file, using the appropriate character if the permission is enabled and
-
if the permission is disabled.
This particular listing enables full read, write, and execute permissions for our user. The group and other settings allow for read and execute permissions, but importantly not write permissions. These are good general-purpose permissions for new directories, making it likely that your server will create your new directory with these permissions as well.
Apache generally runs as its own dedicated user which should not be able to modify (or write to) the website’s files, but which must be able to retrieve the contents of the website’s files (read permission) and run any programs which generate the website’s contents (execute permissions). The current permissions allow for this level of access, so this step is complete.
In addition to cgit’s public directory, it is also a good idea to declare a restricted directory for cgit. This directory should provide full access for your login user plus read and execute permissions for Apache, but deny all other forms of access.
Many hosting providers offer such a directory for Web applications to store such
privileged data. This guide will use /var/webdata/
to represent this
directory:
remote /srv/web/mysite $ ls -la /var/webdata/
drwxrwx--- 13 remoteuser apache 32 Mar 9 22:22 .
drwxr-xr-x 8 root root 12 Feb 26 09:04 ..
remote /srv/web/mysite $
When providing ls
a directory instead of a regular file, ls
will list the
contents of that directory instead of the directory itself. This can be an issue
when trying to determine the owner and permissions settings of the directory
itself, but using -a
will list those properties under the directory’s
self-reference, .
.
This directory offers full permissions for both your SSH login user as well as
users in the apache
group. Just as your user has a special group for itself,
this server’s Apache user also has its own group. Others are not allowed any
permissions to the contents of this directory.
While Apache has write permissions to this directory, it is possible to take away these permissions by creating a subdirectory with a narrower set of permissions:
remote /srv/web/mysite $ mkdir /var/webdata/cgit
remote /srv/web/mysite $ ls -la /var/webdata/
drwxrwx--- 13 remoteuser apache 32 Mar 9 22:22 .
drwxr-xr-x 8 root root 12 Feb 26 09:04 ..
drwxr-xr-x 2 remoteuser remoteuser 20 Jun 11 20:49 cgit
remote /srv/web/mysite $
Notice how the new cgit
directory has different user, group, and permissions
settings than the /var/webdata
. These settings may look more permissive
because they allow read and execute access for the file’s group and all other
users.
However, these permissions ultimately take away write permissions for everyone
except the owning user (notice the -
in the permission bits for group and
others). This achieves the desired effect of preserving Apache’s read and
execute permissions while removing its write permissions.
If your host allows you the ability to alter permissions, you can make these permissions more specific for some additional security:
remote /srv/web/mysite $ sudo chown remoteuser /var/webdata/cgit
Password:
remote /srv/web/mysite $ sudo chgrp apache /var/webdata/cgit
Password:
remote /srv/web/mysite $ sudo chmod g=rx,o-rwx /var/webdata/cgit
Password:
remote /srv/web/mysite $ ls -la /var/webdata/
drwxrwx--- 13 remoteuser apache 32 Mar 9 22:22 .
drwxr-xr-x 8 root root 12 Feb 26 09:04 ..
drwxr-x--- 2 remoteuser apache 20 Jun 11 20:49 cgit
remote /srv/web/mysite $
chown
changes which user owns the provided file or directory. In this example,
it effectively does nothing since remoteuser
already owns the cgit
directory, but it is listed here nonetheless because it is an essential command
to know for changing file ownership.
chgrp
changes the group for the provided file or directory. In this case, it
changes the group of the cgit
directory to apache
.
chmod
changes file permissions. This example uses =
to set g
roup
permissions to r
ead and ex
ecute (taking away write permissions if they are
present) and uses -
to take all permissions from o
thers.
The best way to acquire more information about these commands is by consulting
your server’s man
pages:
remote /srv/web/mysite $ man chown
remote /srv/web/mysite $ man chgrp
remote /srv/web/mysite $ man chmod
Should your host not provide a restricted directory for Web applications, it may be necessary to use the public directory declared in the previous section for these resources. Of course, this has the notable disadvantage of exposing these resources to the public. Consult with your hosting provider or their documentation to determine if they offer a restricted directory for Web application data before resorting to this workaround.
This restricted directory will be used later in the guide when configuring cgit and its build process. Until then, simply make note of its location on your server.
Apache has two main types of configuration files. httpd.conf
contains Apache’s
global configuration, and each directory on a website can contain a file called
.htaccess
that contains configuration settings for that directory and all of
its subdirectories.
Depending on your setup, you may not have access to one of these configuration methods, but you will need to be able to use at least one of them.
Decide which one to use and open it using your favorite text editor:
remote /srv/web/mysite $ vim /etc/apache2/httpd.conf
remote /srv/web/mysite $ vim /srv/web/mysite/code/.htaccess
httpd.conf
may be in a different location than /etc/apache2/httpd.conf
on
your server. Check your server configuration along with your hosting provider’s
documentation to determine the location of this file in your environment.
Add the following base configuration in your text editor:
<Directory "/srv/web/mysite/code">
Options +ExecCGI
AddHandler cgi-script .cgi
DirectoryIndex cgit.cgi
</Directory>
The <Directory "/srv/web/mysite/code">
tag causes all configuration settings
until the closing </Directory>
tag to apply only to the /srv/web/mysite/code
directory, which is desirable for creating an Apache configuration exclusive to
cgit. If you are using .htaccess
to supply your configuration settings, do
not make a new <Directory>
section and simply ignore these tags when listed
in these examples. Apache already knows to restrict the configuration based on
the location of the .htaccess
file, making it unnecessary and potentially
causing configuration issues.
The Options +ExecCGI
setting enables server-side code execution, which is
absolutely required in order to run cgit or any other CGI programs.
The AddHandler cgi-script .cgi
setting tells Apache that files ending in .cgi
are programs, not normal web pages or files. Instead of displaying them to the
user like web pages or offering them for download as files, Apache will now
attempt to run them as programs and display the results of that program to your
visitors. The cgit program is called cgit.cgi
, so it will be covered by this
configuration setting because of its matching .cgi
extension.
Likewise, the DirectoryIndex cgit.cgi
setting tells Apache to use cgit.cgi
as the default file name for the directory /srv/web/mysite/code
. Since a URL
like https://example.com/code/
does not contain a filename, Apache will look
for a file with a default name in the requested directory. This default name is
typically index.html
, which will not exist in the final setup, so we must use
this setting to change that default name to cgit.cgi
. Without this setting,
Apache will exhibit various undesirable behaviors like showing the contents of
the directory or returning an error.
If your configuration looks accurate compared to the example above, save your changes to retain your current progress before moving on to the next section.
The rest of the configuration will make use of Apache’s mod_rewrite
module,
also called the rewrite engine. This is a powerful Apache feature that makes use
of regular expressions to
analyze URLs for achieving a wide range of helpful effects. The rewrite engine
works by applying any number of rewrite rules, each of which can have a number
of conditions that must be true in order for that rule to take effect.
Apache uses Perl Compatible Regular Expressions, or PCRE for short. Regular expressions provide the bulk of Apache’s rewrite engine’s flexibility. This guide will describe the regular expression patterns used in this guide, but will not provide a comprehensive discussion of all available PCRE patterns. That stated, the official PCRE pattern guide, especially the section on characters and metacharacters, is an excellent resource for learning how to build your own regular expression patterns and confirming the accuracy of the examples in this guide.
Rewrite rules are undeniably useful when properly implemented, but a poorly
written rewrite rule can create an infinite
loop and cause your site to become
inaccessible until the rewrite rule is either fixed or removed. Pay close
attention to syntax and triple-check your work before saving your changes, and
be sure to consult Apache’s official mod_rewrite
documentation.
This guide will use the rewrite engine for two major purposes:
Although this setup does not allow pushes over HTTP(S), you will be able to push to your Git repositories over SSH as documented later in the guide.
With this overview of the rewrite engine provided, go ahead and activate the rewrite engine in your configuration file:
<Directory "/srv/web/mysite/code">
Options +ExecCGI
AddHandler cgi-script .cgi
DirectoryIndex cgit.cgi
RewriteEngine on
</Directory>
The RewriteEngine on
setting enables the rewrite engine. It is a simple
setting without any further options beyond on
and off
.
Apache expects the cgit.cgi
program name to appear in URLs that request cgit
and the repositories it is hosting.
This stated, it is not only possible to remove the program name from these URLs, but also desirable:
For these reasons, let’s add a first rewrite rule that will remove cgit.cgi
from URLs referencing cgit:
<Directory "/srv/web/mysite/code">
Options +ExecCGI
AddHandler cgi-script .cgi
DirectoryIndex cgit.cgi
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
</Directory>
This is a single rewrite rule containing two conditions, both of which need to be true in order to process the rewrite rule.
The RewriteCond %{REQUEST_FILENAME} !-f
setting takes the URL the visitor
requested (%{REQUEST_FILENAME}
) and check to see whether it does not (!
)
match an actual file (-f
) on the server. Requests to cgit itself will never be
actual files on the server, so this condition allow requests for such resources
as cgit’s CSS stylesheet and logo to occur without unnecessarily modifying the
URL and causing an HTTP 404 error.
The RewriteCond %{REQUEST_FILENAME} !-d
setting is very similar to the
previous setting, except that it matches against a directory (-d
) instead of a
file (-f
). If there are any subdirectores in the directory where cgit exists,
this rule will ensure that requests for those directories will successfully
resolve without activating the rule and subsequently causing an error.
The next line, RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
, provides the
actual rewrite rule. The first part of the setting uses a regular expression to
match any character (.
) zero or more times (*
), and to capture the results
of that match (()
) for use in the second part, which performs the actual URL
modification. That second part /code/cgit.cgi/$1
simply appends that captured
result ($1
) to the string /code/cgit.cgi/
, completing the silent and
seamless modification of the URL on the backend.
The third part of this rule deserves special attention. A rewrite rule can apply
any number of processing options called flags, supplied as the last part of the
rule in square brackets ([]
). This particular rule applies the END
flag to
stop processing any further rewrite rules for the provided request, and the
QSA
makes sure that the final URL includes the query
string from the original request.
CGI programs make extensive use of query strings to affect their behavior, so it
is essential to include the QSA
flag to preserve cgit’s functionality. Query
strings will receive further discussion in the next section, where they play a
key role in implementing the next rewrite rule.
cgit supports collaborative development by accepting Git pushes, but it does not implement any authentication by default, making this an unsafe option. While cgit does provides a simple authentication script, the least complicated solution to this is to simply deny all pushes over HTTP(S).
The rewrite engine offers a way to implement this behavior:
<Directory "/srv/web/mysite/code">
Options +ExecCGI
AddHandler cgi-script .cgi
DirectoryIndex cgit.cgi
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
RewriteCond %{QUERY_STRING} service=git-receive-pack
RewriteRule .* - [END,F]
</Directory>
As noted in the previous section, CGI programs depend on query strings to affect
their behavior. Query strings appear at the end of a URL after a ?
character
and generally define a list of variables and their values separated with &
.
For instance, the URL https://example.com/search?q=classical+music&lang=en
contains the query string q=classical+music&lang=en
, which sets the variable
q
to classical+music
and the variable lang
to en
. It’s important to note
that the ?
is not actually part of the query string itself, serving merely as
a separator between the query string and the rest of the URL.
The Git client uses query strings to define its interactions with a Git server,
which can be used to detect Git pushes. The RewriteCond
%{QUERY_STRING} service=git-receive-pack
setting performs precisely this
action, inspecting the query string for the string service=git-receive-pack
.
This string will only ever appear when someone is attempting to push changes to
our Web server over Git.
When this occurs, the RewriteRule .* - [END,F]
setting denies access by
returning an HTTP 403 Forbidden error. The first part of this rule uses nearly
the same pattern as the previous rule, just without attempting to capture the
full URL (.*
) using ()
. This capture is unnecessary, because the second part
of this rule tells Apache to leave the URL unmodified (-
).
Instead, this rule uses its flags to affect Apache’s behavior. The END
flag
introduced in the previous section is used here as well, but this rule also sets
the F
flag to return a HTTP 403 Forbidden error. It is this flag which
achieves the desired effect of denying pushes. Later in the guide, you will
configure your repositories to allow pushing using your existing SSH key.
Your final configuration should look like this:
<Directory "/srv/web/mysite/code">
Options +ExecCGI
AddHandler cgi-script .cgi
DirectoryIndex cgit.cgi
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /code/cgit.cgi/$1 [END,QSA]
RewriteCond %{QUERY_STRING} service=git-receive-pack
RewriteRule .* - [END,F]
</Directory>
Remember that if you are using .htaccess
for your configuration file, do not
include either the <Directory "/srv/web/mysite/code">
tag or its closing
</Directory>
tag.
If everything looks accurate, save the file and close your editor to return to the command line. It is time to build and install cgit.
cgit is distributed as source code, so you must compile it into a form that your Web server understands as machine code.
The first step to building cgit is to clone its source repository. While this can occur in any directory where you have full permissions, it’s best to find a personal directory which only your user can access.
Hosting providers generally offer their users some sort of dedicated personal space restricted from all other users. Setting a home directory for each user is probably the easiest means of implementing such a space, especially since it also gives users a place to define personal login settings.
The home directory could be located anywhere on the system, but its
location will always be accessible through the shorthand ~
or the $HOME
variable:
remote /srv/web/mysite $ echo ~
/home/remoteuser
remote /srv/web/mysite $ echo $HOME
/home/remoteuser
remote /srv/web/mysite $
echo
can show the contents of variables, which always begin with $
, as well
as the ~
shorthand.
In the event that these echo
commands return blank lines, your login user does
not have a home directory defined. Consult your hosting provider to determine
whether they offer such personal storage space and how to access it to ensure
that nobody will access your cloned cgit repository.
Should you have a home directory defined, take a look at its permissions:
remote /srv/web/mysite $ ls -la $HOME
drwx------ 10 remoteuser remoteuser 21 Jun 20 02:33 .
drwxr-xr-x 8 remoteuser remoteuser 8 Aug 16 13:42 ..
-rw-rw-r-- 1 remoteuser remoteuser 113 Sep 12 20:10 .bash_profile
As the ownership and permissions settings for .
indicate, remoteuser
has
full access to this directory (rwx
), but permissions for anyone else are
completely denied (------
). Therefore, this directory is accessible only for
your login user, making it an ideal place for cloning cgit.
Switch to the directory and clone cgit:
remote /srv/web/mysite $ cd /home/remoteuser
remote /home/remoteuser $ git clone https://git.zx2c4.com/cgit
Cloning into 'cgit'...
remote: Enumerating objects: 6921, done.
remote: Counting objects: 100% (6921/6921), done.
remote: Compressing objects: 100% (2624/2624), done.
remote: Total 6921 (delta 4952), reused 5983 (delta 4281)
Receiving objects: 100% (6921/6921), 8.70 MiB | 3.41 MiB/s, done.
Resolving deltas: 100% (4952/4952), done.
remote /home/remoteuser $ ls
cgit
remote /home/remoteuser $
cgit relies on the source code for Git itself. Conveniently, it is a submodule of the cgit repository, so to satisfy this dependency, simply initialize and update the submodule:
remote /home/remoteuser $ cd cgit
remote /home/remoteuser/cgit $ git submodule init
Submodule 'git' (https://git.kernel.org/pub/scm/git/git.git) registered for path 'git'
remote /home/remoteuser/cgit $ git submodule update
Cloning into '/home/remoteuser/cgit/git'...
Submodule path 'git': checked out '2e1470b37befa08393093cd4d7e36429e1eeeef7'
Like many projects, cgit ships with a Makefile to automate its various build procedures. It is a standard text file that you can read and edit with a text editor:
remote /home/remoteuser/cgit $ vim Makefile
You should see the Makefile define a series of variables at the top, which it uses for the various build procedures it defines later in the file. Take a moment to appreciate the structure of the Makefile, but you do not need a deep comprehension of Makefile syntax to build and install cgit, so go ahead and close your editor once you are done looking at the Makefile.
For more information on make and its syntax, your best resource is to consult
your server’s man
pages depending on the name of your make program:
remote /home/remoteuser/cgit $ man make
remote /home/remoteuser/cgit $ man gmake
While a full comprehension of the Makefile is not necessary, you do need to
redefine some of its default values to match your specific environment before
building cgit. Instead of editing the Makefile itself, cgit uses a file called
cgit.conf
to record your environment-specific build settings.
This file does not exist by default. Go ahead and open this file with your text editor:
remote /home/remoteuser/cgit $ vim cgit.conf
The first configuration settings to define are cgit’s public paths:
CGIT_SCRIPT_PATH = /srv/web/mysite/code
CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
CGIT_SCRIPT_PATH
is the path where cgit will ultimately get installed. Be
sure to replace /srv/web/mysite/code
with whatever directory path you are
using for your own cgit installation.
CGIT_DATA_PATH
is the pathname where cgit stores its favicon, CSS file, and
logo image. These resources should be visible for public web users, which makes
it convenient to use the same directory as CGIT_SCRIPT_PATH
. Be sure to use
the proper Makefile syntax $(VARIABLE)
for capturing the value of a variable
for reassignment to another variable.
The next settings will make use of the restricted directory declared earlier in the guide:
CGIT_CONFIG = /var/webdata/cgit/cgitrc
CACHE_ROOT = /var/webdata/cgit/cache
prefix = /var/webdata/cgit/local
CGIT_CONFIG
is the path for cgit’s configuration file, separate from this build
configuration. Take note of the path you provide here, since we will set up this
configuration file in the next section.
CACHE_ROOT
provides the location for cgit’s cache, which is disabled by
default but valuable to keep in a defined subdirectory if you want to use it.
prefix
defines the base directory where cgit will save various local files,
including helper scripts for syntax highlighting. This value is /usr/local
by
default, but you may not have access to this directory on your server, making
the restricted directory or a subdirectory of the restricted directory a good
place to put these files.
Configure any other variables whose Makefile defaults you want to override in
your cgit.conf
file, save the file, then exit your editor and run the
make
/gmake
/etc. command to build cgit:
remote /home/remoteuser/cgit $ make
SUBDIR git
CGIT_VERSION = v1.2.1-16-g7d87
* new CGit build flags
CC ../cgit.o
CC ../cache.o
<snip>
CC xdiff/xpatience.o
CC xdiff/xhistogram.o
AR xdiff/lib.a
* linking without autodetected Lua support
LINK ../cgit
remote /home/remoteuser/cgit $
It’s important that the build process completes without error. If you encounter
any errors, check your cgit.conf
file for syntax errors or see if the build
succeeds using a different make utility.
Once you compile cgit successfully, you can now install it using the make
install
or gmake install
command:
remote /home/remoteuser/cgit $ make install
SUBDIR git
install -m 0755 -d /srv/www/mysite/code
install -m 0755 cgit /srv/www/mysite/code/cgit.cgi
install -m 0755 -d /srv/www/mysite/code
install -m 0644 cgit.css /srv/www/mysite/code/cgit.css
install -m 0644 cgit.png /srv/www/mysite/code/cgit.png
install -m 0644 favicon.ico /srv/www/mysite/code/favicon.ico
install -m 0644 robots.txt /srv/www/mysite/code/robots.txt
install -m 0755 -d /var/webdata/cgit/local/lib/cgit/filters
cp -r filters/* /var/webdata/cgit/local/lib/cgit/filters
remote /home/remoteuser/cgit $
As the command output suggests, “installation” simply means copying the compiled program into your designated destination directory.
At this point, cgit is live! However, your installation now needs configuration so that it knows where your repositories are, and you also need at least one repository for cgit to display.
Edit a new file at the pathname you used for CGIT_CONFIG
:
remote /home/remoteuser/cgit $ grep CGIT_CONFIG /home/remoteuser/cgit.conf
CGIT_CONFIG = /var/webdata/cgit/cgitrc
remote /home/remoteuser/cgit $ vim /var/webdata/cgit/cgitrc
Supply a basic configuration for cgit in your cgitrc
file:
# Use /code/ as the basis for generating URLs
virtual-root=/code/
# Uncomment this line to prevent web spiders from accessing repos
#robots=noindex, nofollow
# Define locations for the CSS and logo files
css=/code/cgit.css
logo=/code/cgit.png
favicon=/code/favicon.ico
# Describe the repository
root-title=example.com git
root-desc=a cgit instance running on example.com
# Clicking the link will take the user back to https://example.com/
logo-link=/
# Read each repo's Git configuration files
enable-git-config=1
# Run code source files through the syntax highlighter
# If the .sh file does not work and you have python, use the .py file instead
source-filter=/var/webdata/cgit/local/lib/cgit/filters/syntax-highlighting.sh
# Remove .git suffix from repo names
remove-suffix=1
# Define the default clone url
clone-url=https://example.com/code/$CGIT_REPO_URL
# Define the path where the repos live
scan-path=/var/webdata/cgit/repos
The accompanying comments explain the essence of each configuration option, but a few critical ones will receive additional coverage here.
virtual-root
completes the standardization of cgit’s URL structure by
providing the prefix for the URLs cgit generates. Without this option, cgit will
generate links containing the undesirable cgit.cgi
element. Be sure to match
this directory to whatever folder contains cgit.
logo-link=/
will take visitors who click the upper-left cgit logo on each page
back to the main page of your website. This is probably the best solution for
most use cases, but you can point this link to wherever you want.
source-filter
offers an opportunity to apply syntax highlighting to your
project’s code files. If the .sh
file does not work and you have Python
installed on your web server, you can change this extension to .py
. If neither
script works on your host, you must unfortunately remove the entire line or else
you code will not display at all.
clone-url
ensures the presence of cgit displaying a clone link for each
project. The URL format used here ensures the exclusion of any cgit.cgi
element in the URL.
scan-path
is the directory where cgit will search for the Git repositories
that it will display. A subdirectory inside your restricted directory is the
best choice for this path.
cgit has many more configuration values than those listed here. Consult the file
cgitrc.5.txt
wherever you cloned cgit as a reference for expanding this
configuration to your needs:
remote /home/remoteuser/cgit $ vim cgitrc.5.txt
Add any extra configuration options you desire and make sure your syntax is accurate, then save your cgit configuration file and close the editor.
With cgit’s configuration established, it is time to add some repositories.
Create the directory whose path matches the value of scan-path
from the cgit
configuration file:
remote /home/remoteuser/cgit $ grep scan-path /var/webdata/cgit/cgitrc
scan-path=/var/webdata/cgit/repos
remote /home/remoteuser/cgit $ cd /var/webdata/cgit/
remote /var/webdata/cgit $ mkdir repos
remote /var/webdata/cgit $ cd repos
remote /var/webdata/cgit/repos $
The easiest way to set up new repositories is to make a bare repository on the remote server, after which you can create a remote on your local Git project pointing to that new bare repository.
cgit makes use of the gitweb.owner
and gitweb.url
configuration settings
as well as the contents of the .git/description
file. You can provide a
default value for these variables by creating a new template from the default
template located at /usr/share/git-core/templates
:
remote /var/webdata/cgit/repos $ cp -r /usr/share/git-core/templates ~/git-template
remote /var/webdata/cgit/repos $ cd ~/git-template
remote /home/remoteuser/git-template $ git config --local gitweb.owner 'Test User'
remote /home/remoteuser/git-template $ git config --local gitweb.url 'https://example.com/'
remote /home/remoteuser/git-template $ echo "default description" > description
To use this template as the default, set the init.templateDir
in your global
Git configuration:
remote /home/remoteuser/git-template $ git config --global init.templateDir /home/remoteuser/git-template
You are now using your custom template as the default template for bare repositories.
To test the cgit setup, let’s start a new Git project. First, initialize a bare repository in the remote repository store:
remote /home/remoteuser/git-template $ cd /var/webdata/cgit/repos
remote /var/webdata/cgit/repos $ git init --bare testrepo.git
Initialized empty Git repository in /var/webdata/cgit/repos/testrepo.git/
remote /var/webdata/cgit/repos $ ls
testrepo.git
Provide a relevant description for the project by editing the description
file inside the bare repository directory:
remote /var/webdata/cgit/repos $ echo "Example repository" > testrepo.git/description
Now log out from your server (or open a new terminal session on your local machine), initialize a new Git repository, and declare this new bare repository as an upstream remote accessible over SSH:
remote /var/webdata/cgit/repos $ exit
Connection to remote closed.
local$ git init ~/git/testrepo/
Initialized empty Git repository in /home/user/git/testrepo/.git/
local$ cd ~/git/testrepo/
local ~/git/testrepo/ $ git remote add public ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo.git/
local ~/git/testrepo/ $ git checkout -b release
Switched to a new branch 'release'
local ~/git/testrepo/ $
Let’s add a small script to this repository:
local ~/git/testrepo/ $ cat << __END_SCRIPT > example.sh
#!/bin/bash
#loop over a range of integers and note which are even
begin=1
end=20
for ((count=start;count<=end;count++)); do
echo "You are on count \$count"
if [[ \$((count % 2)) -eq 0 ]]; then
echo "\$count is an even number"
fi
done
__END_SCRIPT
local ~/git/testrepo/ $ chmod +x example.sh
local ~/git/testrepo/ $ git add example.sh
local ~/git/testrepo/ $ git commit -m'Include example script'
[release (root-commit) 7b6378f] Include example script
1 file changed, 10 insertions(+)
create mode 100755 example.sh
local ~/git/testrepo/ $ git push public release
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 2 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 386 bytes | 386.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo.git/
* [new branch] release -> release
Congratulations! You just pushed your first cgit-viewable repository!
At this point, you should have a functional instance of cgit running on your server ready to populate with more repositories.
Now that cgit has a repository in its store, let’s make sure it’s working correctly.
First, you should be able to pull up cgit in your web browser:
If this step does not work, make sure you are using the right directory and check your base Apache configuration to see if any piece of the setup is missing or inaccurate.
Next, make sure that the repository links are working by clicking the repository:
As the mouse cursor indicates in the graphic, cgit should be generating working
links that do not contain cgit.cgi
. If they do, go over your Apache rewrite
rules and your cgitrc’s virtual-root
setting to make sure these settings are properly configured.
Your server should have no problem with the lack of a trailing slash when
clicking the clone URL, but if you require or prefer a trailing slash, simply
add a trailing slash to the clone-url
in your cgitrc
file.
Navigate to the tree to view your script file and ensure it renders with syntax highlighting:
If you encounter problems with its display, you may need to use the other language version of the syntax highlighter filter. If neither one works, you may unfortunately need to disable syntax highlighting or else your code will not display at all. Review the cgitrc configuration section for more information.
See if you can clone the repository over HTTP(S) into a temporary directory:
local ~/git/testrepo $ cd /tmp
local /tmp $ git clone https://example.com/code/testrepo/
Cloning into 'testrepo'...
local /tmp $ cd testrepo/
local /tmp/testrepo $
Your new cloned remote should have its upstream remote automatically set to the clone URL:
local /tmp/testrepo $ git remote -v
origin https://example.com/code/testrepo/ (fetch)
origin https://example.com/code/testrepo/ (push)
Public access should be read-only, so ensure this feature works to prevent unwanted pushes to your repositories:
local /tmp/testrepo $ git remote -v
fatal: unable to access 'https://example.com/code/testrepo/': The requested URL returned error: 403
If you do not receive a 403 error, Git is trying to find a way to authenticate your access to this repository. Unless you are attempting to set up an authentication mechanism, review how to prevent HTTP(S) pushing to ensure your server properly returns a 403 error.
You can now delete the temporary clone and revert back to your permanent repository:
local /tmp/testrepo $ cd ~/git/testrepo
local ~/git/testrepo $ rm -rf /tmp/testrepo
Last but not least, test the backlinks to verify the site’s navigability:
If the logo does not point to the right URL, check the value of link-logo
in
your cgitrc file.
Now that your cgit instance is fully deployed, it is important to make sure you keep it up to date with the latest features and fixes.
Updating cgit is as simple as pulling the latest cgit repository and its submodules, then building the new code from a clean environment:
remote /srv/www/mysite $ cd ~/cgit
remote /home/remoteuser/cgit $ git pull
remote /home/remoteuser/cgit $ git submodule update
remote /home/remoteuser/cgit $ make clean
rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
rm -f cgit VERSION CGIT-CFLAGS *.o tags
rm -f -r .deps
remote /home/remoteuser/cgit $ make
SUBDIR git
CGIT_VERSION = v1.2.1-16-g54c4
* new CGit build flags
CC ../cgit.o
CC ../cache.o
<snip>
CC xdiff/xpatience.o
CC xdiff/xhistogram.o
AR xdiff/lib.a
* linking without autodetected Lua support
LINK ../cgit
remote /home/remoteuser/cgit $
Once the build is complete, simply install the updated cgit:
remote /home/remoteuser/cgit $ make install
SUBDIR git
install -m 0755 -d /srv/www/mysite/code
install -m 0755 cgit /srv/www/mysite/code/cgit.cgi
install -m 0755 -d /srv/www/mysite/code
install -m 0644 cgit.css /srv/www/mysite/code/cgit.css
install -m 0644 cgit.png /srv/www/mysite/code/cgit.png
install -m 0644 favicon.ico /srv/www/mysite/code/favicon.ico
install -m 0644 robots.txt /srv/www/mysite/code/robots.txt
install -m 0755 -d /var/webdata/cgit/local/lib/cgit/filters
cp -r filters/* /var/webdata/cgit/local/lib/cgit/filters
remote /home/remoteuser/cgit $
Since this setup forbids pushing over HTTP(S), you may find it problematic to push modified repositories that were originally cloned from the Web.
Fortunately, Git supports configuring separate push and remote URLs for a single remote. The Git documentation stresses that these URLs should refer to the same location to maintain logical consistency as a single source, which fortunately matches the use case for the deployment described in this guide.
Normally, setting a remote’s URL without any options changes both the fetch and the push URLs:
local ~/git/testrepo $ git remote set-url public https://example.com/code/testrepo/
local ~/git/testrepo $ git remote -v
public https://example.com/code/testrepo/ (fetch)
public https://example.com/code/testrepo/ (push)
local ~/git/testrepo $
To change just the push URL for a remote, use the command git remote set-url
--push
:
local ~/git/testrepo $ git remote set-url --push public ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo
local ~/git/testrepo $ git remote -v
public https://example.com/code/testrepo/ (fetch)
public ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo (push)
Similarly, it is possible to change just the fetch URL without changing the push
URL using the git remote set-url --no-push
command:
local ~/git/testrepo $ git remote set-url --no-push public https://example.com/code/testrepo/
local ~/git/testrepo $ git remote -v
public https://example.com/code/testrepo/ (fetch)
public ssh://remoteuser@remote/var/webdata/cgit/repos/testrepo (push)
However you configure your remotes, make sure both pushing and fetching work:
local ~/git/testrepo $ git push public release
Everything up-to-date
local ~/git/testrepo $ git pull public release
Already up to date.
local ~/git/testrepo $
cgit lets you display a small logo image in the upper-left corner of its interface. Determine the path of the logo in your cgitrc configuration:
remote /srv/www/mysite $ grep 'logo=' /var/webdata/cgit/cgitrc
logo=/code/cgit.png
remote /srv/www/mysite $
Note that the listed path is relative to your website’s root directory. Using
the guide’s examples would make this path /srv/www/mysite/code/cgit.png
, but
be sure to use whatever settings you defined in your setup to locate the logo
file on your own server.
The default image is a 96x64 PNG image. There is some flexibility in the size,
but do try to find an image similar in size to this image. Afterwards, simply
use scp
or another upload utility to upload the file to your server:
local ~ $ scp mylogo.png remote:/srv/www/mysite/code/cgit.png
mylogo.png 100% 1284 16.1KB/s 00:00
local ~ $
cgit may overwrite the logo after upgrade. If this occurs, simply repeat these steps after upgrading cgit to restore your custom logo.
You should now have a working self-hosted repository store! Go ahead and start populating it with your personal projects!
There are many more setup options that this guide does not cover. I invite you to look through the extensive documentation for both cgit and Apache to experiment these options and tailor this guide’s setup procedure to your specific requirements.
As you might expect, I am using the information from this guide for my own cgit deployment. Feel free to explore my repository store and clone my projects!