Поднимаем http-репозитарий для git

После покупки ноутбука и организации небольшой домашней сети появилась проблема — как синхронизировать проекты на разных комьютерах? Решение очевидно — сделать из одного из них центральный хаб.

Git предоставляет несколько способов организации общего доступа к хранилищам. Сначала я попробовал способ с gitosis — не понравился. Там используется довольно мутный механизм авторизации и организации хранилища. В общем, остановился на Apache + gitweb.
В данном способе apache используется git для доступа к хранилищам по протоколу http, а gitweb организует удобный просмотр проектов через браузер. Способ привлек минимумом необходимых компонентов. Apache и git у меня и так установлены, а gitweb — это всего лишь скрипт на perl и входит стандартную поставку git. Кстати, вместо него можно ипользовать cgit — то же самое, только написан на C.

Руководств по настройке подобной связки в интернете полно. Но вопросы все равно возникают. Поэтому в статье я попытаюсь дать помимо готовых настроек еще и методики поиска проблем и некоторый объем теории.

Начало

Устанавливаем, если еще нет, Apache, git
1. Для Apache понадобятся дополнительные модули mod_rewrite, mod_dav, mod_dav_fs, mod_env. Остальные, вроде mod_auth — по желанию
2. Настраиваем базовый виртуальный хост, у меня www.github.local
3. Cоздаем в DOCUMENT_ROOT виртуального хоста каталог для проектов. Я назвал его ./git

Немного теории

В принципе, на этом можно было бы остановиться.
Для базовой работы с git достаточно, но это не интересно. Все, что будем делать в дальнейшем нужно для развязывания обращений git к репозитариям и браузера к gitweb. Другими словами, необходимо чтобы на запросы git к репозитарию не отвечал gitweb и наоборот.

Это довольно неочевидный момент, который практически не упоминают в руководствах. Я долго был уверен, что gitweb как раз и обрабатывает подключения git-клиентов, а возможность просмотра репозитариев — просто приятное дополнение.

Настраиваем gitweb

Здесь ничего сложного нет, просто создаем файл со следующим содержанием:
my_uri = "http://www.github.local/git"; # адрес репозиториев
$site_name = "www.github.local"; # название сайта, отображается в заголовке
$projectroot = "/home/www/github/git/"; # путь к репозиториям git на жёстком диске
 
$git_temp = "/tmp";
$home_link = $my_uri; # ссылка на «домашнюю страничку»
$projects_list = $projectroot;
$stylesheet = "/gitweb/static/gitweb.css";
$logo = "/gitweb/static/git-logo.png";
$favicon = "/gitweb/static/git-favicon.png";
$projects_list_description_width = 40;
$javascript = "/gitweb/static/gitweb.js";
 
$feature{'pathinfo'}{'default'} = [1];
Я назвал его gitweb.conf и поместил в DOCUMENT_ROOT. Не самое удачное решение, но у меня локальная сеть из 2-х компьютеров и проблем с безопасностью я не ожидаю.

Обратите внимание, uri всех дополнительных файлов gitweb (стили, лого, скрипты) указываются относительно каталога gitweb и при настройке хостинга мы укажем соответствущий алиас.

В качестве альтернативы можно предложить просто скопировать gitweb в DOCUMENT_ROOT хоста, тогда бы все это делать не пришлось, но потерялась бы возможность автоматического обновления.

Далее стоит проверить, чтобы в конфигрурации apache была задан путь для базы данных mod_dav — DAVLockDB, такой файл существует и доступен для записи. Обычно с этим все в порядке, но проверить не помешает.

Настраиваем Apache

Файл с настройкой виртуального хоста выглядит так:
<VirtualHost *:80>
    DocumentRoot "/home/www/github"
    ServerName www.github.local
    ErrorLog "/var/log/httpd/github-error_log"
    CustomLog "/var/log/httpd/github-access_log" combined
    LogLevel debug
 
    SetEnv GITWEB_CONFIG /home/www/github/gitweb.conf
    Alias /gitweb /usr/share/gitweb/
 
    RewriteEngine On
    RewriteLogLevel 9
    RewriteLog "/var/log/httpd/github-rewrite_log"
    RewriteRule ^/$ /cgi-bin/gitweb.cgi [L,PT]
    # Переписываем не для реальных файлов и каталогов. По идее, должно работать и без DOCUMENT_ROOT, но почему-то не работает
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
    # исключаем из подстановки системные объекты git - к ним обращаемся непосредственно
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !/git/[a-zA-Z0-9-]+\.git/(HEAD|info|objects|refs)
    # PT - очень важно! Иначе не происходит подстановка Alias-а, и все ссылается на каталог DocumentRoot
    RewriteRule ^/git/(.*) /cgi-bin/gitweb.cgi/$1 [PT]
 
    # Эта штука обязательно после RewriteRule - почему, см. выше
    ScriptAlias /cgi-bin/ /usr/share/gitweb/
 
    <Directory "/home/www/github/">
	Options Indexes FollowSymlinks ExecCGI
	Order allow,deny
	Allow from all
    </Directory>
 
    <Location "/git">
	Dav On
    </Location>
