rcmdnk's blog
Last update

git commit murder

ChatGPTにGit commitのmessageを書かせる。

Gitのcommit messageを書くには

必要な情報としては変更点に関する記述なのでgit diffの結果を見て何が変わったかを書けば良い、 という比較的形式的にはわかりやすいものです。

コマンドラインからChatGPTを簡単に使えるツールを作ったのでそこにgit diffの結果を与えて いい感じのcommit messageを考えてもらえば良さそう。

git-gpt-commit

gitのサブコマンドとしてgpt-commitというコマンドを追加するツールです。

コミットメッセージをあらかじめ用意する以外は基本的にgit commitと同じように使えて、 -aとかオプションもそのまま使えます。

中身は簡単なシェルスクリプトです。

準備

インストール

インストールはHomebrewを使っていれば

1
$ brew install rcmdnk/rcmdnkpac/git-gpt-commit

これで chatgpt-prompt-wrapper も依存先として一緒にインストールされます。

直接入れる場合はbinディレクトリにあるgit-gpt-commitを PATH下に置けばgit gpt-comitが使えるようになります。

ただし、直接入れる場合は chatgpt-prompt-wrapper も手動でいれる必要があります。

その他の準備

chatgpt-prompt-wrapper を使うにあたってOpenAIのAPI Keyが必要になります。

OpenAIでアカウントを作って ここ からAPI Keyを取得して 環境変数として

1
export OPENAI_API_KEY="sk-..."

のように設定しておきます。

使い方

何かしら変更を行ったレポジトリの中で、変更したファイルをgit addして

1
$ git gpt-commit

とすると、ChatGPTがgit diff を見て考えたcommit messageがあらかじめ書かれた状態で commit messageの編集画面に入ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
feat: Added ability to exclude files from git diff
docs: Updated README with information about new feature and defaults
style: Updated formatting of default config values in README
refactor: Renamed PROMPT default value to _PROMPT
test: Added missing tests for new file exclusion feature
fix: Fixed issue with canceling commits when using '-a' option

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
# Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
#   (use "git pull" to update your local branch)
#
# Changes to be committed:
#       modified:   README.md
#       modified:   bin/git-gpt-commit
#

