🧑🔧
Git Hooks の post-commit で意識せずにコードフォーマット
先にまとめ
- Git Hooks の post-commit で半自動的にコードフォーマッターをかける
- フォーマッターの差分は別のコミットとして積まれる
背景
- 以前は pre-commit でフォーマッターをかけていた
- 本来の変更とフォーマッターの変更が一緒になってしまい、Pull Request 時にレビュアーの負荷が高かった
- プロジェクト全体に一括でフォーマットすれば、この問題は起きない
解決策
- pre-commit ではなく post-commit でフォーマットする
- 本来の変更とは別にコードフォーマットのコミットが作成される
コード
#!/bin/bash
set -euo pipefail
# 直前の commit の .swift ファイルに swift-format で整形して commit するスクリプト
readonly last_commit_message="$(git log -1 --pretty=%B | tr -d '\n')"
readonly commit_message='[post-commit] swift-format'
if [ "${last_commit_message}" = "${commit_message}" ]; then
# post-commit の連続実行を避ける
exit 0
fi
# 新規 or 変更されたファイルを抽出
readonly files=($(git diff HEAD~ --name-status | grep -E '^[AM].*.swift$' | awk '{print $2}'))
if [ ${#files[@]} -eq 0 ]; then
# 対象ファイルがなければ終了
exit 0
fi
readonly PROJECT_DIR="$(cd $(dirname ${BASH_SOURCE:-$0}); cd ..; pwd)"
readonly SWIFT_FORMAT="${PROJECT_DIR}/Tools/.build/release/swift-format"
if [ ! -x "${SWIFT_FORMAT}" ]; then
# swift-format コマンドが存在しなければ終了
echo 'If you want to format .swift files with swift-format, check the Makefile.'
exit 0
fi
# フォーマット実行
"${SWIFT_FORMAT}" format --in-place --configuration "${PROJECT_DIR}/.swift-format.json" --parallel "${files[@]}" || true
git add "${files[@]}"
git commit -m "${commit_message}" "${files[@]}"
こんな具合にフォーマットのコミットは別になる
補足
- 掲載したコードは apple/swift-format を使っているが、「対象ファイル抽出」「コミットメッセージ」「フォーマット実行」の3箇所を置き換えたら別の言語に転用可能
- Git Hooks は各自の開発環境で設定が必要なので、プロジェクトのセットアップ用の Makefile に Git Hooks 設定を追加して、各自が導入しやすいようにしている
# .git hooks 設定
.PHONY: git_config
git_config:
git config --local core.hooksPath "$(PROJECT_DIR)/.githooks"
Discussion