</VirtualHost>

Первые несколько строк должны быть понятны — просто настройка путей и имен.

Дальше уже интереснее — устанавливаем переменную окружения GITWEB_CONFIG, которая сообщает gitweb расположение файла конфигурации и создаем алиас на реальный каталог gitweb.

Теперь самое важное. Настройка RewriteEngine.

Главную страничку будет генерировать gitweb:
RewriteRule ^/$ /cgi-bin/gitweb.cgi [L,PT]


Смысл следующих строк в том, что apache должен выполнять скрипт gitweb для всех запросов, кроме тех, что направлены непосредственно к реальным файлам каталогам проектов.

Теоретически, для этого было бы достаточно !-f !-d, но это не совсем так и я потратил довольно много времени на выяснение причин. Дело в том, что при выполнении push-а в удаленный репозитарий git отправляет запросы к mod_dav на блокировку некоторых файлов и каталогов, но они не всегда в этот момент существуют! А раз их нет, то apache с полным правом перенаправляет эти запросы к gitweb и в результате получаем ерунду. Так что третье RewriteCond нужно использовать обязательно!

# Переписываем не для реальных файлов и каталогов. По идее, должно работать и без DOCUMENT_ROOT, но почему-то не работает
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
# исключаем из подстановки системные объекты git - к ним обращаемся непосредственно
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !/git/[a-zA-Z0-9-]+\.git/(HEAD|info|objects|refs)
# PT - очень важно! Иначе не происходит подстановка Alias-а, и все ссылается на каталог DocumentRoot
RewriteRule ^/git/(.*) /cgi-bin/gitweb.cgi/$1 [PT]
Здесь везде используются флаги PT для RewriteRule. Этот флаг означает, что Apache после подстановки адреса в самом правиле также применит и следующую команду из конфиг-а. В данном случае это ScriptAlias /cgi-bin/ /usr/share/gitweb/. Т.о. эта опция обязательно должна идти после всех RewriteRule.

Для авторизованного доступа к репозитариям необходимо в раздел Location следующие строки:
AuthType Basic
AuthName "Git"
AuthUserFile "/home/www/github/passwd.git"
<LimitExcept GET HEAD PROPFIND OPTIONS REPORT>
    Require valid-user
</LimitExcept>
B создать файл паролей. Также может потребоваться создать файл .netrc в домашнем каталоге пользователя для получения доступа к серверу вообще, а не к репозитариям. Правда нет смысла требовать авторизацию при доступе на чтение, т.к. содержимое проектов можно получить и через gitweb.

Создание репозитария

Здесь есть одна тонкость. Cам репозитарий содается, как обычно
git init --bare myproject.git


Но после этого необходимо зайти в myproject.git/hooks и скопировать (или перименовать) файл post-update.example в post-update. Проверьте, чтобы он был исполняемым. Это задаст обработчик, вызываемый после обновления репозитария. Если этого не сделать, при выполнении push изменения в хранилище передадутся, но все указатели HEAD не изменятся, что потом может привести к конфликтам.

Отладка

Все это замечательно, но, как правило, сразу все не заработает, и нужно будет искать причину. Вот несколько советов.
1. Включите логирование всего с максимальной подробностью (в конфиге выше это сделано)
2. Попробуйте работу git-а без всяких RewriteRule, чтобы убедиться что все доступы и базовые настройки выполенены паравильно
3. После добавления RewriteRule, в случае каких-либо проблем изучайте лог mod_rewrite. Особенно стоит обращать внимание на служебные запросы git — MKCOL, LOCK, UNLOCK и т.п. Подавляющее большинство ошибок, по крайней мене у меня, состояло в том, что такие запросы обрабатывал gitweb, вместо самого apache
4. Если вы используете авторизованный доступ — попробуйте его отключить на время

Заключение

Некоторые моменты в этой статье я не осветил. В частности, почти не затронуты вопросы авторизации, возможности доступа не по паролю, а по ключу.

Также, я думаю, можно упростить и конфигурационный файл apache, в частности, избавиться от RewriteCondition вообще, а все необходимые условия указывать сразу в RewriteRule.

Но, надеюсь, помог кому-нибудь разобраться с основными принципами, и теперь настроить все необходимое самостоятельно не стоставит труда.

Ссылки

http://habrahabr.ru/blogs/Git/43806/ — отсюда я начал свои попытки настроить сервер, и отсюда же взял основы для файлов конфигурации.
https://git.wiki.kernel.org/index.php/Gitweb — официальный сайт gitweb. Есть подробные описания и примеры настроек.


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.