-------------------------------------------------------------------------------
Set up a personal GIT repository - with upload!
OR GIT with a .htaccess and a CGI script
OR using "Smart HTTP GIT" without root.
-------------------------------------------------------------------------------
Normal /git/ setup
As a contrast to below this is the normal way to setup git-http-backend
access for GIT over HTTP
Create a /etc/httpd/conf.d/git.conf.
# Git over HTTP
SetEnv GIT_PROJECT_ROOT /data/repos
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
SSLRequire
AuthType Basic
AuthName "Git Access"
AuthUserFile /var/www/passwd.git
Require valid-user
If you have SELinux
chcon -R -t httpd_git_rw_content_t /data/repos
chcon -t httpd_git_script_exec_t /usr/libexec/git-core/git-http-backend
Now any repos in GIT_PROJECT_ROOT can be accessed using
something like...
http://server/git/repo.git
The important notes about this is that there is no real per-repo
access control, just a global access. And all access is via the
http-backend CGI script.
Personal Repos are similar but involve creating a specific CGI inside
the web access area that maps those URL's to a personal GIT_PROJECT_ROOT.
-------------------------------------------------------------------------------
Basic set up - via CGI execution, rather than command line
Based on a document...
http://www-verimag.imag.fr/~moy/?Host-a-Git-repository-over-HTTP-S
But a good complete guide to what is actually going on is
http://git-scm.com/2010/03/04/smart-http.html
You can Google for "git http-backend .htaccess" BUT some of the docs assumes
https://your.domain.com/ references the top level of your public html area.
Which is not the case for general web servers like hobbit.
The method below assumes...
* you can create and upload web files and directories
* you can set execute permission on ".cgi" files
* The server allows you to use .htaccess files and ExecCGI (hobbit does)
It does NOT require...
* Command Line (you run uploaded scripts instead)
Though command line would make it easier (you do on hobbit)
* The server has the cgi module and GIT installed (hobbit does)
NOTE: I used HTTPS for everything below, because it is the only way to
protect web passwords in the communications. DO NOT USE HTTP with passwords.
NOTE: the git repository does not actually need to be in the Web
page document tree. Only the ".htaccess" and the git-html-backend CGI
script needs to be accessable and password protected in the Web document tree
(public_html).
---
In your public_html directory, or some other web server exported directory
the server administration has provided...
Create a repository directory, call it anything you like.
mkdir ~/public_html/git_repos
chmod 755 ~/public_html/git_repos
cd ~/public_html/git_repos
Now lets get some information from the http server...
First allow CGI programs -- REQUIRED
vi .htaccess
Options +ExecCGI
AddHandler cgi-script cgi
chmod 644 .htaccess
Find out the directory of the repository, if you don't know.
And insure you can run CGI scripts.
vi config.cgi
#! /bin/sh
echo 'Content-type: text/plain'
echo
pwd
git --version
chmod 755 config.cgi
Now use the appropriate URL to get the output of "config.cgi"
You can use a browser, but for me it was the remote command...
wget -q -O - https://www.example.com/user/git_repos/config.cgi
for me it reported...
/home/anthony/public_html/git_repos
git version 1.7.1
A Version of 1.6.6 or later is needed to provide '
---
Set up authentation, using the above directory path (OPTIONAL)
NOTE: the dwarf 'secure' area already has this setup by server
Add to .htaccess file...
vi .htaccess
AuthUserFile /home/anthony/public_html/git_repos/.htpasswd
AuthType Basic
AuthName "Anthony's Private GIT Repository"
Require valid-user
Create the .htpasswd (using whatever method is appropriate)
htpasswd -c .htpasswd anthony
New password: my secret password
Re-type password: my secret password
Adding password for user anthony
chmod 644 .htpasswd
Test this again using a proper browser using the previous URL
Or from command line using
wget -q -O - https://www.example.com/anthony/git_repos/config.cgi \
--user=anthony --ask-password
You could at this point set up ".htgroup" for group access to specific repos.
EG: create a ".htaccess" in the repository sub-directory (we create next)
with something like... "Require group ????" to control access to that repo.
---
NOW create a test repo, (just valid user access)
Again this will be done via uploaded script,
but if you have commandline you can run the commands directly...
NOTE: directory is world writable as we will be pushing to this.
vi create_repo.cgi
#!/bin/sh
echo 'Content-type: text/plain'
echo
exec 2>&1
# echo "Disabled"; exit 0
REPO=myrepo.git
DESC="Description of my Example Repo"
mkdir $REPO
( cd $REPO
git --bare init
git --bare update-server-info
echo "$DESC" > descriptionk
cp hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update
# The following are to ensure the repo is exportable via HTTP(s)
touch git-daemon-export-ok
find . -type f -print | xargs chmod 666
find . -type d -print | xargs chmod 777
)
echo "---------------"
ls -Fla $REPO
chmod 755 create_repo.cgi
And run it (browser or as follows)
wget -q -O - --user=anthony --ask-password \
https://www.example.com/anthony/git_repos/create_repo.cgi
When run, uncomment the 'Disabled' line to prevent it running again.
---
At this point you have a read-only, old style repository, which you
can 'clone' or 'pull' from. For example...
git clone https://www.example.com/anthony/git_repos/myrepo.git
But this always download the whole files, even if only one 'object' in a file
changed. Also you will never be able to 'push' changes up to that repo.
---
To now do Smart HTTP GIT to provide 'push' capability.
We need a way to get at the repository via the 'http-backend' CGI
program provided by GIT.
vi git.cgi
#!/bin/bash
export GIT_PROJECT_ROOT=~anthony/public_html/git_repos
export REMOTE_USER="${REMOTE_USER:-$REDIRECT_REMOTE_USER}"
exec git http-backend "$@"
chmod 755 git.cgi
This script basically defines a CGI script that knows where your repositories
are located and calls the backend, and sets what user is reading/writing the
repository.
The user was pre-defined by the webserver using $REMOTE_USER environment
variable. For anonymous writes you can just set REMOTE_USER to anything.
You can use it as follows.
git clone https://www.example.com/anthony/git_repos/git.cgi/myrepo.git
cd myrepo.git
touch new_file
git add new_file
git commit -a
git push
---
Repository Ownership and File Permissions
The above starts with a bare-bones repository, and all commits to it is
via the WWW. This is important as the server will read-write to the
repository, and as such save files with its own ownership and group.
All new files will actually be located in the "objects" sub-directory of the
GIT repo.
---
Error Handling..
If you get a "error: unpack failed: unpack-objects abnormal exit"
you have a write permission issue, on the "objects" for the web server.
Try running a HEAD URL request directly
https://www.example.com/anthony/git_repos/git.cgi/myrepo.git/HEAD
This should return something like
ref: refs/heads/master.
Run the above clone with more error handling
GIT_CURL_VERBOSE=1 GIT_TRACE=1 git clone https://....
For this a more complex git.cgi script is needed
Note a writable log.txt is uploaded
touch log.txt
chmod 666 log.txt
vi git.cgi
#!/bin/sh
export GIT_PROJECT_ROOT=/home/anthony/public_html/git_repos
export REMOTE_USER="${REMOTE_USER:-$REDIRECT_REMOTE_USER}"
exec 2>>log.txt
echo >&2 "-----------------------"
date >&2 "+%Y-%m-%d_%H:%M:%S"
git http-backend "$@" || echo failed >&2
and after running look at the "log.txt" file created
Error message meanings
Service not enabled: 'receive-pack'
This means you attempted to push a repository without authentication
-------------------------------------------------------------------------------
Garbage Collection...
Create a script called "gc.cgi" and run it every so often to
'clean up' the repositories
vi gc.cgi
#! /bin/sh
echo 'Content-Type: text/plain'
echo
for repo in *.git; do
printf "%s ... " "$repo"
(cd "$repo" && git gc) && echo "OK" || echo "failed"
done
chmod 755 gc.cgi
https://www.example.com/anthony/git_repos/gc.cgi
===============================================================================
Remap the CGI script...
This method uses some rewrite rules to remove the need to referance the
CGI script, by having the server remap the repository path though the CGI
script.
A general guide
https://github.com/tmacam/private-git-on-dreamhost
and a updated version on
http://www.burocrata.org/blog/archives/2010/11/08/345/
Which also sets up a 'gitweb' overview for the respoitory area
NOTE: these last two are actually a better guide than this. But...
CAVAT: The only difference from 'DreamHost' to 'Hobbit' is that 'Hobbit'
needs the repositories to be writable by the apache webserver. The former
'DreamHost' uses "SUexec" to run CGI scripts as the script owner rather
than as the apache web server.
Set up directory (as before).
mkdir ~/public_html/git_repos
chmod 705 ~/public_html/git_repos
cd ~/public_html/git_repos
The ".htaccess" file is...
NOTE: Replace paths containing "anthony" and "git_repos" as appropriate
=======8<--------
#
# Directory Options
#
Options +ExecCGI
#Options +Indexes
#
# Authenticate everyone entering this directory
#
AuthType Basic
AuthName "GIT Repository"
AuthUserFile /home/anthony/public_html/git_repos/.htpasswd
AuthGroupFile /dev/null
Require valid-user
Order allow,deny
Allow from localhost
#
# Redirect GIT requests through the git-http-backend.cgi script
#
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#RewriteCond %{REQUEST_URI} ^/anthony/git_repos/.*\.git/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))|git-(upload|receive)-pack))$
RewriteCond %{REQUEST_URI} ^/anthony/git_repos/.*\.git/
# Route requests through the backend CGI script in this directory
RewriteRule (.*) /anthony/git_repos/git-http-backend.cgi/$1
=======8<--------
chmod 644 .htaccess
The "git-http-backend.cgi" script (as above)...
=======8<--------
#!/bin/bash
export GIT_PROJECT_ROOT=~anthony/public_html/git_repos
export REMOTE_USER="${REMOTE_USER:-$REDIRECT_REMOTE_USER}"
exec git http-backend "$@"
=======8<--------
chmod 705 .htaccess
See notes about the script above.
Create a ".htpasswd"
htpasswd -c .htpasswd anthony
New password: my secret password
Re-type password: my secret password
Adding password for user anthony
chmod 644 .htpasswd
Create a bare test repo "myrepo.git" -- as per "create_repo.cgi: script above
REPO=myrepo.git
DESC="Description of my Example Repo"
mkdir $REPO
( cd $REPO
git --bare init
git --bare update-server-info
echo "$DESC" > description
cp hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update
# The following are to ensure the repo is exportable via HTTP(s)
touch git-daemon-export-ok
find . -type f -print | xargs chmod 666
find . -type d -print | xargs chmod 777
)
echo "---------------"
ls -Fla $REPO
Testing in another location, or machine...
git clone https://www.example.com/anthony/git_repos/myrepo.git
cd myrepo.git
touch new_file
git add new_file
git commit -a
git push
For some strange reason the password is needed on "git push" as well!
---
NOTE: a CGI script does NOT need to have a ".cgi" suffix if it is
declared to be a CGI script in the ".htaccess" file.
=======8<--------
# make "cgi-executable-file" executed as a CGI script.
SetHandler cgi-script
=======8<--------
===============================================================================
SERVER Config methods...
From the git-http-backend manpage
https://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html
You do not need to set the environment variables at all if you include the URI
path after the CGI script part of the URL. That is if GIT_PROJECT_ROOT is
not set, git http-backend reads PATH_TRANSLATED, whcih is part of the CGI
interface protocol.
For example if a "git-http-backend" is in the servers standard "cgi-bin", but
does not set a GIT_PROJECT_ROOT environemnt vaiable then you should be able to
do this.
https://server.domain/cgi-bin/git-http-backend/~s123456/git_repos/myrepo.git
THIS HAS NOT BEEN TESTED
With a script alias such as
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
The last URL becomes
https://server.domain/git/~s123456/git_repos/myrepo.git
If the server does set a *single* GIT_PROJECT_ROOT environment variable
and "myrepo.git" is in that area, then the above becomes the more commonly
known GIT repository URL.
https://server.domain/git/myrepo.git
Basically by using server configuration, you are only providing a scheme for
'URL beautification'
The technique is nicely explained in
http://git-scm.com/2010/03/04/smart-http.html
-------------------------------------------------------------------------------
Alternative to CGI is to look at using a PHP solution "GitPHP"
http://gitphp.org/projects/gitphp/wiki
-------------------------------------------------------------------------------