├── git-http-backend-private.cgi ├── parse_readme_includes.awk ├── gitweb_wrapper.cgi ├── Makefile ├── gitweb_config.perl ├── info.cgi ├── LICENSE ├── model-htaccess ├── newgit.sh ├── README-real.rst └── README.rst /git-http-backend-private.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export GIT_HTTP_EXPORT_ALL=1 3 | export GIT_PROJECT_ROOT=${HTTP_GIT_PROJECT_ROOT:?HTTP_GIT_PROJECT_ROOT env. variable not set. Aborting.} 4 | /usr/lib/git-core/git-http-backend 5 | -------------------------------------------------------------------------------- /parse_readme_includes.awk: -------------------------------------------------------------------------------- 1 | /.. include::/ , /^[[:space:]]*:literal:/{ 2 | if($3){ 3 | cmd="sed 's/^/ /' " $3; 4 | print("::\n\n") 5 | system(cmd); 6 | next; 7 | } else { 8 | #ignore the :literal: line 9 | next; 10 | } 11 | } 12 | 13 | {print;} 14 | -------------------------------------------------------------------------------- /gitweb_wrapper.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export GITWEB_CONFIG=${HTTP_GITWEB_CONFIG:?HTTP_GITWEB_CONFIG env. variable not set. Aborting.} 3 | export GIT_PROJECT_ROOT=${HTTP_GIT_PROJECT_ROOT:?HTTP_GIT_PROJECT_ROOT env. variable not set. Aborting.} 4 | 5 | # Replace "/home/user/gitweb/" for the correct path to where 6 | # gitweb.cgi was installed. 7 | 8 | /home/user/gitweb/gitweb.cgi 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGETS := README.html 3 | RST2HTML := $(shell which rst2html) 4 | RST2HTMLOPT := --input-encoding=utf-8 --output-encoding=utf-8 5 | #--initial-header-level=3 6 | 7 | all: $(TARGETS) README.rst 8 | 9 | # GitHub 10 | README.rst : README-real.rst 11 | awk -f parse_readme_includes.awk $< > .tmp.$@ 12 | mv -fv .tmp.$@ $@ 13 | 14 | %.html: %.rst 15 | $(RST2HTML) $(RST2HTMLOPT) $< $@ 16 | 17 | clean: 18 | -rm -fv $(TARGETS) 19 | 20 | .PHONY: clean all 21 | -------------------------------------------------------------------------------- /gitweb_config.perl: -------------------------------------------------------------------------------- 1 | # where is the git binary? 2 | $GIT = "/usr/bin/git"; 3 | # where are our git project repositories? 4 | $projectroot = $ENV{'GIT_PROJECT_ROOT'}; 5 | # what do we call our projects in the gitweb UI? 6 | $home_link_str = "My Git Projects"; 7 | # where are the files we need for gitweb to display? 8 | @stylesheets = ("gitweb.css"); 9 | $logo = "git-logo.png"; 10 | $favicon = "/favicon.png"; 11 | # what do we call this site? 12 | $site_name = "My Personal Git Repositories"; 13 | -------------------------------------------------------------------------------- /info.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # disable filename globbing 4 | set -f 5 | 6 | echo "Content-type: text/plain; charset=iso-8859-1" 7 | echo 8 | 9 | echo CGI/1.0 test script report: 10 | echo 11 | 12 | echo argc is $#. argv is "$*". 13 | echo 14 | 15 | echo SERVER_SOFTWARE = $SERVER_SOFTWARE 16 | echo SERVER_NAME = $SERVER_NAME 17 | echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE 18 | echo SERVER_PROTOCOL = $SERVER_PROTOCOL 19 | echo SERVER_PORT = $SERVER_PORT 20 | echo REQUEST_METHOD = $REQUEST_METHOD 21 | echo HTTP_ACCEPT = "$HTTP_ACCEPT" 22 | echo PATH_INFO = "$PATH_INFO" 23 | echo PATH_TRANSLATED = "$PATH_TRANSLATED" 24 | echo SCRIPT_NAME = "$SCRIPT_NAME" 25 | echo QUERY_STRING = "$QUERY_STRING" 26 | echo REMOTE_HOST = $REMOTE_HOST 27 | echo REMOTE_ADDR = $REMOTE_ADDR 28 | echo REMOTE_USER = $REMOTE_USER 29 | echo AUTH_TYPE = $AUTH_TYPE 30 | echo CONTENT_TYPE = $CONTENT_TYPE 31 | echo CONTENT_LENGTH = $CONTENT_LENGTH 32 | echo "" 33 | echo HTTP_GIT_PROJECT_ROOT = $HTTP_GIT_PROJECT_ROOT 34 | echo HTTP_GITWEB_CONFIG = $HTTP_GITWEB_CONFIG 35 | 36 | exit 0 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Tiago Alves Macambira 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /model-htaccess: -------------------------------------------------------------------------------- 1 | Options +Indexes 2 | 3 | # GIT BEGIN ########################################################### 4 | 5 | SetEnv HTTP_GIT_PROJECT_ROOT /home/user/private_repos/ 6 | SetEnv HTTP_GITWEB_CONFIG /home/user/private_repos/gitweb_config.perl 7 | 8 | 9 | RewriteEngine On 10 | DirectoryIndex gitweb_wrapper.cgi 11 | # The following two rules can be used instead of DirectoryIndex 12 | #RewriteRule ^$ gitweb_wrapper.cgi/ [L,E=SCRIPT_URL:/$1] 13 | #RewriteRule ^([?].*)$ gitweb_wrapper.cgi/ [L,E=SCRIPT_URL:/$1] 14 | 15 | # Everything else that is not a file is forwarded to git-http-backend 16 | RewriteCond %{REQUEST_FILENAME} !-f 17 | RewriteRule ^([^?].+)$ git-http-backend-private.cgi/$1 18 | 19 | 20 | # GIT END ############################################################ 21 | 22 | # AUTHENTICATION BEGIN ############################################### 23 | AuthType Digest 24 | AuthName "Private Git Repository Access" 25 | # UNCOMMENT THE LINE BELLOW FOR BETTER PERFORMANCE 26 | # AuthDigestDomain /corporate-git/ 27 | AuthUserFile /home/user/private_repos/.htpasswd 28 | Require valid-user 29 | # AUTHENTICATION END ################################################ 30 | -------------------------------------------------------------------------------- /newgit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is based on code from the following blog post 4 | # http://arvinderkang.com/2010/08/25/hosting-git-repositories-on-dreamhost/ 5 | # and http://gist.github.com/73622 6 | 7 | 8 | set -e 9 | 10 | 11 | # Please, configure a default GIT_REPOS_ROOT to match your config 12 | #GIT_REPOS_ROOT="~/private_repos/" 13 | 14 | DEFAULT_DESCRIPTION='no description :(' 15 | 16 | 17 | # describe how the script works 18 | usage() 19 | { 20 | echo "Usage: $0 [ -h ] [ -r directory] [ -d description ] [ -n projectname ]" 21 | echo "" 22 | echo "If no projectname is given, the name of the parent folder will be used as project name." 23 | echo "" 24 | echo " -r directory : (root) directory holding your git repositories" 25 | echo " -d description : description for gitweb" 26 | echo " -h : print this screen" 27 | echo " -n name : name of the project (should end in .git)" 28 | echo "" 29 | } 30 | 31 | DESCRIPTION=${DEFAULT_DESCRIPTION} 32 | 33 | # evaluate the options passed on the command line 34 | while getopts r:d:n:h option 35 | do 36 | case "${option}" 37 | in 38 | r) GIT_REPOS_ROOT=${OPTARG};; 39 | d) DESCRIPTION=${OPTARG};; 40 | n) REPONAME=${OPTARG};; 41 | h) usage 42 | exit 1;; 43 | esac 44 | done 45 | 46 | # check if repositories directory is given and is accessible 47 | if [ -z $GIT_REPOS_ROOT ]; then 48 | usage 49 | exit 1 50 | fi 51 | if ! [ -d $GIT_REPOS_ROOT ]; then 52 | echo "ERROR: '${GIT_REPOS_ROOT}' is not a directory" 53 | echo "" 54 | usage 55 | exit 1 56 | fi 57 | 58 | 59 | # check if name of repository is given. if not, use folder name 60 | if [ -z $REPONAME ]; then 61 | REPONAME=$(basename $PWD) 62 | fi 63 | 64 | # Add .git at and if needed 65 | if ! ( echo $REPONAME | grep -q '\.git$'); then 66 | REPONAME="${REPONAME}.git" 67 | fi 68 | 69 | 70 | # 71 | # Ready to go 72 | # 73 | 74 | 75 | REP_DIR="${GIT_REPOS_ROOT}/${REPONAME}" 76 | mkdir ${REP_DIR} 77 | pushd ${REP_DIR} 78 | git --bare init 79 | git --bare update-server-info 80 | cp hooks/post-update.sample hooks/post-update 81 | chmod a+x hooks/post-update 82 | echo $DESCRIPTION > description 83 | # This mark the repository as exportable. 84 | # For more info refer to git-http-backend manpage 85 | touch git-daemon-export-ok 86 | popd 87 | exit 0 88 | -------------------------------------------------------------------------------- /README-real.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | ======================================= 4 | Private GIT repositories (on DreamHost) 5 | ======================================= 6 | 7 | :Author: Tiago Alves Macambira 8 | :Licence: Creative Commons By-SA 9 | 10 | 11 | .. contents:: Table of Contents 12 | 13 | 14 | 15 | Introduction 16 | ============ 17 | 18 | This is *yet another* guide describing how to setup private 19 | HTTP-accessible Git repositories on Dreamhost_ using Git's 20 | ``git-http-backend`` (a.k.a git's `Smart HTTP protocol`__). While 21 | similar guides can easily be found by the thousands in the Web (I've 22 | listed some of them in the References section), I've found that some 23 | guides have outdated information or that the setup described in 24 | them could be improved. Thus, this guide tries to update, improve and 25 | consolidate the information dispersed in such sources. 26 | 27 | __ GitSmartHTTP_ 28 | 29 | Some might ask "Why on Earth would someone opt to create its own 30 | private Git hosting solution while better offerings are available from 31 | sites such as, let's say, GitHub_?" As the guy `from RailsTips 32 | pointed out in one of his articles`__, sometimes you don't need or 33 | don't want to "share" a project with anyone but yourself and 34 | paying for a GitHub_-like service might just not make sense. If 35 | that's your case, than this guide is for you. 36 | 37 | __ RailsTipsArticle_ 38 | 39 | While aimed at a Dreamhost_-hosted accounts and the environment such 40 | accounts have as of 2010-10-17, I believe the process described here 41 | can be used in other hosting providers as well. 42 | 43 | 44 | It is important to highlight that one of the objectives of this guide 45 | is to describe a process that: 46 | 47 | * should be easy to perform by just renaming or editing this guide's 48 | companion files and 49 | * once complete, can be easily be reused to generate other "collection 50 | of repositories", with different URLs and passwords. 51 | 52 | 53 | Assumptions and requirements 54 | ---------------------------- 55 | 56 | * No WebDav or SSH support is needed nor used for serving Git repositories. 57 | 58 | Once again, the focus is on HTTP access using 59 | ``git-http-backend``. As for SSH, guides describing how to setup a 60 | similar environment for SSH-accessible repositories can be found 61 | easily on the web. 62 | 63 | * Repositories will be password protected and available for both 64 | reading and writing. 65 | 66 | As we will explain later, in the `Setup git-http-backend for your 67 | repositories`_ section, we will have to password-protect our 68 | repositories in order to be able to ``git push`` to them through HTTP. 69 | 70 | * We will stick to a DRY_ (Don't Repeat Yourself) philosophy. 71 | 72 | Thus, we want configuration options to be repeated in as few places 73 | as possible. We will use environment variables in Apache 74 | configuration files to do this. If this makes you uncomfortable, 75 | well, you can always manually spread configuration options all over 76 | the place. :-) Your call. 77 | 78 | 79 | * The web server being used is Apache. 80 | 81 | Well, that is what Dreamhost_ allows me to use so that is going to 82 | be the focus of this guide. While it should not be that difficult to 83 | port the settings here to something suitable for another web server, 84 | describing how to do it is out of the scope of this guide. 85 | 86 | * We are able to run CGI scripts. 87 | 88 | `DreamHosts puts some restrictions`__ 89 | on how a CGI script can be executed and the environment where it 90 | runs. We will abide to those restrictions. 91 | 92 | __ DreamHostWikiCGI_ 93 | 94 | * ``ScriptAlias`` is not allowed by the web server. 95 | 96 | The instructions given in git-http-backend_ manpage will not work as 97 | they use ``ScriptAlias``. The idea is to use common CGI scripts and 98 | ``mod_rewrite`` instead, roughly following the ideas presented in 99 | http://wiki.dreamhost.com/Git#Smart_HTTP . 100 | 101 | * SuExec_ is used to run CGI scripts. 102 | 103 | Notice that, as stated in DreamHost page on CGI, ``SuExec`` "*wipes out all 104 | environment variables that don't start with HTTP\_"*. All our 105 | env. vars. will have this prefix. 106 | 107 | * Your private git repositories will be accessible in a subpath of your 108 | domain. 109 | 110 | The idea is that your private git repos will be available in an 111 | address such as **http://www.example.tld/corporate-git/**. Adapting 112 | the instructions below so you can serve them from the root of a 113 | domain of its own, say **http://corporate-git.example.tld** should 114 | be fairly simple. 115 | 116 | About this document 117 | ------------------- 118 | 119 | This document and its companion files are initially hosted on 120 | http://github.com/tmacam/private-git-on-dreamhost. 121 | 122 | The file ``README.rst`` is generated from ``README-real.rst``. So, if 123 | you plan on doing any updates or fixes, ``README-real.rst`` is the 124 | file you ought to edit. Just run ``make`` afterwards in order to get 125 | ``README.rst`` updated as well. This is done because I wanted to use 126 | GitHub_'s automatic rendering of README files but I didn't want to 127 | just paste the contents of the companion files in this ``README.rst`` 128 | and risk getting the Guide and files out of sync. Unfortunately, 129 | GitHub does not allow the use of RestructuredText's ``include`` 130 | directive, so I had to fake it -- and here is the reason why we have 131 | ``README-real.rst``. 132 | 133 | This guide is distributed under the Creative Common BY-SA license while 134 | companion files are distributed under a MIT License. 135 | 136 | 137 | Installation 138 | ============ 139 | 140 | All the commands and instructions given bellow should be performed on 141 | the machine in Dreamhost where your account is installed. So ssh to it 142 | and let's start. 143 | 144 | Install Git 145 | ----------- 146 | 147 | Well, this should probably be a non-issue since git comes 148 | pre-installed on most Dreamhost machines. To verify it:: 149 | 150 | $ git --version 151 | git version 1.7.1.1 152 | 153 | As you see, the box that serves my domain in DreamHost has git version 154 | 1.7.1.1 installed. `Anything greater than 1.6.6 shall do`__. 155 | 156 | __ GitSmartHTTP_ 157 | 158 | 159 | 160 | If you don't have git installed in you box, have an old version or if 161 | for some other reason your need to compile git, follow Craig's instructions in 162 | |CraigJolicoerArticle|_. 163 | 164 | 165 | 166 | Create the directory where your repositories will live 167 | ------------------------------------------------------ 168 | 169 | 170 | It should reside somewhere not accessible from the web or directly 171 | served by the web server. We will tell Apache and ``git-http-backend`` 172 | how to properly and securely serve those repositories later. For now, 173 | we want them protected from third parties. 174 | 175 | Say we decided to store them in ``~/private_repos/``. We will refer to 176 | this directly by ``GIT_REPOS_ROOT`` in the rest of this guide. Create 177 | this directory and protect it against file system access from others:: 178 | 179 | export GIT_REPOS_ROOT="~/private_repos/" 180 | mkdir ${GIT_REPOS_ROOT} 181 | chmod 711 ${GIT_REPOS_ROOT} 182 | 183 | 184 | Setup the bare repository creation script 185 | ----------------------------------------- 186 | 187 | 188 | 189 | We will use the script ``newgit.sh``, presented bellow, to create new 190 | repositories [1]_ [2]_ . Remember to modify 191 | the value of the GIT_REPOS_ROOT variable in it to match our setup: 192 | 193 | .. include:: newgit.sh 194 | :literal: 195 | 196 | Move or copy this file to an appropriate path (say, your home 197 | directory would be fine) and turn it into an executable:: 198 | 199 | chmod u+x ~/newgit.sh 200 | 201 | .. [1] This script is based in http://gist.github.com/73622 202 | 203 | .. [2] Other guides prefer to use something similar wrapped as a Bash 204 | function but I'd rather have it as a script 205 | 206 | 207 | Apache Setup 208 | ------------ 209 | 210 | Now, let's configure Apache to securely serve those repositories. 211 | 212 | 213 | Setup your .htaccess 214 | ~~~~~~~~~~~~~~~~~~~~ 215 | 216 | As we stated in `Assumptions and requirements`_, we want to serve our files from 217 | **http://www.example.tld/corporate-git/**. So, go to the directory 218 | holding your domain files (``~/www.example.tld``, in our example), 219 | create a ``corporate-git`` directory in it if it doesn't exist yet and create 220 | a ``.htaccess`` file in it:: 221 | 222 | cd ~/www.example.tld 223 | mkdir corporate-git 224 | cd corporate-git 225 | export GIT_WEB_DIR=`pwd` # we will use it in later steps 226 | touch .htaccess 227 | chmod 644 .htaccess 228 | 229 | 230 | Now, edit this ``.htaccess`` contents to match the text presented 231 | bellow or just copy the contents of the file ``model-htaccess`` into 232 | it and adapt it to match your config: 233 | 234 | 235 | .. include:: model-htaccess 236 | :literal: 237 | 238 | For now we will focus on the area between the ``# GIT BEGIN`` and ``# 239 | GIT END`` blocks. Modify ``HTTP_GIT_PROJECT_ROOT`` to match you setup: 240 | it should point to the **full path** where you store your private 241 | repositories. Just expand the value of ``GIT_REPOS_ROOT`` to get this 242 | information:: 243 | 244 | $ (cd ${GIT_REPOS_ROOT}; pwd) 245 | /home/user/private_repos/ 246 | 247 | So, in our example, ``HTTP_GIT_PROJECT_ROOT`` value should be set to 248 | ``/home/user/private_repos/``, as presented in the example above. 249 | 250 | Setup git-http-backend for your repositories 251 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 252 | 253 | Not we will create a CGI script that will invoke 254 | ``git-http-backend``. In your ``.htaccess`` this script is referred as 255 | ``git-http-backend-private.cgi``. Create it in the same directory 256 | where your ``.htaccess`` is by coping the one that comes with this guide 257 | to that directory or by creating an empty file with the following 258 | contents: 259 | 260 | .. include:: git-http-backend-private.cgi 261 | :literal: 262 | 263 | Turn it into an executable file:: 264 | 265 | chmod 755 git-http-backend-private.cgi 266 | 267 | 268 | .. attention:: 269 | You may need to update the path to ``git-http-backend`` executable 270 | if git was installed in a non-default location. 271 | 272 | And that's it. No need to setup anything: all the settings this 273 | scripts are passed to it through environment variables set by Apache 274 | and defined in the ``.htaccess`` file. 275 | 276 | From this point on you should be able to create repositories from the 277 | command line and 278 | access them through HTTP, but they will be 279 | **read-only**. As stated in git-http-backend_ manpage, "*by default, 280 | only the ``upload-pack`` service is enabled, which serves git ``fetch-pack`` 281 | and git ls-remote clients, which are invoked from ``git fetch``, ``git pull``, 282 | and ``git clone``*". For **write access**, i.e., to be able to perform a 283 | ``git push``, the ``receive-pack`` service is needed, and it **is only 284 | enabled when the client is authenticated**. 285 | 286 | 287 | Password-protect your repository 288 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 289 | 290 | We are almost set. Let's configure password protection for this whole 291 | thing. We will focus on the latter part of your ``.htaccess``, the one 292 | between ``# AUTHENTICATION BEGIN`` and ``# AUTHENTICATION END`` that we 293 | reproduce bellow:: 294 | 295 | # AUTHENTICATION BEGIN ######################## 296 | AuthType Digest 297 | AuthName "Private Git Repository Access" 298 | AuthUserFile /home/user/private_repos/.htpasswd 299 | Require valid-user 300 | # AUTHENTICATION END ######################### 301 | 302 | You will have to create the password file pointed by ``AuthUserFile`` 303 | and use the ``htdigest`` tool to add a user to this file :: 304 | 305 | touch /home/user/private_repos/.htpasswd 306 | htdigest /home/user/private_repos/.htpasswd "Private Git Repository Access" username 307 | 308 | 309 | You will be prompted for a password. And that's it. 310 | 311 | 312 | Notice: 313 | 314 | * we are using `Digest Authentication 315 | `_. It 316 | is supposed to be more secure than plain authentication. 317 | * The password file should be keep in a place not directly accessible 318 | from the web. Ideally it should not even be placed in the directory 319 | to be served by ``git-http-backend`` but I'm lazy and I hope this 320 | will be enough. :) 321 | * If you update the value of the ``AuthName`` setting you **must** 322 | also change the |2nd|. parameter passed to ``htdigest``, i.e., the 323 | *Realm*, as `they must match 324 | `_! 325 | Odd, I know. But that's the way it is. 326 | 327 | 328 | Setup GitWeb 329 | ~~~~~~~~~~~~ 330 | 331 | If you followed this guide up to this point than you are able to use 332 | your repositories with git with no major issues. But you will not be 333 | able to browse them with a web browser, retrieve the list of 334 | repositories you have, see diffs, commit messages nor nothing like 335 | that. To make things better, let's install GitWeb, another CGI 336 | interface that will provide a web interface that allows to do all 337 | those things I just said you couldn't. 338 | 339 | .. note:: 340 | Most of the content in this section comes from Kang's |KangArticle|_. 341 | 342 | 343 | Retrieving and installing 344 | +++++++++++++++++++++++++ 345 | 346 | GitWeb comes in the same source package as git itself. Unfortunately, 347 | Dreamhost doesn't install it by default so we will have to install it 348 | manually ourselves. Do your remember what is your git version? No? 349 | Find it all:: 350 | 351 | git --version 352 | 353 | Go to `git homepage`_ and download the corresponding source 354 | package. In my example, in which my git version is 1.7.1.1, I would 355 | need to grab the ``git-1.7.1.1.tar.gz`` source package:: 356 | 357 | cd ~ # Yep, we will download it in our home directory 358 | wget http://www.kernel.org/pub/software/scm/git/git-1.7.1.1.tar.gz 359 | 360 | Unpack it, build GitWeb:: 361 | 362 | tar zxvf git-1.7.1.1.tar.gz 363 | cd git-1.7.1.1 364 | make prefix=/usr/bin gitweb/gitweb.cgi 365 | rm gitweb/gitweb.perl # we won't need it 366 | 367 | We will install it into ``~/gitweb/``:: 368 | 369 | export GITWEB_INSTALL_DIR="~/gitweb" 370 | cp -r gitweb ${GITWEB_INSTALL_DIR} 371 | 372 | We are almost there. 373 | 374 | 375 | Setting up GitWeb 376 | +++++++++++++++++ 377 | 378 | Now, copy all the GitWeb's media files into the directory 379 | where your ``.htaccess`` is:: 380 | 381 | cp ${GITWEB_INSTALL_DIR}/*.{css,png,js} ${GIT_WEB_DIR} 382 | # in this example, GIT_WEB_DIR points 383 | # to ~/www.example.tld/corporate-git 384 | 385 | Get back to where your ``.htaccess`` file is 386 | (i.e. ``GIT_WEB_DIR``). We will create a wrapper CGI for 387 | GitWeb. Just copy ``gitweb_wrapper.cgi`` or create an empty file with 388 | the contents bellow: 389 | 390 | .. include:: gitweb_wrapper.cgi 391 | :literal: 392 | 393 | Turn it into an executable file:: 394 | 395 | chmod 755 gitweb_wrapper.cgi 396 | 397 | 398 | .. attention:: 399 | Remember to to update this file by replacing ``/home/user/gitweb`` 400 | to match gitweb's install location, i.e., to match the contents 401 | of ``${GIT_WEB_DIR}``. 402 | 403 | Once again, we are using settings stored in ``.htaccess`` file and 404 | passing them to a script using environment variables set by Apache. In 405 | this case, we are informing the wrapper script where our repositories 406 | are with ``HTTP_GIT_PROJECT_ROOT``, and informing it where GitWeb 407 | configuration file is with ``HTTP_GITWEB_CONFIG``. The wrapper script, 408 | in turn, will forward these informations to both GitWeb and to its 409 | config file. 410 | 411 | Now, let's create GitWeb configuration file. Just 412 | copy ``gitweb_config.perl`` provided with this guide to 413 | ``${GIT_REPOS_ROOT}/gitweb_config.perl`` or create an empty file in 414 | that path location with the following contents: 415 | 416 | .. include:: gitweb_config.perl 417 | :literal: 418 | 419 | You can customize it a little bit, if you want, but the most important 420 | setting, ``$projectroot``, is set to match the value of 421 | ``HTTP_GIT_PROJECT_ROOT``, a env. var. set by Apache. 422 | 423 | Notice that this file, ``gitweb_config.perl`` is stored in the same 424 | directory where your repositories are, in ``${GIT_REPOS_ROOT}``. If, 425 | for some reason, you prefer to store it elsewhere, you will have to 426 | update this information in the ``.htaccess`` file. 427 | 428 | 429 | 430 | Troubleshooting 431 | --------------- 432 | 433 | So, something is not working as expected? 434 | 435 | Disable authentication 436 | ~~~~~~~~~~~~~~~~~~~~~~ 437 | 438 | Comment out the authentication code. This will ease your "debugging" 439 | process. 440 | 441 | Remember to uncomment it later. 442 | 443 | Use info.cgi script to check CGI script's environment 444 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 445 | 446 | A nice way to check if there is something really wrong with your setup 447 | is to use the ``info.cgi``, whose code is presented bellow. This 448 | script is only a minor modification to the one presented in `Dreamhost 449 | wiki page on CGI`__ and allows your to do verify if you are able to 450 | execute CGI scrips and what settings Apache is passing to the other 451 | CGI scripts we use here. 452 | 453 | .. include:: info.cgi 454 | :literal: 455 | 456 | Copy it to ``GIT_WEB_DIR``, turn it into an executable script 457 | (``chmod 755 ...``) and point your browser to it ( That would be 458 | ``http://www.example.tld/corporate-git/`` in our example). 459 | 460 | 461 | __ DreamHostWikiCGI_ 462 | 463 | Check the server logs 464 | ~~~~~~~~~~~~~~~~~~~~~ 465 | 466 | We are listing this as a last step but that's probably the fist place 467 | where you should have looked for clues: your server logs. 468 | 469 | 470 | For example:: 471 | 472 | [Mon Oct 25 18:30:28 2010] [error] [client 150.164.3.192] Service not enabled: 'receive-pack' 473 | 474 | This message says that 'receive-pack' was not enable -- probably 475 | because you are trying to push to a repository and authentication was 476 | disabled. As we explained in `Setup git-http-backend for your 477 | repositories`_, you **must** use authentication to be able to write 478 | (*push*) to repositories using git-http-backend. 479 | 480 | 481 | This one should be pretty obvious:: 482 | 483 | Digest: user username: password mismatch: /corporate-git/test.git/info/refs 484 | 485 | And so on... 486 | 487 | 488 | 489 | Usage 490 | ===== 491 | 492 | So everything is ready to use. How do you actually create and use 493 | these new repositories? 494 | 495 | Creating new bare repositories 496 | ------------------------------ 497 | 498 | In order to create a new repository, say ``toyproject.git``, all you 499 | have to do is ssh into your Dreamhost account and:: 500 | 501 | ~/newgit.sh -r ${GIT_REPOS_ROOT} -d "My first private repository" -n toyproject 502 | 503 | 504 | That's it: your created and empty repository in you repository 505 | collection. You can *clone* it if you want. 506 | 507 | 508 | Cloning an empty repository 509 | --------------------------- 510 | 511 | So, you got a new pristine and empty repository. Let's *clone* it, shall we?:: 512 | 513 | 514 | $ git clone http://username@www.example.tld/corporate-git/toyproject.git 515 | Initialized empty Git repository in /private/tmp/teste/.git/ 516 | Password: 517 | warning: You appear to have cloned an empty repository. 518 | 519 | 520 | .. important:: 521 | Have you noticed that we have a ``username@`` in the URL? This 522 | tells git that it must athenticate to the server before trying to 523 | access the git repository. 524 | 525 | In this example, we are acessing the 526 | repository with the crentials of the user ``username``, the one we 527 | setup in `Password-protect your repository`_. Modify it to match 528 | the user you created in that step. 529 | 530 | But what if you already have a local repository and all you want is 531 | push it and its history to the server? 532 | 533 | Pushing to a new empty repository 534 | --------------------------------- 535 | 536 | What you usually do is creating a local repository, adding file to it and committing this repository history to the new, empty and pristine repository in your web server:: 537 | 538 | mkdir toyproject 539 | cd toyproject 540 | git init 541 | touch README 542 | git add README 543 | git commit -m 'first commit' 544 | git remote add origin http://username@www.example.tld/corporate-git/toyproject.git 545 | git push origin master 546 | 547 | If you have an existing Git Repo, that's the procedure:: 548 | 549 | cd existing_toyproject_git_repo 550 | git remote add origin http://username@www.example.tld/corporate-git/toyproject.git 551 | git push origin master 552 | 553 | The above workflow follows what is presented in http://help.github.com/creating-a-repo/. 554 | 555 | 556 | 557 | Final remarks 558 | ============= 559 | 560 | If you need more than one collection of private repositories (say, one 561 | for you and one to share privately with a group of coworkers), all you 562 | need to do is: 563 | 564 | 1. Create a directory for each of these collections. 565 | 2. Create copies of ``newgit.sh``, one for each collection, and setup 566 | the value of GIT_REPOS_ROOT in each of them. 567 | 3. Adapt each .htaccess accordingly. 568 | 4. GitWeb: copy its files too.. Or just sym-link it from a pristine copy. 569 | 570 | 571 | TODOs 572 | ===== 573 | 574 | * Focus on reusability. 575 | * Write the `Final remarks`_ section properly. 576 | 577 | 578 | http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritecond -- 579 | serve directly w/ apache if... 580 | 581 | Adding project .description directly in the scripts 582 | 583 | 584 | 585 | References 586 | ========== 587 | 588 | * arvinderkang.com - |KangArticle|_ 589 | * craigjolicoeur.com - |CraigJolicoerArticle|_ 590 | * |RailsTipsArticle|_ 591 | * http://faves.eapen.in/guide-to-hosting-git-repositories-on-dreamhos 592 | * http://gist.github.com/73622 593 | * http://wiki.dreamhost.com/Git#Smart_HTTP 594 | * http://arvinderkang.com/2010/08/25/hosting-git-repositories-on-dreamhost/ 595 | * git-http-backend_ manpage 596 | * |GitSmartHTTP|_ 597 | * http://www.jedi.be/blog/2009/05/06/8-ways-to-share-your-git-repository/ 598 | * http://help.github.com/creating-a-repo/ 599 | 600 | 601 | .. _DreamHost: http://www.dreamhost.com 602 | .. _GitHub: http://github.com 603 | .. |RailsTipsArticle| replace:: Git'n Your Shared Host On 604 | .. _RailsTipsArticle: http://railstips.org/blog/archives/2008/11/23/gitn-your-shared-host-on/ 605 | .. |CraigJolicoerArticle| replace:: Hosting Git Repositories on Dreamhost 606 | .. _CraigJolicoerArticle: http://craigjolicoeur.com/blog/hosting-git-repositories-on-dreamhost 607 | .. |KangArticle| replace:: Hosting Git repositories on Dreamhost 608 | .. _KangArticle: http://arvinderkang.com/2010/08/25/hosting-git-repositories-on-dreamhost/ 609 | .. _SuExec: http://wiki.dreamhost.com/Suexec 610 | .. _DRY: http://en.wikipedia.org/wiki/Don't_repeat_yourself 611 | .. _git-http-backend: http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html 612 | .. |GitSmartHTTP| replace:: Pro Git - Smart HTTP Transport 613 | .. _GitSmartHTTP: http://progit.org/2010/03/04/smart-http.html 614 | .. _Git homepage: http://git-scm.com/ 615 | .. _DreamHostWikiCGI: http://wiki.dreamhost.com/CGI 616 | .. |2nd| replace:: 2\ :sup:`nd` 617 | .. 618 | .. target-notes:: 619 | 620 | 621 | .. sectnum:: 622 | 623 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | ======================================= 4 | Private GIT repositories (on DreamHost) 5 | ======================================= 6 | 7 | :Author: Tiago Alves Macambira 8 | :Licence: Creative Commons By-SA 9 | 10 | 11 | .. contents:: Table of Contents 12 | 13 | 14 | 15 | Introduction 16 | ============ 17 | 18 | This is *yet another* guide describing how to setup private 19 | HTTP-accessible Git repositories on Dreamhost_ using Git's 20 | ``git-http-backend`` (a.k.a git's `Smart HTTP protocol`__). While 21 | similar guides can easily be found by the thousands in the Web (I've 22 | listed some of them in the References section), I've found that some 23 | guides have outdated information or that the setup described in 24 | them could be improved. Thus, this guide tries to update, improve and 25 | consolidate the information dispersed in such sources. 26 | 27 | __ GitSmartHTTP_ 28 | 29 | Some might ask "Why on Earth would someone opt to create its own 30 | private Git hosting solution while better offerings are available from 31 | sites such as, let's say, GitHub_?" As the guy `from RailsTips 32 | pointed out in one of his articles`__, sometimes you don't need or 33 | don't want to "share" a project with anyone but yourself and 34 | paying for a GitHub_-like service might just not make sense. If 35 | that's your case, than this guide is for you. 36 | 37 | __ RailsTipsArticle_ 38 | 39 | While aimed at a Dreamhost_-hosted accounts and the environment such 40 | accounts have as of 2010-10-17, I believe the process described here 41 | can be used in other hosting providers as well. 42 | 43 | 44 | It is important to highlight that one of the objectives of this guide 45 | is to describe a process that: 46 | 47 | * should be easy to perform by just renaming or editing this guide's 48 | companion files and 49 | * once complete, can be easily be reused to generate other "collection 50 | of repositories", with different URLs and passwords. 51 | 52 | 53 | Assumptions and requirements 54 | ---------------------------- 55 | 56 | * No WebDav or SSH support is needed nor used for serving Git repositories. 57 | 58 | Once again, the focus is on HTTP access using 59 | ``git-http-backend``. As for SSH, guides describing how to setup a 60 | similar environment for SSH-accessible repositories can be found 61 | easily on the web. 62 | 63 | * Repositories will be password protected and available for both 64 | reading and writing. 65 | 66 | As we will explain later, in the `Setup git-http-backend for your 67 | repositories`_ section, we will have to password-protect our 68 | repositories in order to be able to ``git push`` to them through HTTP. 69 | 70 | * We will stick to a DRY_ (Don't Repeat Yourself) philosophy. 71 | 72 | Thus, we want configuration options to be repeated in as few places 73 | as possible. We will use environment variables in Apache 74 | configuration files to do this. If this makes you uncomfortable, 75 | well, you can always manually spread configuration options all over 76 | the place. :-) Your call. 77 | 78 | 79 | * The web server being used is Apache. 80 | 81 | Well, that is what Dreamhost_ allows me to use so that is going to 82 | be the focus of this guide. While it should not be that difficult to 83 | port the settings here to something suitable for another web server, 84 | describing how to do it is out of the scope of this guide. 85 | 86 | * We are able to run CGI scripts. 87 | 88 | `DreamHosts puts some restrictions`__ 89 | on how a CGI script can be executed and the environment where it 90 | runs. We will abide to those restrictions. 91 | 92 | __ DreamHostWikiCGI_ 93 | 94 | * ``ScriptAlias`` is not allowed by the web server. 95 | 96 | The instructions given in git-http-backend_ manpage will not work as 97 | they use ``ScriptAlias``. The idea is to use common CGI scripts and 98 | ``mod_rewrite`` instead, roughly following the ideas presented in 99 | http://wiki.dreamhost.com/Git#Smart_HTTP . 100 | 101 | * SuExec_ is used to run CGI scripts. 102 | 103 | Notice that, as stated in DreamHost page on CGI, ``SuExec`` "*wipes out all 104 | environment variables that don't start with HTTP\_"*. All our 105 | env. vars. will have this prefix. 106 | 107 | * Your private git repositories will be accessible in a subpath of your 108 | domain. 109 | 110 | The idea is that your private git repos will be available in an 111 | address such as **http://www.example.tld/corporate-git/**. Adapting 112 | the instructions below so you can serve them from the root of a 113 | domain of its own, say **http://corporate-git.example.tld** should 114 | be fairly simple. 115 | 116 | About this document 117 | ------------------- 118 | 119 | This document and its companion files are initially hosted on 120 | http://github.com/tmacam/private-git-on-dreamhost. 121 | 122 | The file ``README.rst`` is generated from ``README-real.rst``. So, if 123 | you plan on doing any updates or fixes, ``README-real.rst`` is the 124 | file you ought to edit. Just run ``make`` afterwards in order to get 125 | ``README.rst`` updated as well. This is done because I wanted to use 126 | GitHub_'s automatic rendering of README files but I didn't want to 127 | just paste the contents of the companion files in this ``README.rst`` 128 | and risk getting the Guide and files out of sync. Unfortunately, 129 | GitHub does not allow the use of RestructuredText's ``include`` 130 | directive, so I had to fake it -- and here is the reason why we have 131 | ``README-real.rst``. 132 | 133 | This guide is distributed under the Creative Common BY-SA license while 134 | companion files are distributed under a MIT License. 135 | 136 | 137 | Installation 138 | ============ 139 | 140 | All the commands and instructions given bellow should be performed on 141 | the machine in Dreamhost where your account is installed. So ssh to it 142 | and let's start. 143 | 144 | Install Git 145 | ----------- 146 | 147 | Well, this should probably be a non-issue since git comes 148 | pre-installed on most Dreamhost machines. To verify it:: 149 | 150 | $ git --version 151 | git version 1.7.1.1 152 | 153 | As you see, the box that serves my domain in DreamHost has git version 154 | 1.7.1.1 installed. `Anything greater than 1.6.6 shall do`__. 155 | 156 | __ GitSmartHTTP_ 157 | 158 | 159 | 160 | If you don't have git installed in you box, have an old version or if 161 | for some other reason your need to compile git, follow Craig's instructions in 162 | |CraigJolicoerArticle|_. 163 | 164 | 165 | 166 | Create the directory where your repositories will live 167 | ------------------------------------------------------ 168 | 169 | 170 | It should reside somewhere not accessible from the web or directly 171 | served by the web server. We will tell Apache and ``git-http-backend`` 172 | how to properly and securely serve those repositories later. For now, 173 | we want them protected from third parties. 174 | 175 | Say we decided to store them in ``~/private_repos/``. We will refer to 176 | this directly by ``GIT_REPOS_ROOT`` in the rest of this guide. Create 177 | this directory and protect it against file system access from others:: 178 | 179 | export GIT_REPOS_ROOT="~/private_repos/" 180 | mkdir ${GIT_REPOS_ROOT} 181 | chmod 711 ${GIT_REPOS_ROOT} 182 | 183 | 184 | Setup the bare repository creation script 185 | ----------------------------------------- 186 | 187 | 188 | 189 | We will use the script ``newgit.sh``, presented bellow, to create new 190 | repositories [1]_ [2]_ . Remember to modify 191 | the value of the GIT_REPOS_ROOT variable in it to match our setup: 192 | 193 | :: 194 | 195 | 196 | #!/bin/bash 197 | 198 | # this script is based on code from the following blog post 199 | # http://arvinderkang.com/2010/08/25/hosting-git-repositories-on-dreamhost/ 200 | # and http://gist.github.com/73622 201 | 202 | 203 | set -e 204 | 205 | 206 | # Please, configure a default GIT_REPOS_ROOT to match your config 207 | #GIT_REPOS_ROOT="~/private_repos/" 208 | 209 | DEFAULT_DESCRIPTION='no description :(' 210 | 211 | 212 | # describe how the script works 213 | usage() 214 | { 215 | echo "Usage: $0 [ -h ] [ -r directory] [ -d description ] [ -n projectname ]" 216 | echo "" 217 | echo "If no projectname is given, the name of the parent folder will be used as project name." 218 | echo "" 219 | echo " -r directory : (root) directory holding your git repositories" 220 | echo " -d description : description for gitweb" 221 | echo " -h : print this screen" 222 | echo " -n name : name of the project (should end in .git)" 223 | echo "" 224 | } 225 | 226 | DESCRIPTION=${DEFAULT_DESCRIPTION} 227 | 228 | # evaluate the options passed on the command line 229 | while getopts r:d:n:h option 230 | do 231 | case "${option}" 232 | in 233 | r) GIT_REPOS_ROOT=${OPTARG};; 234 | d) DESCRIPTION=${OPTARG};; 235 | n) REPONAME=${OPTARG};; 236 | h) usage 237 | exit 1;; 238 | esac 239 | done 240 | 241 | # check if repositories directory is given and is accessible 242 | if [ -z $GIT_REPOS_ROOT ]; then 243 | usage 244 | exit 1 245 | fi 246 | if ! [ -d $GIT_REPOS_ROOT ]; then 247 | echo "ERROR: '${GIT_REPOS_ROOT}' is not a directory" 248 | echo "" 249 | usage 250 | exit 1 251 | fi 252 | 253 | 254 | # check if name of repository is given. if not, use folder name 255 | if [ -z $REPONAME ]; then 256 | REPONAME=$(basename $PWD) 257 | fi 258 | 259 | # Add .git at and if needed 260 | if ! ( echo $REPONAME | grep -q '\.git$'); then 261 | REPONAME="${REPONAME}.git" 262 | fi 263 | 264 | 265 | # 266 | # Ready to go 267 | # 268 | 269 | 270 | REP_DIR="${GIT_REPOS_ROOT}/${REPONAME}" 271 | mkdir ${REP_DIR} 272 | pushd ${REP_DIR} 273 | git --bare init 274 | git --bare update-server-info 275 | cp hooks/post-update.sample hooks/post-update 276 | chmod a+x hooks/post-update 277 | echo $DESCRIPTION > description 278 | # This mark the repository as exportable. 279 | # For more info refer to git-http-backend manpage 280 | touch git-daemon-export-ok 281 | popd 282 | exit 0 283 | 284 | Move or copy this file to an appropriate path (say, your home 285 | directory would be fine) and turn it into an executable:: 286 | 287 | chmod u+x ~/newgit.sh 288 | 289 | .. [1] This script is based in http://gist.github.com/73622 290 | 291 | .. [2] Other guides prefer to use something similar wrapped as a Bash 292 | function but I'd rather have it as a script 293 | 294 | 295 | Apache Setup 296 | ------------ 297 | 298 | Now, let's configure Apache to securely serve those repositories. 299 | 300 | 301 | Setup your .htaccess 302 | ~~~~~~~~~~~~~~~~~~~~ 303 | 304 | As we stated in `Assumptions and requirements`_, we want to serve our files from 305 | **http://www.example.tld/corporate-git/**. So, go to the directory 306 | holding your domain files (``~/www.example.tld``, in our example), 307 | create a ``corporate-git`` directory in it if it doesn't exist yet and create 308 | a ``.htaccess`` file in it:: 309 | 310 | cd ~/www.example.tld 311 | mkdir corporate-git 312 | cd corporate-git 313 | export GIT_WEB_DIR=`pwd` # we will use it in later steps 314 | touch .htaccess 315 | chmod 644 .htaccess 316 | 317 | 318 | Now, edit this ``.htaccess`` contents to match the text presented 319 | bellow or just copy the contents of the file ``model-htaccess`` into 320 | it and adapt it to match your config: 321 | 322 | 323 | :: 324 | 325 | 326 | Options +Indexes 327 | 328 | # GIT BEGIN ########################################################### 329 | 330 | SetEnv HTTP_GIT_PROJECT_ROOT /home/user/private_repos/ 331 | SetEnv HTTP_GITWEB_CONFIG /home/user/private_repos/gitweb_config.perl 332 | 333 | 334 | RewriteEngine On 335 | DirectoryIndex gitweb_wrapper.cgi 336 | # The following two rules can be used instead of DirectoryIndex 337 | #RewriteRule ^$ gitweb_wrapper.cgi/ [L,E=SCRIPT_URL:/$1] 338 | #RewriteRule ^([?].*)$ gitweb_wrapper.cgi/ [L,E=SCRIPT_URL:/$1] 339 | 340 | # Everything else that is not a file is forwarded to git-http-backend 341 | RewriteCond %{REQUEST_FILENAME} !-f 342 | RewriteRule ^([^?].+)$ git-http-backend-private.cgi/$1 343 | 344 | 345 | # GIT END ############################################################ 346 | 347 | # AUTHENTICATION BEGIN ############################################### 348 | AuthType Digest 349 | AuthName "Private Git Repository Access" 350 | # UNCOMMENT THE LINE BELLOW FOR BETTER PERFORMANCE 351 | # AuthDigestDomain /corporate-git/ 352 | AuthUserFile /home/user/private_repos/.htpasswd 353 | Require valid-user 354 | # AUTHENTICATION END ################################################ 355 | 356 | For now we will focus on the area between the ``# GIT BEGIN`` and ``# 357 | GIT END`` blocks. Modify ``HTTP_GIT_PROJECT_ROOT`` to match you setup: 358 | it should point to the **full path** where you store your private 359 | repositories. Just expand the value of ``GIT_REPOS_ROOT`` to get this 360 | information:: 361 | 362 | $ (cd ${GIT_REPOS_ROOT}; pwd) 363 | /home/user/private_repos/ 364 | 365 | So, in our example, ``HTTP_GIT_PROJECT_ROOT`` value should be set to 366 | ``/home/user/private_repos/``, as presented in the example above. 367 | 368 | Setup git-http-backend for your repositories 369 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 370 | 371 | Not we will create a CGI script that will invoke 372 | ``git-http-backend``. In your ``.htaccess`` this script is referred as 373 | ``git-http-backend-private.cgi``. Create it in the same directory 374 | where your ``.htaccess`` is by coping the one that comes with this guide 375 | to that directory or by creating an empty file with the following 376 | contents: 377 | 378 | :: 379 | 380 | 381 | #!/bin/sh 382 | export GIT_HTTP_EXPORT_ALL=1 383 | export GIT_PROJECT_ROOT=${HTTP_GIT_PROJECT_ROOT:?HTTP_GIT_PROJECT_ROOT env. variable not set. Aborting.} 384 | /usr/lib/git-core/git-http-backend 385 | 386 | Turn it into an executable file:: 387 | 388 | chmod 755 git-http-backend-private.cgi 389 | 390 | 391 | .. attention:: 392 | You may need to update the path to ``git-http-backend`` executable 393 | if git was installed in a non-default location. 394 | 395 | And that's it. No need to setup anything: all the settings this 396 | scripts are passed to it through environment variables set by Apache 397 | and defined in the ``.htaccess`` file. 398 | 399 | From this point on you should be able to create repositories from the 400 | command line and 401 | access them through HTTP, but they will be 402 | **read-only**. As stated in git-http-backend_ manpage, "*by default, 403 | only the ``upload-pack`` service is enabled, which serves git ``fetch-pack`` 404 | and git ls-remote clients, which are invoked from ``git fetch``, ``git pull``, 405 | and ``git clone``*". For **write access**, i.e., to be able to perform a 406 | ``git push``, the ``receive-pack`` service is needed, and it **is only 407 | enabled when the client is authenticated**. 408 | 409 | 410 | Password-protect your repository 411 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 412 | 413 | We are almost set. Let's configure password protection for this whole 414 | thing. We will focus on the latter part of your ``.htaccess``, the one 415 | between ``# AUTHENTICATION BEGIN`` and ``# AUTHENTICATION END`` that we 416 | reproduce bellow:: 417 | 418 | # AUTHENTICATION BEGIN ######################## 419 | AuthType Digest 420 | AuthName "Private Git Repository Access" 421 | AuthUserFile /home/user/private_repos/.htpasswd 422 | Require valid-user 423 | # AUTHENTICATION END ######################### 424 | 425 | You will have to create the password file pointed by ``AuthUserFile`` 426 | and use the ``htdigest`` tool to add a user to this file :: 427 | 428 | touch /home/user/private_repos/.htpasswd 429 | htdigest /home/user/private_repos/.htpasswd "Private Git Repository Access" username 430 | 431 | 432 | You will be prompted for a password. And that's it. 433 | 434 | 435 | Notice: 436 | 437 | * we are using `Digest Authentication 438 | `_. It 439 | is supposed to be more secure than plain authentication. 440 | * The password file should be keep in a place not directly accessible 441 | from the web. Ideally it should not even be placed in the directory 442 | to be served by ``git-http-backend`` but I'm lazy and I hope this 443 | will be enough. :) 444 | * If you update the value of the ``AuthName`` setting you **must** 445 | also change the |2nd|. parameter passed to ``htdigest``, i.e., the 446 | *Realm*, as `they must match 447 | `_! 448 | Odd, I know. But that's the way it is. 449 | 450 | 451 | Setup GitWeb 452 | ~~~~~~~~~~~~ 453 | 454 | If you followed this guide up to this point than you are able to use 455 | your repositories with git with no major issues. But you will not be 456 | able to browse them with a web browser, retrieve the list of 457 | repositories you have, see diffs, commit messages nor nothing like 458 | that. To make things better, let's install GitWeb, another CGI 459 | interface that will provide a web interface that allows to do all 460 | those things I just said you couldn't. 461 | 462 | .. note:: 463 | Most of the content in this section comes from Kang's |KangArticle|_. 464 | 465 | 466 | Retrieving and installing 467 | +++++++++++++++++++++++++ 468 | 469 | GitWeb comes in the same source package as git itself. Unfortunately, 470 | Dreamhost doesn't install it by default so we will have to install it 471 | manually ourselves. Do your remember what is your git version? No? 472 | Find it all:: 473 | 474 | git --version 475 | 476 | Go to `git homepage`_ and download the corresponding source 477 | package. In my example, in which my git version is 1.7.1.1, I would 478 | need to grab the ``git-1.7.1.1.tar.gz`` source package:: 479 | 480 | cd ~ # Yep, we will download it in our home directory 481 | wget http://www.kernel.org/pub/software/scm/git/git-1.7.1.1.tar.gz 482 | 483 | Unpack it, build GitWeb:: 484 | 485 | tar zxvf git-1.7.1.1.tar.gz 486 | cd git-1.7.1.1 487 | make prefix=/usr/bin gitweb/gitweb.cgi 488 | rm gitweb/gitweb.perl # we won't need it 489 | 490 | We will install it into ``~/gitweb/``:: 491 | 492 | export GITWEB_INSTALL_DIR="~/gitweb" 493 | cp -r gitweb ${GITWEB_INSTALL_DIR} 494 | 495 | We are almost there. 496 | 497 | 498 | Setting up GitWeb 499 | +++++++++++++++++ 500 | 501 | Now, copy all the GitWeb's media files into the directory 502 | where your ``.htaccess`` is:: 503 | 504 | cp ${GITWEB_INSTALL_DIR}/*.{css,png,js} ${GIT_WEB_DIR} 505 | # in this example, GIT_WEB_DIR points 506 | # to ~/www.example.tld/corporate-git 507 | 508 | Get back to where your ``.htaccess`` file is 509 | (i.e. ``GIT_WEB_DIR``). We will create a wrapper CGI for 510 | GitWeb. Just copy ``gitweb_wrapper.cgi`` or create an empty file with 511 | the contents bellow: 512 | 513 | :: 514 | 515 | 516 | #!/bin/bash 517 | export GITWEB_CONFIG=${HTTP_GITWEB_CONFIG:?HTTP_GITWEB_CONFIG env. variable not set. Aborting.} 518 | export GIT_PROJECT_ROOT=${HTTP_GIT_PROJECT_ROOT:?HTTP_GIT_PROJECT_ROOT env. variable not set. Aborting.} 519 | 520 | # Replace "/home/user/gitweb/" for the correct path to where 521 | # gitweb.cgi was installed. 522 | 523 | /home/user/gitweb/gitweb.cgi 524 | 525 | Turn it into an executable file:: 526 | 527 | chmod 755 gitweb_wrapper.cgi 528 | 529 | 530 | .. attention:: 531 | Remember to to update this file by replacing ``/home/user/gitweb`` 532 | to match gitweb's install location, i.e., to match the contents 533 | of ``${GIT_WEB_DIR}``. 534 | 535 | Once again, we are using settings stored in ``.htaccess`` file and 536 | passing them to a script using environment variables set by Apache. In 537 | this case, we are informing the wrapper script where our repositories 538 | are with ``HTTP_GIT_PROJECT_ROOT``, and informing it where GitWeb 539 | configuration file is with ``HTTP_GITWEB_CONFIG``. The wrapper script, 540 | in turn, will forward these informations to both GitWeb and to its 541 | config file. 542 | 543 | Now, let's create GitWeb configuration file. Just 544 | copy ``gitweb_config.perl`` provided with this guide to 545 | ``${GIT_REPOS_ROOT}/gitweb_config.perl`` or create an empty file in 546 | that path location with the following contents: 547 | 548 | :: 549 | 550 | 551 | # where is the git binary? 552 | $GIT = "/usr/bin/git"; 553 | # where are our git project repositories? 554 | $projectroot = $ENV{'GIT_PROJECT_ROOT'}; 555 | # what do we call our projects in the gitweb UI? 556 | $home_link_str = "My Git Projects"; 557 | # where are the files we need for gitweb to display? 558 | @stylesheets = ("gitweb.css"); 559 | $logo = "git-logo.png"; 560 | $favicon = "/favicon.png"; 561 | # what do we call this site? 562 | $site_name = "My Personal Git Repositories"; 563 | 564 | You can customize it a little bit, if you want, but the most important 565 | setting, ``$projectroot``, is set to match the value of 566 | ``HTTP_GIT_PROJECT_ROOT``, a env. var. set by Apache. 567 | 568 | Notice that this file, ``gitweb_config.perl`` is stored in the same 569 | directory where your repositories are, in ``${GIT_REPOS_ROOT}``. If, 570 | for some reason, you prefer to store it elsewhere, you will have to 571 | update this information in the ``.htaccess`` file. 572 | 573 | 574 | 575 | Troubleshooting 576 | --------------- 577 | 578 | So, something is not working as expected? 579 | 580 | Disable authentication 581 | ~~~~~~~~~~~~~~~~~~~~~~ 582 | 583 | Comment out the authentication code. This will ease your "debugging" 584 | process. 585 | 586 | Remember to uncomment it later. 587 | 588 | Use info.cgi script to check CGI script's environment 589 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 590 | 591 | A nice way to check if there is something really wrong with your setup 592 | is to use the ``info.cgi``, whose code is presented bellow. This 593 | script is only a minor modification to the one presented in `Dreamhost 594 | wiki page on CGI`__ and allows your to do verify if you are able to 595 | execute CGI scrips and what settings Apache is passing to the other 596 | CGI scripts we use here. 597 | 598 | :: 599 | 600 | 601 | #!/bin/sh 602 | 603 | # disable filename globbing 604 | set -f 605 | 606 | echo "Content-type: text/plain; charset=iso-8859-1" 607 | echo 608 | 609 | echo CGI/1.0 test script report: 610 | echo 611 | 612 | echo argc is $#. argv is "$*". 613 | echo 614 | 615 | echo SERVER_SOFTWARE = $SERVER_SOFTWARE 616 | echo SERVER_NAME = $SERVER_NAME 617 | echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE 618 | echo SERVER_PROTOCOL = $SERVER_PROTOCOL 619 | echo SERVER_PORT = $SERVER_PORT 620 | echo REQUEST_METHOD = $REQUEST_METHOD 621 | echo HTTP_ACCEPT = "$HTTP_ACCEPT" 622 | echo PATH_INFO = "$PATH_INFO" 623 | echo PATH_TRANSLATED = "$PATH_TRANSLATED" 624 | echo SCRIPT_NAME = "$SCRIPT_NAME" 625 | echo QUERY_STRING = "$QUERY_STRING" 626 | echo REMOTE_HOST = $REMOTE_HOST 627 | echo REMOTE_ADDR = $REMOTE_ADDR 628 | echo REMOTE_USER = $REMOTE_USER 629 | echo AUTH_TYPE = $AUTH_TYPE 630 | echo CONTENT_TYPE = $CONTENT_TYPE 631 | echo CONTENT_LENGTH = $CONTENT_LENGTH 632 | echo "" 633 | echo HTTP_GIT_PROJECT_ROOT = $HTTP_GIT_PROJECT_ROOT 634 | echo HTTP_GITWEB_CONFIG = $HTTP_GITWEB_CONFIG 635 | 636 | exit 0 637 | 638 | 639 | Copy it to ``GIT_WEB_DIR``, turn it into an executable script 640 | (``chmod 755 ...``) and point your browser to it ( That would be 641 | ``http://www.example.tld/corporate-git/`` in our example). 642 | 643 | 644 | __ DreamHostWikiCGI_ 645 | 646 | Check the server logs 647 | ~~~~~~~~~~~~~~~~~~~~~ 648 | 649 | We are listing this as a last step but that's probably the fist place 650 | where you should have looked for clues: your server logs. 651 | 652 | 653 | For example:: 654 | 655 | [Mon Oct 25 18:30:28 2010] [error] [client 150.164.3.192] Service not enabled: 'receive-pack' 656 | 657 | This message says that 'receive-pack' was not enable -- probably 658 | because you are trying to push to a repository and authentication was 659 | disabled. As we explained in `Setup git-http-backend for your 660 | repositories`_, you **must** use authentication to be able to write 661 | (*push*) to repositories using git-http-backend. 662 | 663 | 664 | This one should be pretty obvious:: 665 | 666 | Digest: user username: password mismatch: /corporate-git/test.git/info/refs 667 | 668 | And so on... 669 | 670 | 671 | 672 | Usage 673 | ===== 674 | 675 | So everything is ready to use. How do you actually create and use 676 | these new repositories? 677 | 678 | Creating new bare repositories 679 | ------------------------------ 680 | 681 | In order to create a new repository, say ``toyproject.git``, all you 682 | have to do is ssh into your Dreamhost account and:: 683 | 684 | ~/newgit.sh -r ${GIT_REPOS_ROOT} -d "My first private repository" -n toyproject 685 | 686 | 687 | That's it: your created and empty repository in you repository 688 | collection. You can *clone* it if you want. 689 | 690 | 691 | Cloning an empty repository 692 | --------------------------- 693 | 694 | So, you got a new pristine and empty repository. Let's *clone* it, shall we?:: 695 | 696 | 697 | $ git clone http://username@www.example.tld/corporate-git/toyproject.git 698 | Initialized empty Git repository in /private/tmp/teste/.git/ 699 | Password: 700 | warning: You appear to have cloned an empty repository. 701 | 702 | 703 | .. important:: 704 | Have you noticed that we have a ``username@`` in the URL? This 705 | tells git that it must athenticate to the server before trying to 706 | access the git repository. 707 | 708 | In this example, we are acessing the 709 | repository with the crentials of the user ``username``, the one we 710 | setup in `Password-protect your repository`_. Modify it to match 711 | the user you created in that step. 712 | 713 | But what if you already have a local repository and all you want is 714 | push it and its history to the server? 715 | 716 | Pushing to a new empty repository 717 | --------------------------------- 718 | 719 | What you usually do is creating a local repository, adding file to it and committing this repository history to the new, empty and pristine repository in your web server:: 720 | 721 | mkdir toyproject 722 | cd toyproject 723 | git init 724 | touch README 725 | git add README 726 | git commit -m 'first commit' 727 | git remote add origin http://username@www.example.tld/corporate-git/toyproject.git 728 | git push origin master 729 | 730 | If you have an existing Git Repo, that's the procedure:: 731 | 732 | cd existing_toyproject_git_repo 733 | git remote add origin http://username@www.example.tld/corporate-git/toyproject.git 734 | git push origin master 735 | 736 | The above workflow follows what is presented in http://help.github.com/creating-a-repo/. 737 | 738 | 739 | 740 | Final remarks 741 | ============= 742 | 743 | If you need more than one collection of private repositories (say, one 744 | for you and one to share privately with a group of coworkers), all you 745 | need to do is: 746 | 747 | 1. Create a directory for each of these collections. 748 | 2. Create copies of ``newgit.sh``, one for each collection, and setup 749 | the value of GIT_REPOS_ROOT in each of them. 750 | 3. Adapt each .htaccess accordingly. 751 | 4. GitWeb: copy its files too.. Or just sym-link it from a pristine copy. 752 | 753 | 754 | TODOs 755 | ===== 756 | 757 | * Focus on reusability. 758 | * Write the `Final remarks`_ section properly. 759 | 760 | 761 | http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritecond -- 762 | serve directly w/ apache if... 763 | 764 | Adding project .description directly in the scripts 765 | 766 | 767 | 768 | References 769 | ========== 770 | 771 | * arvinderkang.com - |KangArticle|_ 772 | * craigjolicoeur.com - |CraigJolicoerArticle|_ 773 | * |RailsTipsArticle|_ 774 | * http://faves.eapen.in/guide-to-hosting-git-repositories-on-dreamhos 775 | * http://gist.github.com/73622 776 | * http://wiki.dreamhost.com/Git#Smart_HTTP 777 | * http://arvinderkang.com/2010/08/25/hosting-git-repositories-on-dreamhost/ 778 | * git-http-backend_ manpage 779 | * |GitSmartHTTP|_ 780 | * http://www.jedi.be/blog/2009/05/06/8-ways-to-share-your-git-repository/ 781 | * http://help.github.com/creating-a-repo/ 782 | 783 | 784 | .. _DreamHost: http://www.dreamhost.com 785 | .. _GitHub: http://github.com 786 | .. |RailsTipsArticle| replace:: Git'n Your Shared Host On 787 | .. _RailsTipsArticle: http://railstips.org/blog/archives/2008/11/23/gitn-your-shared-host-on/ 788 | .. |CraigJolicoerArticle| replace:: Hosting Git Repositories on Dreamhost 789 | .. _CraigJolicoerArticle: http://craigjolicoeur.com/blog/hosting-git-repositories-on-dreamhost 790 | .. |KangArticle| replace:: Hosting Git repositories on Dreamhost 791 | .. _KangArticle: http://arvinderkang.com/2010/08/25/hosting-git-repositories-on-dreamhost/ 792 | .. _SuExec: http://wiki.dreamhost.com/Suexec 793 | .. _DRY: http://en.wikipedia.org/wiki/Don't_repeat_yourself 794 | .. _git-http-backend: http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html 795 | .. |GitSmartHTTP| replace:: Pro Git - Smart HTTP Transport 796 | .. _GitSmartHTTP: http://progit.org/2010/03/04/smart-http.html 797 | .. _Git homepage: http://git-scm.com/ 798 | .. _DreamHostWikiCGI: http://wiki.dreamhost.com/CGI 799 | .. |2nd| replace:: 2\ :sup:`nd` 800 | .. 801 | .. target-notes:: 802 | 803 | 804 | .. sectnum:: 805 | 806 | --------------------------------------------------------------------------------