4

Есть ли способ обслуживать файлы из веток в git-репо, не проверяя их в разных каталогах?

Мне бы очень понравилось удобство создания новой ветки, отправки ее на тестовый репозиторий сервера, а затем сделать что-то вроде перехода по адресу http://branchname.test.com. Я могу использовать правила перезаписи для обработки субдомена имени ветки, чтобы указывать на разные корневые пути, но Apache, конечно, не может читать пустые репозитории.

Гитфс может работать, но кажется заброшенным.

Мне не особенно нужен Apache.

3 ответа3

3

Вы можете обслуживать контент непосредственно из репозитория, используя комбинацию git-show (или git cat-file) и Apache CGI или аналогичного. Я настроил пример с использованием .htaccess и простого скрипта zsh:

.htaccess

Options +ExecCGI
AddHandler cgi-script .sh

RewriteEngine On
RewriteRule ^(?!index.sh)(.*)$ /index.sh [L]

index.sh

#!/bin/zsh

# Strips the initial slash, as git show ain't got time for that!
# REQUEST_URI is set by Apache when executing the script.
# You may also be interested in HTTP_HOST which is the request host.
# Don't get confused with HOST which is a default shell variable.
function get_request_uri {
    if [ $REQUEST_URI[0,1] = '/' ]
    then
        REQUEST_URI=$REQUEST_URI:s#/##
    fi

    echo $REQUEST_URI
}

# You may not want to show the folders (represented by trees).
# Or you may want to manipulate the output to produce a proper directory index.
function is_file_folder {
    [ $(git cat-file -t HEAD:$1) = 'tree' ]
}

# Just gets the content of the file.
# For trees will produce a directory listing, one line per file.
# To show different branches, pass them to git show in place of HEAD
function get_file_content {
    local file=$1

    git show HEAD:${file}
}

# Attempts to determine the file type without writing the data to a file.
# Has to resort to manually looking up the type in /usr/share/mime/globs
# to test by extension, you may have to adjust that code to your environment.
function get_file_type {
    local file=$1
    local content=$2

    mime_type=$(echo ${content} | mimetype -b --stdin)
    if [ -e /usr/share/mime/globs ]
    then
        file_mime_type=$(
            grep "\*.${file:e}\$" /usr/share/mime/globs |
                sed -ne 's/:.*$//' -e '1p'
                    )

        # Conflict resolution is also something you should work on.
        if [ $file_mime_type != '' ]
        then
            mime_type=$file_mime_type
        fi
    fi

    echo $mime_type
}

function do_404 {
    echo 'Status: 404 Not Found'
    echo ''
}

# Just does the header because there can be problems
# getting binary data to the client by echoing it.
# A little something for you to explore a fix for :)
function do_200 {
    mime_type=$1

    echo "Content-Type: $mime_type"
    echo ''
}


cd site.git

uri=$(get_request_uri)
file_content=$(get_file_content $uri)

# A non zero exit status from git show probably means the file does not exist.
# Even if the error was something else, you still have nothing to show for it.
if [ $? -ne 0 ]
then
    do_404
fi

# You may not want to do the directory listing. Even if you do, you may
# not want to use the default directory listing (which is very primitive).
# If you wanted to redirect to index or similar, you would also handle that
# here.
if is_file_folder $uri
then
    file_type='text/plain'
else
    file_type=$(get_file_type $uri "$file_content")
fi

do_200 $file_type

# Doing this twice is not optimal, but it is very easy to
# mangle binary data accidentally when passing around the
# $file_content. Just reading the file directly to the client
# seems to sidestep this. Another thing for you to fix :)
get_file_content $uri

# You might want to uncomment the two lines below (commenting out 
# any response code from above first), to show all of the variables
# available to you. This list will include the variables set by this
# script - to filter them out just run this earlier, with an exit after.

# do_200 'text/plain'
# set

# vim: set ai et sw=4 syntax=zsh :

Это предполагает, что ваш сайт находится в site.git в той же папке, что и два файла выше. Есть много улучшений, которые могут быть сделаны в сценарии - как прокомментировано. Это скорее подтверждение концепции.

2

Простейшим подходом будет проверка каждой ветви и создание клонов для каждой ветви в подкаталогах. Это очень легко сделать с помощью скрипта, запускаемого cron (или с помощью пост-фиксации), например:

#!/bin/sh
ROOT=/srv/repos/control
cd ${ROOT} # it's an internal repo
git fetch
for ref in .git/refs/remotes/origin/*; do
  BRANCH=`basename ${ref}`
  if ! [ -d ../${BRANCH} ]; then
    git clone --local ${ROOT} ../${BRANCH}
    cd ../${BRANCH}
    git branch --track ${BRANCH} remotes/origin/${BRANCH}
    git checkout ${BRANCH}
  else
    cd ../${BRANCH}
    git pull
  fi
done

После его работы вы получите кучу каталогов с ветками в /srv/repos .

git clone --local очень быстр и оптимален даже для огромных репозиториев, таких как ядро Linux, потому что он не создает все объекты в репозитории, а вместо этого создает жесткие ссылки на них, поэтому вы не будете тратить пространство на копии репозитория. Вы будете тратить пустое пространство на извлеченные копии, но а) они, вероятно, не очень большие, и б) это сделает веб-доступ намного проще и быстрее.

1

Также взгляните на Divergence, стоечное приложение, которое отображает ветви git на поддомены. Я еще не использовал его сам, но, кажется, он вполне соответствует вашим потребностям.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .