Этот пост-приемный крюк имеет серьезные недостатки. Хорошая новость заключается в том, что это легко исправить.
root вашей конкретной проблемы заключается в том, что каждый Git репозиторий, включая пустой 1 , который получает pu sh здесь начинается с одного (только одного) index . Индекс отслеживает (одиночное, как в одном) рабочее дерево. Это верно, даже если весь смысл чистого репозитория состоит в том, чтобы иметь нет рабочего дерева: у него все еще есть индекс.
Когда кто-то использует git --work-tree=<path>
с этим пустым репозиторием, чтобы сделать git checkout
операция, эта операция извлечения использует (один, один) индекс для отслеживания того, что было сохранено в (временно добавленном) рабочем дереве. Таким образом, на время git checkout
этот пустой репозиторий становится непокрытым: он имеет один индекс и одно рабочее дерево, и рабочее дерево является тем, которое выбрано для этой операции git checkout
.
Для каждой последующей git --work-tree=<path> checkout
операции Git будет предполагать , что индекс правильно описывает текущую проверку для целевого рабочего дерева. (Git может обнаружить, что при выполнении своей работы индекс неправильно описывает целевое рабочее дерево, но начинает с предположения, что это так.) Файлы, которые Git решает обновить в что рабочее дерево будет основано на этом допущении, с некоторыми исправлениями, которые могут, но не всегда, вставляться по ходу дела. Это основное предположение о том, что индекс правильно описывает текущую проверку для рабочего дерева, содержит тогда и только тогда, когда аргумент path
, используемый в последующем git checkout
- это то же , что и аргумент path
, использованный в previous git checkout
.
Но у нас есть два разных git checkout
команды:
git --work-tree=/home/www/domain.com/subdomains/www ...
и:
git --work-tree=/home/www/domain.com/subdomains/test
(за исключением небольшого: что-то пошло не так с вырезанным и вставленным выше, так как опция --git-dir
объединена с --work-tree
; я сделал здесь свое собственное предположение об исправлении).
Это два разных пути. Поэтому им нужны два разных индексных файла - или вообще нет индекса. 2
1 A голое хранилище - Git хранилище, где core.bare
установлено на true
. Пустой репозиторий не имеет рабочего дерева, и его файлы базы данных Git хранятся на верхнем уровне, а не в отдельном каталоге .git
.
2 Когда индекс отсутствует Как и в начальном git checkout
, Git создаст его по мере необходимости. Это более ресурсоемкий процесс, и в непонятном хранилище, где мы фактически используем индекс для построения следующего коммита, уничтожение индекса теряет наш тщательно построенный следующий коммит: Git создает новый один из текущего коммита снова, поэтому мы должны начать заново.
Само рабочее дерево не затронуто этим процессом перестройки, но если мы можем позволить себе пространство для одного индексного файла на рабочее дерево - и индексные файлы, как правило, довольно маленькие - кажется, что мы должны это сделать. Тем не менее, это вариант, если вы хотите.
Исправление проблемы
Во-первых, хотя это на самом деле не связано, давайте исправим другие очевидные (но незначительная) проблема:
while read oldrev newrev ref
do
branch=`echo $ref | cut -d/ -f3`
Предположим, мы добавили тег с именем master/v1.1
. Этот реф как полное его написание имеет название refs/tags/master/v1.1
. Давайте посмотрим, что мы получим здесь:
$ ref=refs/tags/master/v1.1
$ branch=`echo $ref | cut -d/ -f3`
$ echo $branch
master
Упс: наш код будет думать, что мы обновили ответвление master
, когда мы сделали добавили тег master/v1.1
. Вероятно, мы не будем использовать такой тег, но почему бы не сделать это правильно? Вместо использования cut
, давайте проверим всю ссылку:
case $ref in
refs/heads/*) branch=${ref#refs/heads/};;
*) continue;; # not a branch
esac
Попробовав это с нашей проверочной ссылкой, мы получим:
$ case $ref in
> refs/heads/*) branch=${ref#refs/heads/};;
> *) echo not a branch;;
> esac
not a branch
Замена ref
на refs/heads/master
we get:
$ ref=refs/heads/master
$ case $ref in
refs/heads/*) branch=${ref#refs/heads/};;
*) echo not a branch;;
esac
$ echo $branch
master
(В этом разделе нет вывода PS2
, потому что я использовал control-P для повторения предыдущего case
.)
Для истинной правильности мы также должны взглянуть на $old
и $new
, чтобы определить, создается ли рассматриваемая ссылка, обновляется или удаляется; но в этом случае мы можем просто предположить, что имена ветвей master
и dev
никогда не будут удалены. Поэтому наш новый код выглядит так:
#!/bin/sh
while read oldrev newrev ref
do
case $ref in
refs/heads/*) branch=${ref#refs/heads/};;
*) continue;; # not a branch - do not deploy
esac
case "$branch" in
master) wt=/home/www/domain.com/subdomains/www loc=production;;
dev) wt=home/www/domain.com/subdomains/test loc=dev;;
*) continue;; # not an INTERESTING branch - do not deploy
esac
# It's an interesting branch; deploy it to $wt. Use an index
# based on the branch name. There is no need to specify the
# git directory, which is in $GIT_DIR right now because this is
# a post-receive script.
GIT_INDEX_FILE=index.$branch git --work-tree=$wt checkout $branch -f
echo "deployed to $loc"
done
Опция -f
для git checkout
здесь несколько опасна: она будет перезаписывать или удалять файлы, которые были изменены в рабочем дереве, даже если у нас теперь есть правильный индекс для отслеживания того, какие файлы должны быть в каком состоянии. Но он был там раньше, поэтому я сохранил его.
Этот сценарий пост-получения (который я должен отметить, полностью не проверен: следите за опечатками) все еще имеет недостаток. Какую бы ветку мы здесь git checkout
не оставили, этот набор хранилищ будет рекомендовать эту ветку тому, кто клонирует этот репозиторий. Исправление требует обновления HEAD
:
git symbolic-ref HEAD refs/heads/master
после шага git checkout
. Вы все время жили с этим недостатком, так что, возможно, вас это не волнует.