Обычный хук пост-получения репозитория git больше не может оформить заказ

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

Однако коллега недавно клонировал голое репо (git clone <ssh url>), внес некоторые изменения в свое локальное репо, зафиксировал их, а затем отправил изменения обратно (git push origin master). Я не уверен, что именно этот процесс вызвал проблему, но с момента нажатия хук после получения больше не работает. Он не работает с выводом:

fatal: You are on a branch yet to be born

Вот крючок:

#!/bin/bash
GIT_WORK_TREE=/home/marweldc/app git checkout -f

Я могу перенести изменения, внесенные моим коллегой, с удаленного компьютера в мой локальный экземпляр. Запуск git branch внутри голого каталога репо на сервере показывает * master, а git log показывает все изменения, включая изменения моего коллеги, поэтому удаленное репо, очевидно, отслеживает все нормально.

Однако, если я сделаю, скажем, GIT_WORK_TREE=/home/marweldc/app git status, я получу следующий вывод:

# Not currently on any branch.
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ../CONTRIBUTING.md
#       ../README.inno.txt
#       <all the other files and folders that should be in the repo>
nothing added to commit but untracked files present (use "git add" to track)

Я также пробовал запустить GIT_WORK_TREE=/home/marweldc/app git checkout -f master, что дает:

error: pathspec '.git/master' did not match any file(s) known to git.

Мы в тупике. Что изменилось, из-за чего наш хук перестал работать таким образом, хотя раньше он работал нормально?

Изменить:

Некоторые вещи, которые я пробовал, комментируют ниже:

  • cat HEAD изнутри голого репо дает ref: refs/heads/master
  • GIT_TRACE=1 git rev-parse master из голой доходности репо

    trace: built-in: git 'rev-parse' 'master' 
    f6462b06f75d126ab932e3cfccef7385da2805ba 
    

    но та же команда с набором опций --work-tree дает

    trace: built-in: git 'rev-parse' 'master'
    master
    fatal: ambiguous argument 'master': unknown revision or path not in the working tree.
    Use '--' to separate paths from revisions
    
  • Запуск GIT_WORK_TREE=/home/marweldc/app-new git checkout -f (новый каталог для проверки) работает нормально

person ralbatross    schedule 24.04.2017    source источник
comment
что вы получаете от git worktree --list?   -  person Mark Adelsberger    schedule 24.04.2017
comment
Он ведет себя так, как будто у вас есть пустой репозиторий git. Пожалуйста, дважды проверьте URL-адреса, на которые вы нажимаете.   -  person kan    schedule 24.04.2017
comment
git worktree --list дает мне git: 'worktree' is not a git command. See 'git --help'. Видимо у меня устаревший гит?   -  person ralbatross    schedule 24.04.2017
comment
@kan мы почти уверены, что URL-адреса верны. Кроме того, разве тот факт, что мы получаем ожидаемые результаты от git log в удаленном репо, не означает, что он не пуст?   -  person ralbatross    schedule 24.04.2017


Ответы (1)


Изменить: есть очень специфическая (и, возможно, специфичная для CentOS) проблема с отдельными каталогами Git и рабочего дерева в очень старой версии Git 1.7.1, используемой здесь, так что для этого конкретного случая --work-tree=/home/marweldc/app приводит к тому, что Git не может путешествовать туда и обратно между каталогом Git и рабочим деревом. (Другие пути рабочего дерева не вызывают проблемы.)

Git не замечает, что ему не удалось вернуться к пустому репозиторию, а затем он не может ничего сделать с именами веток (поскольку он не может вернуться к репозиторию, он, скорее всего, потерпит неудачу все в эта точка).

Более современный Git, вероятно, вообще не имеет ошибки или заметит невозможность переключения между рабочим деревом и голым репозиторием.

Между тем, указание --git-dir=<path>, кажется, решает проблему.

Оригинальный (общий) ответ ниже.


Этот:

fatal: You are on a branch yet to be born

означает только то, что он говорит, хотя то, что он говорит, может сбивать с толку. :-) Вы действительно находитесь "на" какой-то еще не существующей ветке.

(Неясно, в какой ветке вы находитесь — у вас может быть «отключенный HEAD», как его называет Git, — но похоже, что у вас больше нет ветки master, если вы когда-либо были .)

Укороченная версия

Используйте git branch, чтобы увидеть, в какой ветке вы сейчас находитесь, и какие ветки у вас есть. Используйте git symbolic-ref HEAD refs/heads/master, чтобы принудительно установить HEAD голого репозитория обратно в ветку master. Для другой ветки с именем B используйте refs/heads/B.

Подробный ответ, то есть, что происходит, следует.


Независимо от того, пустой репозиторий или нет, в нем все равно есть текущая ветвь

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

Еще один способ узнать, на какой ветке вы находитесь, — запустить git branch:

$ git branch
  diff-merge-base
* master
  stash-exp

Это работает как в голых, так и в не голых репозиториях. * идет по имени текущей ветки.

Как в обычном репозитории изменить ветку, в которой вы находитесь?

