pre-commitの代替ツールのprekを試してみた
はじめに
prekというpre-commitの高速なRust製の代替ツールがあることを知ったので試してみたいと思います。
何を試してみたか
自分にとって身近なプロジェクト構成で、どのくらい早くなるのかを確認したかったため、適当なPythonプロジェクトを作成し、prek公式のベンチマークと同じように、hyperfineを使って複数のシナリオで測定しました。
- 初期インストール(install_hook)
- フック実行(対象なし、全フック、特定フック)
全体環境
- MacBook Air/Apple M3/16GB
- mise: 2025.10.7 macos-arm64 (2025-10-10)
- hyperfine: 1.19.0
miseとhyperfineはbrewで導入しています。
それ以外に使ったツールやバージョンはmiseやuvによって管理しています。
prekの導入(pre-commitも)
miseを使って環境を構築しました。
[tools]
python = "3.14"
pre-commit = "4.3.0"
uv = "0.8.13"
prek = "0.2.5"
mise install
これでpre-commitとprekの両方が使える状態になります。
プロジェクトの設定
.pre-commit-config.yaml
pre-commitとprekで使用する設定ファイルをこのような形で準備しています。
repos:
# General pre-commit hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
stages: [pre-commit, manual]
- id: end-of-file-fixer
stages: [pre-commit, manual]
- id: check-yaml
stages: [pre-commit, manual]
- id: check-toml
stages: [pre-commit, manual]
- id: check-added-large-files
args: [--maxkb=500]
stages: [pre-commit, manual]
- id: check-merge-conflict
stages: [pre-commit, manual]
- id: check-docstring-first
stages: [pre-commit, manual]
files: \.py$
- id: debug-statements
stages: [pre-commit, manual]
files: \.py$
- id: check-ast
stages: [pre-commit, manual]
files: \.py$
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.0
hooks:
- id: ruff
args: [--fix]
stages: [pre-commit, manual]
files: \.py$
- id: ruff-format
stages: [pre-commit, manual]
files: \.py$
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.18.2
hooks:
- id: mypy
stages: [pre-commit, manual]
files: \.py$
additional_dependencies: []
pyproject.toml
mypyとruffの設定を入れた適当なプロジェクトを作成し、下記のようなpyproject.tomlを配置します。
[project]
name = "prek-sandbox"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = []
[dependency-groups]
dev = [
"mypy>=1.18.2",
"ruff>=0.14.0",
]
[tool.ruff]
target-version = "py314"
line-length = 100
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ANN", # flake8-annotations
]
[tool.mypy]
python_version = "3.14"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true
ソースコードなど
mypyとruffが動くように適当にsrcディレクトリを作成し、Pythonファイルを作成します。
今回のお試してはコードの内容は関係ないので、全部生成AIに作成してもらいました。
$ tree src
src
├── api_client.py
├── data_processor.py
├── database.py
├── main.py
├── utils.py
└── validators.py
1 directory, 6 files
ベンチマーク結果
初期インストール(install-hooks)
Gitフックへの登録や環境セットアップを行うコマンドは、prek install-hooks
とpre-commit install-hooks
です。
下記を手動で3回実行して結果を確認します。
hyperfine \
--prepare 'prek clean && pre-commit clean && uv cache clean' \
--runs 1 \
'prek install-hooks' \
'pre-commit install-hooks'
実行回 | prek | pre-commit | 倍率 |
---|---|---|---|
1回目 | 3.449 s | 8.580 s | 2.49倍 |
2回目 | 2.603 s | 8.579 s | 3.30倍 |
3回目 | 3.577 s | 8.633 s | 2.41倍 |
初期セットアップ時も、prekが2.4〜3.3倍高速でした。
全フック実行(変更がなく全部スキップされる)
フックの実行はGitフックに登録してgit commitでも良いのですが、今回は測定なのでprek run
とpre-commit run
コマンドを使って実行します。
まずはGitのStaged Changesにファイルがない状態での実行です。
hyperfine \
--warmup 3 \
--runs 10 \
'prek run' \
'pre-commit run'
コマンド | 平均時間 | 範囲 |
---|---|---|
prek run |
91.1 ms ± 0.6 ms | 90.4 ms … 92.4 ms |
pre-commit run |
314.6 ms ± 4.4 ms | 310.9 ms … 326.6 ms |
結果: prekが3.45倍高速
全フック実行(チェック対象あり)
次に、ファイルがStaged Changesに含まれる状態にして測定してみます。
正しくチェックされていることが確認できるように、Pythonの型チェックでruffとmypyでエラーがでるような状態で実行しています。
hyperfine \
--warmup 3 \
--ignore-failure \
--runs 10 \
'prek run' \
'pre-commit run'
コマンド | 平均時間 | 範囲 |
---|---|---|
prek run |
547.3 ms ± 69.8 ms | 518.6 ms … 744.9 ms |
pre-commit run |
996.2 ms ± 68.6 ms | 967.4 ms … 1190.6 ms |
結果: prekが1.82倍高速
エラー処理や出力の生成を含む場合でも、大幅な高速化が確認できました。
実際にruffやmypyも動いているため、それらの実行時間のほうの割合が大きくなった結果だと考えられます。
特定フック実行(ruff)
続いて、ファイルがStaged Changesに含まれる状態で、ruffのフックだけが実行されるように指定して実行してみます。
hyperfine \
--warmup 3 \
--ignore-failure \
--runs 10 \
'prek run -a ruff' \
'pre-commit run -a ruff'
コマンド | 平均時間 | 範囲 |
---|---|---|
prek run -a ruff |
68.7 ms ± 5.1 ms | 60.1 ms … 75.1 ms |
pre-commit run -a ruff |
263.6 ms ± 1.2 ms | 261.8 ms … 266.3 ms |
結果: prekが3.84倍高速
特定フック実行(check-toml)
同じく、ファイルがStaged Changesに含まれる状態で、check-tomlのフックだけが実行されるように指定して実行してみます。
hyperfine \
--warmup 3 \
--runs 10 \
'prek run -a check-toml' \
'pre-commit run -a check-toml'
コマンド | 平均時間 | 範囲 |
---|---|---|
prek run -a check-toml |
54.4 ms ± 2.0 ms | 53.0 ms … 59.3 ms |
pre-commit run -a check-toml |
276.6 ms ± 1.0 ms | 275.4 ms … 277.9 ms |
結果: prekが5.08倍高速
軽量なフックほど、起動オーバーヘッドの差が顕著に現れています。
prekならではの機能
prekには、pre-commitにはない便利な機能が多数実装されています。
フック一覧表示
$ prek list
.:trailing-whitespace
.:end-of-file-fixer
.:check-yaml
.:check-toml
.:check-added-large-files
.:check-merge-conflict
.:check-docstring-first
.:debug-statements
.:check-ast
.:ruff
.:ruff-format
.:mypy
pre-commitにはlistコマンドがなく、設定されているフックの概要を把握しやすくなっています。
ワークスペースモード
こちらはまだ試せていませんが、prekはワークスペースモードをサポートしており、モノレポ内の複数のサブプロジェクトをそれぞれの.pre-commit-config.yamlファイルで管理できます。pre-commitではsub-pre-commitという別のツールを使うことでしかできなかったので、公式にサポートされるのはありがたいです。
# ワークスペース全体で実行
$ prek run
# 特定のプロジェクトのみ実行
$ prek run project1 project2
感想
良かった点
- 体感できる速度向上: 今回測定したシナリオでは1.82〜5.08倍の高速化が確認できました。特に軽量なフックほど高速化の恩恵が大きく、開発中の快適性が大幅に向上します
-
完全互換性: 既存の
.pre-commit-config.yaml
をそのまま使えるため、移行コストがゼロでした -
豊富な独自機能:
prek list
、--last-commit
、--directory
など、pre-commitにはない便利そうな機能が多数追加されています - ワークスペースモード: モノレポでの利用を想定した機能があり、大規模プロジェクトでの活用が期待できます
注意点
- まだアルファ版のため、本番利用には慎重な検証が必要です
- 一部の言語がなかったり、サブコマンドが未実装です(TODO参照)
結論
小規模ですが、身近なプロジェクトでも速いことが確認できました。
- 通常実行: 91.1 ms vs 314.6 ms(3.45倍高速)
- 軽量フック: 50〜70 ms vs 260〜280 ms(約5倍高速)
- 初期セットアップ: 2.6〜3.6秒 vs 8.6秒(約3倍高速)
開発中に何度も実行するpre-commitの時間が大幅に短縮されるためかなり嬉しい結果となりました。(チーム開発としてpre-commitは入れておくのが好きなんですけど、あまり体験としては好きではなかった。)
pre-commitからの移行も設定ファイルがそのまま使えて楽だったため、気軽に試せるのも良いところ。
期待して安定版のリリースを待ちたいと思います!
Discussion