gitのコミットメッセージをローカルLLMに書かせる

gitのコミットメッセージをローカルLLMに書かせる

コミットメッセージ、ちゃんと書いてるだろうか。僕は正直、面倒になると「fix」「update」の一行で済ませてた。あとから履歴を見返して「このfixって何を直したんだっけ…」となるのがお決まりのパターンだった。

じゃあAIに書かせればいい、となるんだけど、差分をクラウドに渡すのは少し引っかかる。ローカルLLMなら差分は自分のPCから一歩も出ないし、git diff をパイプで渡すだけで書ける。

目次 / INDEX

なぜローカルLLMにコミットメッセージを書かせるのか

結論から言うと「git diffを外に出さずに済むから」。これに尽きる。

git diffは変更したコードそのものだ。APIキーの扱いを変えた行とか、まだ公開してないロジックとか、そういうのが丸ごと入ってる。

クラウドAIに投げれば当然それが送信される。規約上は問題なくても、気持ちの問題として引っかかる人は引っかかると思う。

それと、コミットメッセージ生成というタスクの性質もローカル向きだ。

  • やることは「短い差分を短く要約する」だけの小さいタスク
  • 長い推論も広い知識も要らないから、小型モデルで十分回るはず
  • 1日に何回も呼ぶものだから、API課金がないのも地味に効く

最短の一行はgit diffをパイプで渡すだけ

まずステージ済みの変更を出す。これは普通のgitコマンド。

git add .
git diff --staged

--staged は --cached と書いても同じ意味だ。で、この出力をそのままパイプでOllamaに渡す。

git diff --staged | ollama run <モデル名> "次のgit diffを読んで、簡潔なコミットメッセージを日本語で書いて。1行目は50字以内の要約、本文は何をなぜ変えたか。"

これだけ。パイプで渡した標準入力は、プロンプトより前に置かれてモデルに渡る。つまりモデルからは「差分 → 指示」の順に見えている。

ポイントは --staged を付けること。git addした変更、つまり「これからコミットする内容」だけが対象になる。

付けないと、まだgit addしていない変更の差分が出てきて、コミットする内容とズレる。ここを雑にすると生成されるメッセージもズレる。

Ollama自体をまだ入れてない人は、セットアップはMac版Ollama最短入門にまとめた。

プロンプトの型(Conventional Commitsで揃える)

毎回フリースタイルで書かせると出力がブレる。そこで「Conventional Commits」という定番フォーマットをプロンプトに指定する。1行目の先頭にprefixを付けて、変更の種類を機械的に示す書式で、広く使われている慣習だ。

prefix意味
feat機能追加
fixバグ修正
docsドキュメントの変更
refactor挙動を変えないコード整理
testテストの追加・修正
chore雑務・設定まわり

これをプロンプトに書式ごと埋め込む。僕が使ってる型はこんな感じ。

次のgit diffからコミットメッセージを書いて。
書式:
1行目: <種類>: <要約(50字以内)>
  種類は feat / fix / docs / refactor / test / chore から選ぶ
2行目: 空行
3行目以降: 何をなぜ変えたか(3行以内)
差分に書かれていないことは推測で書かない。

最後の一文が地味に大事で、これを入れないとモデルが差分にない「それっぽい理由」を作文してくることがある。

あと大前提として、生成物は必ず自分で読んで直すこと。AIには差分の「何を」は読めても「なぜ」までは分からない。なぜ変えたかは人間が補う部分だ。

毎回打つのは面倒なのでシェル関数にする

あの1行、毎回打つのは無理だ。続かない。なので、シェル関数にして一発で呼べるようにする。

.zshrc か .bashrc にこう書く(シェルによって書き方が少し違うはず。これはzsh/bash想定)。

gcm() {
  git diff --staged | ollama run <モデル名> "次のgit diffからコミットメッセージを書いて。1行目: <種類>: <要約(50字以内)>、空行、本文3行以内。種類は feat/fix/docs/refactor/test/chore から選ぶ。差分にないことは書かない。"
}

これで git add したあと gcm と打つだけでメッセージ案が出てくる。出てきた案をコピーして git commit に貼るか、気に入らなければもう一回 gcm を叩けばいい。

git hookで半自動にする(prepare-commit-msg)

もう一歩進める。git commit した瞬間に、エディタに下書きが入ってる状態を作れる。使うのは .git/hooks/prepare-commit-msg というgit hookだ。

このフックはコミット時に自動で走るスクリプトで、第1引数にコミットメッセージのファイルパスが渡ってくる。そのファイルにローカルLLMの生成文を書き込んでおくと、エディタが開いた時点で下書きが入っている、という流れだ(細部は環境で違うはずなので、実際に試して確認してほしい)。

雰囲気だけ短い例で。

#!/bin/sh
# .git/hooks/prepare-commit-msg
MSG_FILE="$1"
git diff --staged | ollama run <モデル名> "このdiffのコミットメッセージを書いて" > "$MSG_FILE"

hookファイルには実行権限が要るので chmod +x .git/hooks/prepare-commit-msg を忘れずに。

ここで一つ強く言っておきたい。これはあくまで「下書き」だ。自動で確定させる仕組みにはしないこと。

生成が差分と食い違うことは普通にあるので、最後は必ず人間が読んで、直して、それからコミットする。履歴はプロジェクトの資産だから、そこの最終責任までAIに渡さない方がいいと思う。

モデル選びと、ハマりどころ

モデルは小型のコード寄りモデルで十分なはず。むしろ大きいモデルを選ぶと1回の生成が遅くて、結局使わなくなる。

このタスクは「速くてそこそこ」が正解だ。用途別にどのモデルを選ぶかはローカルLLMの実用的な使い道と必要メモリの目安に詳しく書いた。

ハマりどころを先に3つ。

  • 差分が巨大だとモデルの文脈に収まらず、要約がぼやける。git diff --staged --stat でファイル一覧だけ渡す手もあるけど、そもそもコミットを小さく分けるのが本筋だと思う
  • 生成メッセージが差分と食い違うことがある(いわゆる幻覚)。だから必ず読む。ここはサボれない
  • 日本語で書かせるか英語かは、チームやリポジトリの慣習に合わせる。プロンプトの1語を変えるだけなので切り替えは楽

あと、そもそも差分に余計なファイル(ビルド成果物とか)が混ざってると要約の質が落ちる。リポジトリ側の整理、つまり.gitignoreも効いてくる。

まとめ

git diffをパイプでローカルLLMに渡すだけ。関数にすれば一発、hookを書けば半自動になる。そして差分は最初から最後まで自分のPCの中だ。

まずは git add して、git diff --staged | ollama run <手元のモデル> を1回試してみてほしい。雑な「fix」から抜け出す最初の一歩としては、これで十分だと思う。

よくある質問

git diffをローカルLLMに渡してコミットメッセージを書かせるには?

git addしたあと git diff --staged | ollama run モデル名 とプロンプトを付けて実行するだけ。--stagedを付けると、これからコミットする内容だけが対象になる。

コミットメッセージの生成にクラウドAIではなくローカルLLMを使う理由は?

git diffは自分が書いたコードの変更そのものなので、クラウドに投げれば外部サーバーに送信される。ローカルLLMなら差分がPCから出ず、短い差分の要約という小さいタスクなので小型モデルで足りる。

生成されたコミットメッセージはそのまま使っていい?

下書きとして扱い、必ず自分で読んで直す。差分にない理由を作文したり、内容と食い違ったりすることがあるので、最終確認は人間がやる。

次に読むなら

READMEの書き方|最低限の4つとスクショ・GIFの貼り方コミットを整えたら、次はREADMEで中身を伝える読む →