GIT ловушка после получения для большего количества веток - рабочий процесс или проблема с отметкой времени? - PullRequest
0 голосов
/ 27 марта 2020

У меня есть папка разработки и производства на одном сервере и 1 репозиторий за ними в pu sh для обеих папок, в зависимости от ветви, которая перемещается. Я хотел бы, чтобы папка разработки (тестовая) была развернута, когда dev помещен в репозиторий, и производственная папка (www), когда мастер нажат.

Это работает, но у меня проблема переписывания всех файлов а не только те, которые изменились.

Пример:

  • Я нахожусь в локальной ветке dev, вносить изменения только в индекс файла. php, commit, pu sh - Изменения папка развернута для разработки, только индекс. php изменено (отметка времени нового файла).
  • Следующий шаг: git мастер извлечения, git объединение с веткой dev и затем
    pu sh to master - изменения правильно развернуты в рабочей папке, но не только индекс. php изменен, но все файлы на FTP имеют новую
    метку времени.

  • Далее шаг: git извлеките dev, внесите некоторые изменения в fileX. php, commit и pu sh в dev - изменения правильно развернуты в папке dev, но опять-таки все файлы на FTP имеют новую метку времени, а не только fileX. php

Это нормально или я что-то не так делаю? Может быть, вы можете порекомендовать лучший рабочий процесс git, если у меня есть 1 локальный P C с git - 1 домен разработки и 1 рабочий домен?

Спасибо за помощь

post-receive крючок:

#!/bin/sh
while read oldrev newrev ref
do
  branch=`echo $ref | cut -d/ -f3`
  if [ "master" == "$branch" ]; then
    git --work-tree=/home/www/domain.com/subdomains/www --git-dir=/home/www/domain.com/subdomains/repos/myrepo.git checkout master -f
    echo 'changes pushed to production'
  else
    git --work-tree=/home/www/domain.com/subdomains/test--git-dir=/home/www/domain.com/subdomains/repos/myrepo.git checkout dev -f
    echo 'changes pushed to dev'
  fi
done

1 Ответ

1 голос
/ 27 марта 2020

Этот пост-приемный крюк имеет серьезные недостатки. Хорошая новость заключается в том, что это легко исправить.

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. Вы все время жили с этим недостатком, так что, возможно, вас это не волнует.

...