`

こんな感じのメッセージが準備された状態になります。

あとはこれを適当に編集して保存すればcommit出来ます。

オプション設定

commitメッセージを作るための命令

デフォルトでは以下のようなプロンプトを使っています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Please make git commit messages for the following diff output.

Each commit message must be one line starting with one of the following words.

* feat: (new feature for the user, not a new feature for build script)
* fix: (bug fix for the user, not a fix to a build script)
* docs: (changes to the documentation)
* style: (formatting, missing semi colons, etc; no production code change)
* refactor: (refactoring production code, eg. renaming a variable)
* test: (adding missing tests, refactoring tests; no production code change)
* chore: (updating grunt tasks etc; no production code change)

### diff

$(git diff --cached $exclude_list)

命令部分

まず、命令部分ですが、ここは好きに設定すべきところではありますが、 ここではSemantic Commit Messages を使って書き方を指示しています。

こういった形の形式張った書き方はアルゴリズム相手には相性が良いのでいい感じにまとめてくれます。

この部分は設定で変えることが出来て、PROMPTという環境変数に指示を書いておくと 上の### diffより上の部分を置き換えることができます。

または~/.config/git_gpt_commit/config(もしXDG_CONFIG_HOMEが指定されていたらそれの下のgit_gpt_commit/config) に設定を書いておけばそれが反映されます。

日本語で、

1
PROMPT="以下のgit diffの出力を見てコミットメッセージを簡潔かつ分かりやすく書いてください。"

とかでもOK。

diffの出力

最後の部分でgit diffを渡しています。 --cachedaddしたものとHEADとの比較をします。

もし-aが渡された場合には先にgit add -uを実行して変更があったファイルをaddするようにしているので、 これで必ず必要な部分のdiffが取れるようになっています。

また、$exclude_listが渡されていますが、これはデフォルトでは

1
-- :(exclude)$(git rev-parse --show-toplevel)*.lock

になっています。 poetry.lockとかGemfile.lockとか、パッケージを管理するlockファイルは 1つパッケージを変更しただけでも依存関係で大きく内容が変わってしまいますが その変更を逐次確認してcommit messageに入れることは通常無いと思うので省いています。

特にgpt-3.5-turboだとlockファイルの変更だけでもtoken数の制限にかかっちゃたりもするので。

もし排除するファイルを変更したい倍には EXCLUDEという環境変数に設定します。もしくは configに書きます。

上のデフォルトは

1
EXCLUDE="*.lock"

となっています。 :(exclude)$(git rev-parse --show-toplevel)は勝手に追加されます。

複数指定したい場合は

1
EXCLUDE="*.lock,README.md"

のような感じでコンマ区切りで。

1
-- :(exclude)$(git rev-parse --show-toplevel) *.lock:(exclude)$(git rev-parse --show-toplevel)README.md

のように展開されます。

テンプレートメッセージ

上の例だと入ってませんが、.gitconfigなどでcommit.templateを指定して commit message編集時に表示させるようにしている場合これも準備されたcommit messagesの下に表示されるようになっています。

別途git gpt-commitのときは別のテンプレートを使いたい場合は MESSAGEという環境変数に設定、もしくは configに書きます。

MESSAGEを設定しない場合にcommit.templateが設定されているとそれを使いますが、 MESSAGE=""のように空にすればテンプレートはひょうじされなくなります。

ChatGPTモデル

chatgpt-prompt-wrapper では現在デフォルトでgpt-3.5-turboを使うようになっていますが、 git-gpt-commitでは MODELという環境変数にgpt-4とか指定すればモデルを変更できます。

-c/–changeオプション

追記: 2023/04/19

-c/--changeというオプションを追加しました。 これに続いて今回のcommitの概要てきなものを教えてあげる事ができます。

Example

例えばこの変更を入れたときに使ったものが上の例ですが、 何も指定せずgit gpt-commitだと

1
refactor: Add option to include change descriptions in commit messages

というメッセージになってchangeオプションという部分は入ってますが、まず、最初がrefactorになっているし 文章もちょっと意味が分かりません。

これに対して、

1
$ git gpt-commit -c "add -c/--change options"

とすると

1
feat: Add -c/--change option to specify contents of changes in commit message

となり、これはそのまま使えるレベルです。

日本語で、

1
$ git c -a -c "-c/--changeというオプションを加え、あらかじめ変更を伝えることができるようにする"

と入れても同じドラフトが出来ました。

どうやってるかというと、-c/--changeのオプションがあるとその内容を PROMPTの前に

1
This commit is for: ...

という形で加えるようにしてあるだけです。

あまり詳細に書けるならそのままメッセージを書けば良いじゃん、ということで恩恵が浅いですが、 上のadd ...程度を書いてちゃんとした文章に直してくれるのであれば 結構使う意味はあるんじゃないかな、と。

追記ここまで

その他

コミットのキャンセル

commit messageをエディタで開いた状態からやっぱりやめたい場合には #でコメントアウトしてある部分以外を全て削除して終了すればキャンセルになります。 (--allow-empty-messageを設定していない限り。)

この際、git gpt-commit -aのようにstageされてないものもaddするようになっていても、 キャンセル時にはaddしたものだけgit restore --stagedで戻すようになっています。

pre-commit

pre-commitをインストールしている場合にはChatGPTに送る前にpre-commit単独で走らせてチェックして 失敗した場合にはその場でcommitを辞めるようにしています。

alias

git gpt-commitはちょっと長いので、個人的には~/.gitconfig

1
2
[alias]
  c = gpt-commit

としてgit cコマンドとして使ってます。実際よく使うのはgit c -a

gcにしたかったけどgit-gcは最初からあるので、1文字コマンドは微妙かと思いつつ他に良いのもないのでcで。

aicommits

gpt-3.5のAPIが使えるようになってわりかしすぐに出来ていた同じようなcommit message作成ツール。

この中では

1
Write a git commit message in present tense for the following diff without prefacing it with anything. Do not be needlessly verbose and make sure the answer is concise and to the point. The response must be in the language ${locale}:\n${diff}`;

みたいなプロンプトが使われています。 オプションで変えられる言語のためのlocaleという変数とgit diffの結果のdiffという変数が入っていますが、 簡潔にまとめよ、的な感じの命令です。

このあたりの命令部分を自分で決めたかったのと、 APIを使う部分を外に出してgit commit-mでメッセージのドラフトとして入れてすぐに編集出来る状態にした方が やりやすいかな、ということでgit-gpt-commitを作ってみました。

多分ほかにも色々とあるとは思いますが、 プロンプト部分を簡単に変えられるのと、 API部分はchatgpt-prompt-wrapperにまかせてgit-gpt-commit自体はシンプルなので 好きなように変更して使ったりするにも良いんじゃないかな、と。

感想

ちょっとした変更だと編集なしでもOKなのが多いですが、 多少複雑だと手直ししたくなるのも多いので、 まだ完全自動というよりドラフトを書いてもらって すぐに手直しできる状態にするやり方が一番しっくりきています。

とりあえずこれ使ってcommitするようにしているので、意味のないfixとかupdateとかだけの commitもだいぶ減らせて良い感じです。

あとはやっぱり自由に書かせるよりは Semantic Commit Messagesのようなある程度ちゃんとした決まりがあった方が 出来上がったものをそのまま使える状態になる確率が高いかな、という感じ。

Sponsored Links
Sponsored Links

« chatgpt-prompt-wrapper: コマンドラインから気軽にChatGPTを使う ChatGPT同士で議論させる »

}