コミットメッセージに「Update code」と書くのはもうやめよう
意味のあるコミットメッセージを書くのは、特に開発に集中していてすぐに変更をプッシュしたい時には面倒に感じられがちです。多くの開発者が(私自身もそうでしたが)、「fix bugs」や「wip」、あるいは定番の「update」といったメッセージに逃げてしまうのを見てきました。この習慣は、後でチームの他のメンバーがデバッグ履歴を追う際に悪夢を招きます。
私の実務経験上、クリーンで説明的、かつプロフェッショナルなGit履歴を維持することは、習得すべき不可欠なスキルの1つです。これによりコードレビューの時間を節約でき、開発ワークフローを劇的に効率化する際にも役立ちます。これを常に手動で頑張るのではなく解決するために、私はLLM(大規模言語モデル)とGit Hooksを使用したローカルのGitコミットアシスタントを構築しました。最大のメリットは?すべて自分のマシン上で動作するため、機密コードがローカル環境から外に出ることはありません。
クイックスタート(5分)
Ollamaとシンプルなシェルスクリプトを使えば、わずか数分で基本的なバージョンを動かすことができます。
1. Ollamaとモデルのインストール
公式サイトからOllamaをダウンロードし、MistralやLlama 3のような軽量モデルをプルします。コミットメッセージの生成には、ローカル環境でのAIモデル実行に最適な小さなモデルで十分すぎるほどです。
# モデルをインストール
ollama pull mistral
2. Git Hookの作成
プロジェクトのルートに移動し、hooksディレクトリ内にファイルを作成します。今回は、コミットメッセージのエディタが開く前に実行されるprepare-commit-msgフックを使用します。
# Hookファイルを作成
touch .git/hooks/prepare-commit-msg
chmod +x .git/hooks/prepare-commit-msg
3. シンプルな自動化スクリプトの追加
以下を.git/hooks/prepare-commit-msgに貼り付けてください:
#!/bin/bash
# ステージングされた変更を取得
DIFF=$(git diff --cached)
if [ -z "$DIFF" ]; then
exit 0
fi
# Ollamaにメッセージ生成を依頼
RESPONSE=$(curl -s -X POST http://localhost:11434/api/generate -d '{
"model": "mistral",
"prompt": "これらの変更に対して、短くプロフェッショナルなGitコミットメッセージを書いてください。Conventional Commits形式(feat:, fix:など)を使用してください。メッセージのテキストのみを返してください: '"$DIFF"'',
"stream": false
}' | jq -r '.response')
echo "$RESPONSE" > "$1"
これで、git commitを実行するたびに、アシスタントがステージングされた変更に基づいてメッセージを自動的に入力してくれます。
詳細解説:仕組みについて
このソリューションは、Gitの内部ライフサイクルとローカルAPI呼び出しの相乗効果に依存しています。Git Hooksは、Gitリポジトリで特定のイベントが発生するたびに自動的に実行されるスクリプトです。堅牢なアシスタントを構築するには、データフローを慎重に扱う必要があります。
Git Hookのパイプライン
LLMからの複数行の文字列やJSONレスポンスの処理はBashよりもPythonの方がはるかにクリーンに書けるため、ロジックにはPythonを使うのが好みです。以下は、prepare-commit-msgフックのより高度な構造です。
import subprocess
import requests
import sys
def get_git_diff():
# ステージングされた変更のみを取得
return subprocess.check_output(["git", "diff", "--cached"]).decode("utf-8")
def generate_message(diff):
url = "http://localhost:11434/api/generate"
prompt = f"""以下のGitの差分(diff)を分析し、Conventional Commits標準に従って簡潔なコミットメッセージを記述してください。
差分:
{diff}
レスポンス形式: <type>(<scope>): <description>"""
payload = {"model": "mistral", "prompt": prompt, "stream": False}
response = requests.post(url, json=payload)
return response.json()["response"].strip()
if __name__ == "__main__":
commit_msg_filepath = sys.argv[1]
diff = get_git_diff()
if diff:
message = generate_message(diff)
with open(commit_msg_filepath, "w") as f:
f.write(message)
なぜローカルLLMなのか?
最大の理由はプライバシーです。企業プロジェクトに取り組んでいる場合、コードの断片を外部API(OpenAIなど)に送信することはセキュリティポリシーに違反する可能性があります。OllamaやLocalAIを使用すれば、データはRAM内に留まります。さらに、オフラインでも動作するため、飛行機の中や接続の不安定なカフェで作業しているときでも問題ありません。
高度な活用法:品質チェックの追加
真のアシスタントはメッセージを書くだけでなく、質の低いコードがコミットされるのを防ぎます。ユニットテストの自動化と同様に、pre-commitフックを使用して、コミットが許可される前にLLMで品質チェックを行うことができます。
機密情報やデバッグログのチェック
私はよく、console.logやprint()、あるいはハードコードされたAPIキーをコード内に残していないか確認するチェックを追加します。LLMにこれらを特定して探すようプロンプトを出すことができます。
def check_code_quality(diff):
prompt = f"""この差分をスキャンして、ハードコードされたパスワードや消し忘れたデバッグ文(console.log、print)などのセキュリティリスクを確認してください。
もし見つかった場合は 'REJECT: <理由>' と出力してください。問題がなければ 'PASS' と出力してください。
差分:
{diff}"""
# ... LLMを呼び出すロジック ...
if "REJECT" in response:
print(f"コミットがブロックされました: {response}")
sys.exit(1) # これによりコミットプロセスが停止します
巨大な差分(Diff)への対応
LLMにはコンテキスト制限があります。一度に50個のファイルをステージングすると、差分が大きすぎてモデルが処理できなくなります。通常、私は文字数制限を設けたり、ファイルごとに差分を要約したりするように実装しています。レスポンス時間を速く保つための目安として、差分の最初の2000語程度のみをLLMに送信するのが良いでしょう。
日常で使うための実践的なヒント
- 適切なモデルを選ぶ: コミットメッセージには、
Mistral-7BやLlama-3-8Bが最適です。マシンのスペックが低い場合は、MicrosoftのPhi-3 Miniを試してみてください。サイズに対して驚くほど高速でスマートです。 - プロンプトを微調整する: モデルの出力が冗長すぎる場合は、「極めて簡潔に。最大10語以内で」と指示してください。
- 「編集」ワークフロー:
prepare-commit-msgフックを使っても、エディタ(VimやVS Codeなど)は開きます。LLMが下書きを提供し、保存する前に素早く微調整できるため、これが完璧な形です。AIを100%信頼してはいけません。 - グローバルフック: すべてのプロジェクトでこのアシスタントを使いたい場合は、
git config --global core.hooksPath /path/to/your/global/hooksを使用してグローバルなGit Hooksディレクトリを設定できます。
このセットアップには15分ほどかかりますが、毎日の開発でその価値を実感できるはずです。差分を意識的に確認するようになり、月曜日の朝の疲れ切った状態であっても、プロジェクトの履歴をシニアエンジニアが書いたようなクリーンな状態に保つことができます。

