結論から書くと、pre-commit hookを pre-commit に切り替えたメモ
保存時更新
このメモに表示されているtoken数は、frontmatterに記録されていて、以前は watchdog を使用して保存時に更新していた
from watchdog.events import FileSystemEvent, FileSystemEventHandler
from watchdog.observers import Observer
class MarkdownHandler(FileSystemEventHandler):
def on_modified(self, event: FileSystemEvent) -> None:
modified_path = Path(str(event.src_path))
if modified_path.suffix != ".md":
return
if modified_path.name.endswith(".tmp.md"):
return
update(modified_path) # insert token to the target markdown
if __name__ == "__main__":
target = Path("<target_path>")
observer = Observer()
observer.schedule(MarkdownHandler(), str(target), recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
保存時更新だと、ローカルで動かしているHugoが、ファイル保存時と、token数更新時それぞれに反応する。つまり Ctrl+S を押す度にいちいち2回Hugoがリロードされる。大した負荷ではないが気になったのと、リアルタイムでtoken数見る必要もないので、pre commit時の更新に切り替えることにした。
pre-commit hook
.git/hooks にサンプルがある。ここの pre-commit.sample を直接編集しても有効になるが、それだとGitで管理できないので、 .githook/pre-commit というファイルにスクリプトを書く。細かいエラーハンドリングやログ出力は省略している。
staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep '^content/posts/.*\.md$' || true)
if [ -n "$staged_files" ]; then
for file in $staged_files; do
if [ -f "$file" ]; then
# Run token count and capture exit code
if python token_count.py "$file"; then
# Check if the file was modified by token_count.py
if ! git diff --quiet "$file"; then
git add "$file"
fi
else
exit 1
fi
else
exit 1
fi
done
fi
git config で登録すると有効になる。
$ git config core.hooksPath .githook
pre-commit install
最近は pre-commit が流行っている雰囲気。pre-commit に入れたいlintなどのツールは、シングルバイナリだったりpythonやjsスクリプトと多様な中で、それらを統合的に扱えるようにするのがこのpre-commitツールのモチベーション。
今回の場合は、submodule下で token_count.py が uv でインストールされていたので、以下のような entry を書いて起動する。
repos:
- repo: local
hooks:
- id: token-count
name: Token count updater
entry: uv run --project scripts/token-count python scripts/token-count/token_count.py
language: system
pass_filenames: true
files: '^content/posts/.*\.md$'
pre-commit スクリプトでGitのステージにあるファイルを渡す、みたいな処理が不要となり、YAMLで完結する。あとは pre-commit コマンドでインストール。
$ uv add pre-commit
$ uv run pre-commit install --hook-type pre-commit
実態としては .git/hooks/pre-commit が生成される。
1つだけだとをこのpre-commitで管理するうまみはそこまで高くないが、他のlinterやformatterを複数導入するとき、それらをYAMLで管理できるのは便利。
実際にコミットした時のログ
$ git commit -m "add new post, 1123"
Token count updater......................................................Passed
[main a13abcb] add new post, 1123
1 file changed, 128 insertions(+)
create mode 100644 content/posts/2025/23_pre_commit.md