@ryoppippi

「commit messageを考えてる間によしなに git hooks を実行しておいてね!」を実現するneovimの設定をかいた

16 Aug 2023 ・ 10 min read


Note

この​記事はVim 駅伝の​ 8/16 の​記事です。

TL;DR

  • Git の​ commit message を​考えている​間に​ pre-commit script を​実行する​neovimの​設定を​書いた
  • CLIから​ git commit -n で​ neovim を​立ち上げて​使う
  • 今は​いろいろ雑なので、​今後は​設定を​切り出して​プラグイン化/CLI Tool化したい

はじめに

ある​日、​いつものようにThe Engineers’ Paradiseで​喋っていると、​da-ja-re王ことkuuさんの​ gitcommit の​設定が​話題に​なりました。

https://github.com/kuuote/dotvim/blob/535d6d33e964a46c39076b306ae9f58d59f8e8b5/ftplugin/gitcommit.vim#L20-L37

この​設定は、​「gitの​commit messageを​打っている​間に​ .git/vim に​ある​スクリプトを​実行してくれる」​設定です。

「確かに​commit message を​考えている​間に​スクリプトを​実行できたら​嬉しいだろうな」 「これを​git の​ git hooks の​実行に​使えたら!」

と​いう​ことで、​この​アイデアを​実現する​方​法を​考えてみました。

そも​そも​ git hooks ってなに?

git hooks とは、​git の​コマンドを​実行する​前後に​実行される​スクリプトです。 hooks には​いく​つか​種類が​ありますが、​commit前後に​実行される​スクリプトに​限れば​2つあります。

  • pre-commit: commit前に​実行される​スクリプト。​例えば​formatが​かかっているか​どうかを​チェックしたり、​lintを​かけたりするのに​使われる
  • commit-msg: commit messageを​書いた​後に​実行される​スクリプト。​commit messageの​フォーマットが​正しいか​どうかを​チェックする

さて、​これらの​スクリプトは、​commit を​行うたびに​実行されます。 で、​スクリプトの​処理内容次第で​は​しばしば​長時間​待た​される​ことがあります(prettier とか​ eslint とか。​もちろん lint-staged 等と​組み合わせれば​短く​する​ことも​できるでしょうが​)。 じゃあスクリプトの​終了を​待っている​間に​同時に​commit messageを​編集できたら​嬉しい​よね、と​いうのが​本記事の​趣旨です。

git commit実行時のhookの実行順序

例えば、git commitを​実行した​時

pre-commitが実行される

editor (vim など) が立ち上がるので、commit message を記入する

commit-msgが実行される

commitが実行される

と​いう​流れに​なる。

また、​ git commit -m 'hoge' と​いうように​ -m オプションを​つけて​commit messageを​指定した​場合は、

commit-msgが実行される

pre-commitが実行される

commitが実行される

と​いう​流れに​なる。

いずれの​場合も、​commit messageを​書いている​間に​pre-commitが​実行される​ことはない。

git hooks の設定について

git hooks を​設定する​ための​パッケージと​しては​ husky や​ simple-git-hooks が​有名です。 しかし、​実は​Git 2.9 以降では​Git のcore.hooksPath オプションを​利用すると​追加依存なしで​ Git Hooks を​利用する​ことができます。

例えば

mkdir -p .githooks
touch .githooks/pre-commit
chmod +x .githooks/pre-commit
git config --local core.hooksPath .githooks

を​実行すると、.githooks/pre-commit が​commit前に​実行されるようになります。

詳しくは​以下の​記事を​ご覧ください。 https://efcl.info/2021/08/21/git-precommit-hook/

本記事で​用いている​git hooks はcore.hooksPath で​指定された​ものを​使っています。

どうやって​実現する?

