• 作成:

Gitのコミットメッセージでめっちゃタイプミスをするのでそろそろタイプミスを検出するhooksを書きました

動機

私のGitコミットメッセージは以下のプレフィクスから始まります.

  • added: 機能や関数などを追加した時
  • changed: ユーザから見てわかるような変更をした時(例: ボタンの色を変える)
  • cleaned: コードフォーマッタなど本質的でない変更をかけた時
  • deleted: 機能や関数などを削除した時
  • fixed: バグを修正した時
  • modified: ユーザから見てわからない変更をした時(例: 内部アルゴリズムの変更)
  • renamed: ファイルや識別名の変更
  • updated: ライブラリなどのアップデート

始まるのですが… これをタイプミスしまくります. 特にchangedとか, 間違えて原型でタイプしたりとか.

検出して止めるhooksを書こうと常に思いつつ後回しにしてきたのですが, 今日またタイプミスをしたので生産性に影響があると思い, 検出するhooksを書きました.

global hooksを設定する方法は以前書きました.

プログラム

まずコミットメッセージをチェックするhooksはcommit-msgに書きます.

#!/usr/bin/env zsh
set -eu
`dirname $0`/commit-message-in-style.p6 $@

ところで未だにシェルスクリプトでファイル分割するにはdirname $0というすごいダサい方法を使わないといけないんですかね? 流石に何か別に方法がある気がするのですが見つからない.

それで実際にメッセージをチェックするプログラムである, commit-message-in-style.p6に引数を渡すようにしました.

Raku(Perl6)で書きました. 他のhooksはみなzshなのですが, 文字列操作や配列操作が絡むことが予想できたので, シェルで書く自信は私にはありませんでした.

これで一々コミットメッセージを確認しなくて済むので生産性が向上することでしょう.

書いてて詰まったこと

絶対パスじゃない

何故かGitは絶対パスではなくプロジェクトルートからの相対パスを渡してくるので, プロジェクトルートの検出が必要でした.

git rev-parse --show-toplevelでプロジェクトルートを取り出せるので, 基本的にはこれと結合すればOKなのですが, コミットする対象がサブモジュールだと今度は絶対パスを投げてくるので, 場合分けが必要でした.

これで全てのパターンに対応出来ている自信がないです.

someメソッドが見つからなかった

RakuにおけるJavaScriptのArraysomeメソッドに値するものをめっちゃ探したのですが見つかりませんでした. 仕方ないのでもう諦めてforで書きました. 本当にないのだろうか… この場合だと合致したら即抜けるから問題ないですけど, 他の条件もつけ足す場合はforの外にフラグが必要ですね… と思ったらRakuだとLAST(大文字)というループの最後のときだけ起動するブロックを作れるのでその点心配は無さそうですね. Pythonだとfor elseがあるようですが.

いやそもそもsomeメソッドが見つからないのがおかしい, 探し方が下手なだけでおそらくあるはず.

動的型付けやはりつらい

たったの26行のコードでも動的型受けつらいと思えてきてしまいました. 型の不一致やタイプミスを編集中に指摘してくれないのはつらい.

まあRakuは漸進的型付けなので実行時エラーが起きた時も何処の型が一致していないのか明確に述べてくれて, 「この引数ではPathが欲しいのにStrが来て不一致です」のような明確なエラーを出してくれます.

JavaScriptやPythonのように「どこが原因かわからないけどundefinedです」のようなすっとぼけたエラーでないことは嬉しい限りですね.

ただ動的なメリットが一切ないプログラムだったので, これならrunhaskellでHaskellで書くか, rdmdでD言語で書いた方が楽だったかもしれませんね…

もっと漸進的型付けが進化して編集中にエラーを出してくれれば快適に書けるのかもしれませんが, Rakuが実行時にメソッドを追加できたりする言語である以上それは厳しいのかもしれません.