Ответ1: git checkout. Например, git checkout master ставит вас (или меня) на master, а git checkout stash-exp ставит вас (или меня) на stash-exp.


1Есть еще один способ — использование команды подключения, но обычно вы не хотите использовать ее, так как это может испортить разницу между текущей фиксацией, индексом и работа-дерево. Но так как голый репозиторий не имеет рабочего дерева, это становится... ну, "безопасный" - это слишком сильно, но можно сказать "гораздо менее опасный".


Как в голом репозитории изменить ветку, в которой вы находитесь?

Ответ по-прежнему git checkout.

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

Конечно, вы не можете git checkout создать ветку в пустом репозитории, потому что это обновляет рабочее дерево. За исключением того, что вы можете использовать git --work-tree=... checkout .... Вы указываете имя ветки для извлечения, и это записывает имя новой ветки в HEAD, как обычно. Если вы извлекаете фиксацию по хэш-идентификатору, это отсоединяет HEAD, как обычно.< /эм>

Нерожденные (сиротские) ветки

Но есть еще один особый случай: если вы установите HEAD на имя ветки, которая еще не существует (как вы сделали бы, например, с git checkout --orphan), это помещает имя в HEAD без создания ветки. Это достаточно нормально в обычном (не голом) репозитории, за исключением того, что вы видите это только в двух случаях:

  • Когда вы впервые создаете новый пустой репозиторий. Вы находитесь на master, но master еще не существует. Вы разрешаете эту ситуацию, помещая коммит в репозиторий, который переходит в master, что создает master. (Новый коммит является корневым коммитом, т. е. не имеет родительского коммита.)

  • Когда вы используете git checkout --orphan: это настраивает вас так же, как когда вы находитесь в новом, пустом репозитории, записывая имя ветки в HEAD как обычно, но без создания ветки. Вы разрешаете эту ситуацию, записывая новый коммит, который переходит в нерожденную ветку, которая создает ветку, которая сейчас родилась (существует, указывая на новый корневой коммит, который вы только что сделали).

Делать то же самое в голом репозитории

Очевидно, вы не можете сделать это обычным способом в голом репозитории, потому что обычный способ — работать с рабочим деревом, git add вещей в индекс и git commit. Голый репозиторий не имеет рабочего дерева. (У него все еще есть индекс, и git checkout, что вы можете сделать, когда вы задаете рабочее дерево с --work-tree, по-прежнему записывает сквозь индекс. Это довольно сложно и путается с ловушками развертывания после получения многих людей, поскольку они не понимают индекс.)

То, что вы можете и делаете постоянно, в чистом репозитории — это получение git pushes. Когда вы получаете push, вы принимаете запросы (или команды) от другого Git в форме:

  • Пожалуйста, установите вашу ветку master для фиксации 1234567
  • Удалить ветку testing

(Вы сами решаете, следует ли и как проверять эти запросы в хуке pre-receive или update. Если вы не делаете ничего особенного, вы получаете встроенные проверки Git по умолчанию, которые позволяют любому создавать или удалять любую ветку или тег, но разрешить ускоренную перемотку вперед только для ветвей.)

Предположим, например, что в вашем голом репозитории есть ветка master, а ветка master еще не существует. Затем кто-то из Interwebs подключается к вашему серверу и говорит: «Эй, ты, голый репозиторий Git, здесь есть несколько коммитов, а теперь создай ветку master, указывающую на коммит 1234567!» В этом случае, предполагая, что вы принимаете этот запрос/команду, как только ваш Git закончит их обслуживание, у вас действительно теперь есть master. Это приведет вас к:

fatal: You are on a branch yet to be born

to:

On branch master

потому что ваша текущая ветвь master, которой раньше не было, теперь существует.

Использование сантехнических команд

команды подключения git update-ref и git symbolic-ref предназначены для использования в скриптах. Они предполагают, что вы точно знаете, что делаете. (Если вы используете их, не зная, что делаете, вы можете создать беспорядок: у них нет встроенных проверок безопасности.) Второй из них, в частности, заключается в том, как Git внутренне устанавливает HEAD, чтобы выбрать, на какой ветке вы находитесь. .

Если вы запустите:

git symbolic-ref HEAD refs/heads/branch

это записывает ref: refs/heads/branch в HEAD, и теперь вы On branch branch. Он не обновляет индекс и не обновляет рабочее дерево, но в голом репозитории нет рабочего дерева, и получение push-уведомления не обновляет index, так что это очень похоже на то, что происходит, когда вы получаете push.

Таким образом, это достаточно безопасное занятие. Это позволяет вам изменить ветку, в которой вы находитесь, без использования git checkout. Это нормальный и правильный, хотя и неудобный — убедитесь, что вы правильно пишете имя ветки и включаете префикс refs/heads/ — способ установить HEAD в голом репозитории.