最初は、​CLI Toolを​書こうと​していました。 なぜなら、​自分は​CLIからgit commit -m 'hoge' で​git commit する​人間だからです。 しかし、​CLI Toolを​書くとなると、​いろいろと​面倒なことがあります。 コンパイルしたり、​ライブラリを​選定したり、​面倒くさいです。 特に​今回は​スクリプトを​実行して​結果を​得る​必要が​あるので、​画面を​分割したり非同期処理を​したりと​考える​ことがまあまああります。

そこで、​今回は​Neovimの​設定と​して​書いてしまうことにしました。 Neovimの​設定なら、​設定ファイルに​書けば​すぐ​実行できます。 また、​標準の​APIや​関数を​呼び出す​ことで、​画面分割も​非同期処理も​簡単に​できます。 いいね 👍

お披露目

と​いうわけで、​Neovimの​設定を​書きました。 以下の​動画で​実際に​動いている​様子を​見る​ことができます!!

https://youtu.be/VetyhcZ_rlI

以下に​設定を​載せて​おきます! Neovim​使いの​方は​これを​ after/ftplugin/gitcommit.lua に​記述してください。

https://github.com/ryoppippi/dotfiles/blob/fba410346e51e128a590d17d22bb1d439110c5c3/nvim/after/ftplugin/gitcommit.lua

簡単に​何を​しているか​解説すると

  • git commit -n で​ Neovim を​立ち上げる。​こうする​ことで、​git コマンドは​ hooks を​実行しない
  • もし pre-commit hook が​存在している​ならば​画面を​分割して​ターミナルを​開き、​スクリプトを​実行する
  • もし commit-msg hook が​存在している​ならば、​ COMMIT_EDITMSG ファイルが​保存される​たびに​commit-msg を​実行する
  • も​し どちらかの​ hook が​失敗しているのにも​関わらず Neovim を​終了しようとした​場合、​ :cquit コマンドで​ neovim を​異常終了と​する​ことで​ commit される​ことを​防ぐ
  • もしどちらの​ hook も​パスしていた​場合、:q コマンドで​ Neovim を​終了する​ことで​ commit される

と​いう​流れに​なっています。 これで、​commit message を​書いている​間に​スクリプトを​実行する​ことができました!

Future Works

自分の​ユースケースと​しては​この​設定で​うまく​いくのですが、​これを​いい​感じに​まとめて​行きたいなと​思っています。

具体的には、

  • gin.vim など、​vim 内で​commit を​行う​場合にも​使い物に​なるように​したい​ - 現状 hooks が​pass していない​状態で​buffer を​閉じると​ :cquit で​neovim が​終了されてしまう。​autocmd 等で​うまく​設定すれば​いいのかもしれない。
  • plugin と​して​公開したい​ - そも​そも​設定の​中に​ :cquit が​入っている​時点で​現状では​万人に​お勧めできる​ものではなさそう
  • vim でも​動くように​したい​ - luaで​書いているので​ vim では​動きません。​ Denops で​書き直したい
  • CLI Tool と​して​公開したい​ - vim/neovim に​限らずに​これを​やりたい​人は​いそう。​エディタに​囚われない​CLI Tool と​して​形に​したい​(時間の​関係で​間に​合わなかった​ & go/rust の​勉強が​おっついていない)

と​いうわけで、​現状色々と​課題は​ありますが、​ひと​まず​動く​ものが​できたのは​満足です。

終わりに

  • kuu さん​ありがとう​ございました。
  • 楽園 で​お会いしましょう。

おまけ

git config --global commit.verbose true

を​設定しておくと、COMMIT_EDITMSG ファイルに​ git status と​ git diff が​表示されるようになります。

git config --global commit.verbose false git config --global commit.verbose false の​場合 git config --global commit.verbose true git config --global commit.verbose true の​場合

この​設定に​より​1つプラグインを​減らすことができ、​Neovim の​起動が​また​少し​早くなりました。

(@atusy さんに​教えて​もらいました。​ありがとう​ございました)

comment on bluesky / twitter
CC BY-NC-SA 4.0 2022-PRESENT © ryoppippi