11

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 resetforgit-clang-formatpre-receivegit checkoutgit resetgit-daemon同時プッシュ「セッション」のロック管理が組み込まれていますか? 対応するpre-receiveフック インスタンスを厳密に順番に実行しますか、それともインターリーブの可能性がありますか (未定義の動作を引き起こす可能性があります)。この問題には、具体的な保証を備えた組み込みの解決策があるはずだと何かが教えてくれます。そうしないと、リモートは一般的に(複雑なフックがなくても)同時プッシュにさらされてどのように機能しますか?そのような組み込みソリューションがある場合、コピーは冗長であり、ベア リポジトリを再利用するだけで実際に処理が高速化されます。ところで、この質問に関する公式ドキュメントへの参照は大歓迎です。

次に、ステージングされた(コミットされていない) 変更と特定のコミット (デフォルト)git-clang-formatのみを処理します。したがって、コーナーケースがどこにあるかを簡単に確認できます。はい、ルートコミット (リビジョン) と一緒です。実際には、リセット先の親がないため、ルート コミットには適用できません。したがって、2番目の質問に対する次のチェックがあります。HEADgit 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コード スタイルの規則に準拠しているかどうかを同じようにチェックできるようにすることができれば完璧です。

平和。

4

2 に答える 2