person torek    schedule 24.04.2017
comment
Спасибо за подробный ответ, но я думаю, что я все еще что-то упускаю. Голое репо находится в главной ветке (git branch дает * master), и эта ветвь ДЕЙСТВИТЕЛЬНО существовала ранее (я нажимал на голое репо, и хук после получения работал нормально в течение нескольких месяцев, до сих пор; кроме того, cat HEAD на голое репо дает ref: refs/heads/master). Однако репо, похоже, думает, что это не ветка, когда что-то делает с рабочим деревом. Итак, мне до сих пор не ясно, какое заклинание git мне нужно сделать, чтобы исправить нашу ситуацию. - person ralbatross; 25.04.2017
comment
Интересный. Если вы находитесь на сервере и запускаете git rev-parse master в голом репозитории, появляется ли хэш-идентификатор? Если это сработает, попробуйте то же самое с git --work-tree=... rev-parse master (или то же самое с переменной env — внутри они работают точно так же); он все еще должен работать. В таком случае, почему git checkout не работает? - person torek; 25.04.2017
comment
git rev-parse master дает хэш-идентификатор, но запуск с флагом --work-tree дает fatal: ambiguous argument 'master': unknown revision or path not in the working tree. (и да, рабочий путь дерева существует.) - person ralbatross; 25.04.2017
comment
Ух ты. Это не должно произойти. В качестве эксперимента попробуйте GIT_TRACE=1 git --work-tree=... rev-parse master и то же самое без --work-tree. Они должны сделать то же самое, например, напечатать 13:31:40.410988 git.c:371 trace: built-in: git 'rev-parse' 'master' (а затем хеш; метки времени, конечно, изменятся). - person torek; 25.04.2017
comment
Аналогичный результат: без настройки рабочего дерева я получаю ожидаемый результат; когда я устанавливаю рабочее дерево, я получаю trace: built-in: git 'rev-parse' 'master' master fatal: ambiguous argument 'master': unknown revision or path not in the working tree. Use '--' to separate paths from revisions - person ralbatross; 26.04.2017
comment
Еще один интересный момент: запуск git --work-tree=... checkout -f в пустом временном каталоге, который я настроил, работает нормально... - person ralbatross; 26.04.2017
comment
Вау (опять же). Еще один возможный эксперимент, что произойдет, если вы запустите git --work-tree=... --git-dir=<path-to-bare-repo> rev-parse master (и то же самое с git branch и т. д.)? В любом случае, похоже, что ваш Git уходит в никогда-никогда, когда используется с этим конкретным --work-tree, и это совершенно загадочно. Я попытался указать свой собственный Git на рабочее дерево, в котором было репозиторий Git, и это его не беспокоило, оно все еще использовало голое репо. - person torek; 26.04.2017
comment
Команда non-work-tree работает, но когда я пытаюсь включить параметр --work-tree, я получаю fatal: Could not jump back into original cwd: No such file or directory. (И я без проблем могу войти в путь к рабочему дереву и увидеть там файлы... так странно) - person ralbatross; 26.04.2017
comment
Хм, то, что не удалось вернуться к исходному cwd, явно связано с проблемой. Я не знаю, почему Git не сможет вернуться назад (вероятно, какая-то странная проблема с разрешениями), но он не может вернуться к голому репо, из-за чего он не может проанализировать master. Вы выявили какую-то другую ошибку (Git должен был заметить сбой раньше, а не просто сказать: duuurrr не может найти master...), но посмотрите, сможете ли вы понять, почему Git не может вернуться к точке A. как только он переместится в точку B. - person torek; 26.04.2017
comment
Кстати, какая это версия Git? Я не вижу Could not jump back ни в одном из исходных файлов Git 2.12+. - person torek; 26.04.2017
comment
А, нашел: строка немного изменилась еще в Git 1.7.10. Вы должны использовать еще более старый Git. Вы, вероятно, используете CentOS (насколько я знаю, только пользователи CentOS застряли на древней версии 1.7.1 Git). - person torek; 26.04.2017
comment
Вы правы, я использую git 1.7.1, и я верю, что сервер работает под управлением CentOS. - person ralbatross; 26.04.2017
comment
О боже, я запускал эти команды из корневого каталога голого репо. Когда я запускаю извне и указываю --git-dir, он работает: P Я уже отметил ваш ответ как правильный, но, вероятно, стоит добавить примечание к вашему ответу, чтобы отразить этот вывод. - person ralbatross; 26.04.2017
comment
Я добавил в ответ переднюю часть, описывающую все это. - person torek; 26.04.2017
comment
Я использую в своем hook git --git-dir=/foo/tik.git --work-tree=/home/foo/tik checkout master -f - создает ли git рабочее дерево, если оно не существует? Я попробую, конечно, но интересно, git вообще создает каталоги по своим командам или нет. - person Timo; 15.05.2021
comment
@Timo: когда я тестировал это (вероятно, в Git 1.8 или около того), Git не mkdir создавал рабочее дерево из ничего, а mkdir подкаталогов внутри пустого рабочего дерева. Я ожидаю, что этот конкретный фрагмент кода может быть нестабильным от выпуска к выпуску. Неясно, должен ли Git создавать рабочее дерево только потому, что вы назвали его, но для внутренних целей это может быть удобно, так что Git может переключаться между разными подпрограммами по разным причинам, от версии к версии. - person torek; 15.05.2021