---
title: "「commit messageを考えてる間によしなに git hooks を実行しておいてね!」を実現するneovimの設定をかいた"
date: '2023-08-16'
isPublished: true
lang: 'ja'
---
> [!NOTE]
> この記事は[Vim 駅伝](https://vim-jp.org/ekiden/)の 8/16 の記事です。
# TL;DR
- Git の commit message を考えている間に pre-commit script を実行するneovimの設定を書いた
- CLIから `git commit -n` で neovim を立ち上げて使う
- 今はいろいろ雑なので、今後は設定を切り出してプラグイン化/CLI Tool化したい
# はじめに
ある日、いつものように[The Engineers' Paradise](https://vim-jp.org/docs/chat.html)で喋っていると、da-ja-re王こと[kuuさん](https://github.com/kuuote)の `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](https://github.com/okonet/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](https://github.com/typicode/husky) や [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) が有名です。
しかし、実はGit 2.9 以降ではGit の`core.hooksPath` オプションを利用すると追加依存なしで Git Hooks を利用することができます。
例えば
```sh
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](https://github.com/lambdalisue/gin.vim) など、vim 内でcommit を行う場合にも使い物になるようにしたい - 現状 hooks がpass していない状態でbuffer を閉じると `:cquit` でneovim が終了されてしまう。autocmd 等でうまく設定すればいいのかもしれない。
- plugin として公開したい - そもそも設定の中に `:cquit` が入っている時点で現状では万人にお勧めできるものではなさそう
- vim でも動くようにしたい - luaで書いているので vim では動きません。 [Denops](https://zenn.dev/search?q=denops) で書き直したい
- CLI Tool として公開したい - vim/neovim に限らずにこれをやりたい人はいそう。エディタに囚われないCLI Tool として形にしたい(時間の関係で間に合わなかった & go/rust の勉強がおっついていない)
というわけで、現状色々と課題はありますが、ひとまず動くものができたのは満足です。
# 終わりに
- kuu さんありがとうございました。
- [楽園](https://vim-jp.org/docs/chat.html) でお会いしましょう。
# おまけ
```
git config --global commit.verbose true
```
を設定しておくと、`COMMIT_EDITMSG` ファイルに git status と git diff が表示されるようになります。

_`git config --global commit.verbose false` の場合_

_`git config --global commit.verbose true` の場合_
この設定により1つプラグインを減らすことができ、Neovim の起動がまた少し早くなりました。
([@atusy](https://blog.atusy.net/) さんに教えてもらいました。ありがとうございました)