🧑‍🔧

Git Hooks の post-commit で意識せずにコードフォーマット

2023/12/28に公開

先にまとめ

  • 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[@]}"

こんな具合にフォーマットのコミットは別になる
screenshot

補足

  • 掲載したコードは 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