見出し画像

ChatGPTを使ってGitのコミットメッセージを自動生成する

コミットメッセージはちゃんと書くに越したことはないが、定型文になりがち。なら、たたき台をChatGPTに生成してもらえばいいのではないか、ということで早速作ってみた。

ここで使用するのはChatGPTの非公式CLIツール。

インストールし、OPENAI_API_KEYを設定するとコマンドラインからChatGPTを利用できる。

% chatgpt "Hello, how is it going?"
Hello! I'm an AI assistant, so I don't experience emotions, but I'm here to help you with any questions or tasks you might have. How can I assist you today?

Gitのprepare-commit-msg hookからこれを呼び出せばよいという寸法だ。

#!/usr/bin/env bash

# 元のコミットメッセージを取得する
original=$(grep "^#" "$1")

# diffからChatGPTでコミットメッセージを生成する
chatgpt '' > $1 <<EOF
Generate a descriptive commit message explaining the following diff, following the Conventional Commits format.
$(git diff --name-status -- "$pattern")

$(git diff --cached | head -n100)
EOF

# 元のコミットメッセージを追記する
echo "$original" >> $1
# /path/to/git/hooksの中にprepare-commit-msgスクリプトを置いておく

git config --global core.hooksPath /path/to/git/hooks

すると、こんな感じで、diffの内容を参照してコミットメッセージを生成してくれる。

feat(git-hooks): add prepare-commit-msg hook

This commit adds a new prepare-commit-msg hook script to the project. The script is respons
ible for generating a descriptive commit message from the provided diff, while adhering to
the Conventional Commits format. The script retrieves the original commit message, generate
s a new message using ChatGPT, and appends the original message to the generated one.

このスクリプトを追加したときのコミットメッセージ

しかし、これではrebaseやamendのときの挙動がおかしくなってしまうため、ブランチ上にいない場合や、既存のコミットメッセージがある場合はスキップする。

# Abort if it is not on a branch
if [[ $(git rev-parse --abbrev-ref HEAD) == "HEAD" ]]; then
  exit 0
fi

# Abort if a preivious commit message already exists
if (( $(grep -v "^#" "$1" | wc -l) > 1 )); then
  exit 0
fi

feat(commit message): generate descriptive commit message from diff

This commit updates the prepare-commit-msg hook script to generate a descriptive commit mes
sage from the diff. It skips execution when on a non-branch state (e.g., during a rebase) a
nd when the commit message already exists (e.g., during an amend).

この変更に対するコミットメッセージ

さらに、GitHub上でdiffを折りたたむように.gitattributesで設定している項目については除外したいので、AWKを使って少しこねくり回した。


pattern=.
if [ -e .gitattributes ]; then
  pattern=$(awk 'NF==0 {next} /^#.*/ {next} /linguist-generated=true/ {print ":^" $1}' < .gitattributes)
fi

chatgpt 'Generate a terse but descriptive commit message from the following diff' \
  "$(git diff --cached -- "$pattern" | head -n100)" > $1

feat: Exclude generated files from diff

Excludes generated files from the diff in `prepare-commit-msg` script. The pattern for excluding files is determined based on the presence of a `.gitattributes` file. If the file exists, it uses an awk command to extract the pattern for exclusion from the file.

この変更に対するコミットメッセージ

すべてまとめたものがこちら。

https://github.com/fumieval/dotfiles/blob/master/git-hooks/prepare-commit-msg

まとめ

ChatGPTのコマンドラインインターフェイスとgit hooksを組み合わせ、コミットメッセージの生成を容易に実現することができた。さらに、ChatGPTがConventional Commitsの知識を持っているため、そのフォーマットに対応させることができた。

この記事が気に入ったらサポートをしてみませんか?