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 (開発者に何を修正する必要があるかを伝えるもの) を出力します。基本的に、このフックに関して 2 つの詳細な質問があります。
まず、リモート (サーバー) のベア リポジトリを一時ディレクトリにコピーし、そこで分析用にコードをチェックアウトしていることに注意してください。その意図を説明しよう。プッシュされたすべてのリビジョンを個別git checkout
に. ここで回避しようとしているのは、リモートの (サーバー) ベア リポジトリへのプッシュ アクセスにおける (可能性のある) 同時実行の問題です。つまり、複数の開発者がこのフックがインストールされたリモートに同時にプッシュしようとすると、これらのプッシュ「セッション」のそれぞれがプライベートコピーでs とs を実行しない場合、問題が発生する可能性があるという印象を受けています。リポジトリの。つまり、簡単に言えば、git reset
for
git-clang-format
pre-receive
git checkout
git reset
git-daemon
同時プッシュ「セッション」のロック管理が組み込まれていますか? 対応するpre-receive
フック インスタンスを厳密に順番に実行しますか、それともインターリーブの可能性がありますか (未定義の動作を引き起こす可能性があります)。この問題には、具体的な保証を備えた組み込みの解決策があるはずだと何かが教えてくれます。そうしないと、リモートは一般的に(複雑なフックがなくても)同時プッシュにさらされてどのように機能しますか?そのような組み込みソリューションがある場合、コピーは冗長であり、ベア リポジトリを再利用するだけで実際に処理が高速化されます。ところで、この質問に関する公式ドキュメントへの参照は大歓迎です。
次に、ステージングされた(コミットされていない) 変更と特定のコミット (デフォルト)git-clang-format
のみを処理します。したがって、コーナーケースがどこにあるかを簡単に確認できます。はい、ルートコミット (リビジョン) と一緒です。実際には、リセット先の親がないため、ルート コミットには適用できません。したがって、2番目の質問に対する次のチェックがあります。HEAD
git reset --soft 'HEAD~1'
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
コード スタイルの規則に準拠しているかどうかを同じようにチェックできるようにすることができれば完璧です。
平和。