Позвольте сразу начать с отлома крюка pre-receive
, который я уже написал:
#!/bin/sh
##
format_bold='\033[1m'
format_red='\033[31m'
format_yellow='\033[33m'
format_normal='\033[0m'
##
format_error="${format_bold}${format_red}%s${format_normal}"
format_warning="${format_bold}${format_yellow}%s${format_normal}"
##
stdout() {
format="${1}"
shift
printf "${format}" "${@}"
}
##
stderr() {
stdout "${@}" 1>&2
}
##
output() {
format="${1}"
shift
stdout "${format}\n" "${@}"
}
##
error() {
format="${1}"
shift
stderr "${format_error}: ${format}\n" 'error' "${@}"
}
##
warning() {
format="${1}"
shift
stdout "${format_warning}: ${format}\n" 'warning' "${@}"
}
##
die() {
error "${@}"
exit 1
}
##
git() {
command git --no-pager "${@}"
}
##
list() {
git rev-list "${@}"
}
##
clang_format() {
git clang-format --style='file' "${@}"
}
##
while read sha1_old sha1_new ref; do
case "${ref}" in
refs/heads/*)
branch="$(expr "${ref}" : 'refs/heads/\(.*\)')"
if [ "$(expr "${sha1_new}" : '0*$')" -ne 0 ]; then # delete
unset sha1_new
# ...
else # update
if [ "$(expr "${sha1_old}" : '0*$')" -ne 0 ]; then # create
unset sha1_old
sha1_range="${sha1_new}"
else
sha1_range="${sha1_old}..${sha1_new}"
# ...
fi
fi
# ...
GIT_WORK_TREE="$(mktemp --tmpdir -d 'gitXXXXXX')"
export GIT_WORK_TREE
GIT_DIR="${GIT_WORK_TREE}/.git"
export GIT_DIR
mkdir -p "${GIT_DIR}"
cp -a * "${GIT_DIR}/"
ln -s "${PWD}/../.clang-format" "${GIT_WORK_TREE}/"
error=
for sha1 in $(list "${sha1_range}"); do
git checkout --force "${sha1}" > '/dev/null' 2>&1
if [ "$(list --count "${sha1}")" -eq 1 ]; then
# What should I put here?
else
git reset --soft 'HEAD~1' > '/dev/null' 2>&1
fi
diff="$(clang_format --diff)"
if [ "${diff%% *}" = 'diff' ]; then
error=1
error '%s: %s\n%s' \
'Code style issues detected' \
"${sha1}" \
"${diff}" \
1>&2
fi
done
if [ -n "${error}" ]; then
die '%s' 'Code style issues detected'
fi
fi
;;
refs/tags/*)
tag="$(expr "${ref}" : 'refs/tags/\(.*\)')"
# ...
;;
*)
# ...
;;
esac
done
exit 0
Примечание:
Места с нерелевантным кодом обрезаются с помощью # ...
.
Примечание:
Если вы не знакомы с git-clang-format
, посмотрите здесь.
Этот хук работает так, как ожидалось, и до сих пор я не заметил никаких ошибок, но если вы заметили какую-либо проблему или предложили улучшение, я был бы признателен за любой отчет. Наверное, я должен дать комментарий о том, какова цель этого крючка. Ну, он проверяет каждую нажатую ревизию на соответствие соглашениям стиля кода с помощью git-clang-format
, и если какой-либо из них не соответствует, он выведет соответствующий diff (тот, который говорит разработчикам, что должен быть исправлен) для каждого из них. В принципе, у меня есть два подробных вопроса относительно этого крюка.
Во-первых, обратите внимание, что я делаю копию открытого (серверного) годового репозитория в какой-то временный каталог и проверяю там код для анализа. Позвольте мне объяснить намерение этого. Обратите внимание, что я делаю несколько git checkout
и git reset
(из-за цикла for
), чтобы проанализировать все подталкиваемые ревизии по отдельности с помощью git-clang-format
. То, что я пытаюсь избежать здесь, - это (возможно) concurrency проблема с push-доступом к удаленному (серверному) репозиторию. То есть, у меня создается впечатление, что если несколько разработчиков попытаются одновременно нажать на удаленный компьютер с этим установленным крюком pre-receive
, это может вызвать проблемы, если каждый из этих push-сессий не выполняет git checkout
и git reset
с его частной копией репозитория. Итак, проще говоря, имеет ли git-daemon
встроенное управление блокировкой для одновременных push-сессий? Будет ли он выполнять соответствующие экземпляры крюка pre-receive
строго последовательно или есть возможность чередования (что может потенциально вызвать поведение undefined)? Что-то подсказывает мне, что для этой проблемы должно быть встроенное решение с конкретными гарантиями, в противном случае, как бы удаленные работы вообще (даже без сложных перехватов) подвергались одновременным нажатиям? Если есть такое встроенное решение, то копия избыточна и просто повторное использование голого репозитория фактически ускорит обработку. Кстати, любая ссылка на официальную документацию по этому вопросу очень приветствуется.
Во-вторых, git-clang-format
обрабатывает только поэтапные (но не зафиксированные) изменения по сравнению с определенным commit (HEAD
по умолчанию). Таким образом, вы можете легко увидеть, где находится угол. Да, он с корнем совершает (ревизии). На самом деле, git reset --soft 'HEAD~1'
не может применяться к корневым записям, поскольку у них нет родителей с reset to. Следовательно, следующая проверка с моим вторым вопросом:
if [ "$(list --count "${sha1}")" -eq 1 ]; then
# What should I put here?
else
git reset --soft 'HEAD~1' > '/dev/null' 2>&1
fi
Я пробовал git update-ref -d 'HEAD'
, но это разбивает репозиторий таким образом, что git-clang-format
больше не может его обрабатывать. Я полагаю, что это связано с тем, что все эти подталкиваемые ревизии, которые анализируются (включая этот корень), пока не принадлежат ни одной отрасли. То есть они находятся в состоянии HEAD
. Было бы неплохо найти решение этого углового случая, так что начальные коммиты также могут пройти ту же проверку с помощью git-clang-format
для соответствия соглашениям о стиле кода.
Мир.