Каков наилучший способ написать перехватчик обновления git, который отклоняет недействительные коммиты подмодуля? - PullRequest
10 голосов
/ 21 января 2011

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

Одна оговорка:

  • Я хочу протестировать только субмодули, чьи исходные репозитории существуют на том же сервере, что и родительский репозиторий. В противном случае мы начинаем делать сумасшедшие вещи, такие как «git clone» или «git fetch» ​​изнутри git hook, что было бы неинтересно.

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

  1. Проверьте переданное в ловушку refname, чтобы увидеть, обновляем ли мы что-то под refs/heads/. Если нет, выходите пораньше.
  2. Используйте git rev-list, чтобы получить список удаляемых ревизий.
  3. Для каждой ревизии:
    1. Вызовите git show <revision_id> и используйте регулярное выражение, которое проверяет, был ли обновлен подмодуль (путем поиска `+ Subproject commit [0-9a-f] +).
    2. Если этот коммит изменил подмодуль, получите содержимое файлов .gitmodules, как видно из этого конкретного коммита (git show <revision_id>:.gitmodules).
    3. Используйте результаты 3.1 и 3.2, чтобы получить список URL субмодулей и их обновленные идентификаторы коммитов.
    4. Сверьте этот список, созданный в 3.3, с внешним файлом, который отображает URL-адреса субмодулей в локальные репозитории с открытым исходным кодом в файловой системе.
    5. cd к путям, найденным в 3.4, и выполните git rev-parse --quiet --verify <updated_submodule_commit_id>, чтобы увидеть, существует ли эта фиксация в этом хранилище. Если это не так, выйдите с ненулевым статусом.

(Примечание. Я полагаю, что результаты версии 3.2 могут быть кэшированы между ревизиями, если вывод git rev-parse --quiet --verify <revision_id>:.gitmodules не меняется от одной ревизии к другой. Я упустил эту часть, чтобы упростить решение.)

Так что да, это кажется довольно сложным, и я не могу не задаться вопросом, существуют ли какие-то внутренние команды git, которые могут сделать мою жизнь намного проще. Или, может быть, есть другой способ думать о проблеме?

Ответы [ 2 ]

3 голосов
/ 02 июля 2012

Вот моя маленькая попытка зацепить обновление git. Документирование здесь, чтобы оно могло быть полезным для других. Известно, что особый случай «0000 ...» не обрабатывается.

#!/bin/bash

REF=$1
OLD=$2
NEW=$3

# This update hook is based on the following information:
# /2634675/funktsiya-stsenariya-bash-shell-dlya-proverki-nalichiya-git-tega-ili-fiksatsii-i-ego-otpravki-udalennoe-hranilische

# Get a list of submodules
git config --file <(git show $NEW:.gitmodules) --get-regexp 'submodule..*.path' | while read key path
do
    url=$(git config --file <(git show $NEW:.gitmodules) --get "${key/.path/.url}")
    git diff "$OLD..$NEW" -- "$path" | grep -e '^+Subproject commit ' |
    cut -f3 -d ' ' | while read new_rev
    do
        LINES=$(GIT_DIR="$url" git branch --quiet --contains "$new_rev" 2>/dev/null | wc -l)
        if [ $LINES == 0 ]
        then
            echo "Commit $new_rev not found in submodule $path ($url)" >&2
            echo "Please push that submodule first" >&2
            exit 1
        fi
    done || exit 1
done || exit 1

exit 0
3 голосов
/ 22 января 2011

Редактирование, намного позже: Начиная с Git 1.7.7, git-push теперь имеет опцию --recurse-submodules=check, которая отказывается выдвигать родительский проект, если какие-либо коммиты субмодуля не были переданы на их пульты. Похоже, что соответствующий параметр конфигурации push.recurseSubmodules был добавлен. Это, конечно, не полностью решает проблему - невежественный пользователь все еще может нажать без чека - но это весьма актуально!

Я думаю, что лучший подход, вместо того, чтобы изучать каждый отдельный коммит, - это просматривать различия между всеми выдвинутыми коммитами: git diff <old> <new>. Вы действительно не хотите смотреть на все различия; это может быть огромным. К сожалению, команда git-submodule porcelain не работает в пустых репозиториях, но вы все равно сможете быстро просмотреть .gitmodules, чтобы получить список путей (и, возможно, URL). Для каждого из них вы можете git diff <old> <new> -- path, и, если есть diff, взять новый коммит подмодуля. (И если вы беспокоитесь о возможности фиксации 000000, вы можете просто использовать git show на новой, я считаю.)

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...