Ruby
Git
overcommit

Git Hooks管理を簡単に - overcommitを使おう

More than 1 year has passed since last update.

overcommitとは?

git hooksの管理と、その拡張を簡単にできるRuby製ツールです。同様のツールにpre-commit by YelpやRailsに特化したpre-commitがあります。overcommitはこれらのツールと違い、pre-commit以外のフックにも対応しています。

リポジトリはこちら

https://github.com/brigade/overcommit

overcommitの導入でできること

  • コミットのタイミングでrubocopやeslintなどlintツールをかけられる
    • コードの読みやすさが向上することを期待できます
  • checkoutやpullのタイミングでbundle installnpm installを実行できる
    • 他の開発者がGemfilepackage.jsonに追加したらしくなんか動かない問題を回避できる
  • pushする前に変更したファイルのみ対象にrspecを実行できる
    • CIする前に自分が行った変更の結果を再確認できるので、少なくとも自分が書いたコードに問題がないか確認できる
  • コミットコメントのフォーマットをチェックできる
    • 1行80文字以下に収めることでgit log実行時の一覧性をあげたり、不当に大きなコミットにならないように示せる
  • 既存のgit hookを使い回せる
    • CustomScriptというHookがあり、これを使うと既存のhookに処理を委譲できます

制限事項

インストール

overcommitはRuby製のツールなので、Rubyのインストールを忘れずに。その後、rubygemsで配布されているので下記のコマンドでOKです。rvmやrbenvを使っている方は適宜コマンドを補完してください。

$ gem install overcommit

その後、overcommitを利用したいリポジトリで下記のコマンドを実行します。もし既存のgit hookを利用している場合は、そのファイルをバックアップしておきましょう。上述した通り、CustomScriptとして再利用できます。バックアップをお忘れなく

$ cd git-repo
$ overcommit --install

これでovercommitのベースとなるhookが対象のgitリポジトリにインストールされました。試しに適当なファイルをコミットしてみましょう。

overcommit1.gif

こんな感じで最初はRunovercommit --signif you trust the hooks in this repository.と出ます。overcommit --signはovercommit経由で実行するスクリプトのハッシュを記録しておき、fetchした時に不正にスクリプトが書き換わって意図しない動作を防ぐための機構です。overcommitに何も登録していない状態ではコミットメッセージのチェックだけなので、signしてしまいましょう。

$ overcommit --sign

を実行後、再度git commitをすると下記のようにコミットメッセージがチェックされるはずです。

overcommit.gif

リポジトリからのアンインストールも下記のコマンドでおkっす

$ overcommit --uninstall

設定方法

設定ファイルたる.overcommit.ymlを書き換えたらovercommit --signを実行するのを忘れずに

pre-commit

Rubocopの設定

overcommitの設定は、.overcommit.ymlファイルに記述します。例えばコミット前のタイミング(言い換えればpre-commit)でrubocopを実行したいのであれば、下記のように.overcommit.ymlを設定します。

.overcommit.yml
PreCommit:
  RuboCop:
    enabled: true
    command: ['bundle', 'exec', 'rubocop'] # bundlerの中でrubocopを実行する場合

これで、.rbGemfileの変更がcommitされる前にrubocopのチェックが走ります。1行目のPreCommitpre-commitグループにフックを追加する、という意味で、フックポイントごとにグループを宣言します。

commandは実際に実行するコマンドを記述します。実行ファイルが渡される前に部分と考えていいでしょう。

ESLintの設定

同様にESLintのチェックを行いたい場合は下記のように.overcommit.ymlを記述します。(overcommit側のメッセージ解析の都合上、package.jsonも調整が必要です)

.overcommit.yml
  ESLint:
    enabled: true
    required_executable: 'npm'
    command: ['npm', 'run', 'lint'] 
package.json
{
  "scripts": {
    "lint": "eslint -f compact"
  },
  "devDependencies": {
    "eslint": "^2.0.0"
  }
{

ESLintの場合、jsファイル等がステージングにある時に実行されます。詳しくはhookのソースをみるとよいです。

required_executableは必要な実行ファイルを指定する設定です。相対パスの場合はリポジトリ内のパス、そうでない場合はPATH上に存在するかどうかをチェックします。npmはPATHに存在しなければ実行できないので、指定する必要があります。これがないと、npm run lintが動作しません。

カスタムスクリプト

すでにスクリプトを利用している場合は下記の設定

  CustomScript:
    enabled: true
    required_executable: '.pre-commit.sh' # スクリプトのパスを指定する

post-checkout

bundle install

他のブランチをcheckoutしたタイミングでgem fileの更新をしたい場合は下記のように.overcommit.ymlを設定しましょう

.overcommit.yml
PostCheckout:
  BundleInstall:
    enabled: true

npm install

npm installの場合もこんな感じです。

.overcommit.yml
  NpmInstall:
    enabled: true

ここまでの設定を全て有効にする場合、.overcommit.ymlはこんな感じになっているでしょう。

.overcommit.yml
PreCommit:
  RuboCop:
    enabled: true
    command: ['bundle', 'exec', 'rubocop'] # bundlerの中でrubocopを実行する場合
  ESLint:
    enabled: true
    required_executable: 'npm'
    command: ['npm', 'run', 'lint'] 
  CustomScript:
    enabled: true
    required_executable: '.pre-commit.sh' # スクリプトのパスを指定する
PostCheckout:
  BundleInstall:
    enabled: true
  NpmInstall:
    enabled: true

これらのHookの詳細はリポジトリの説明からたどることができます。

こんな感じでプロジェクトに必要なhookを組み合わせて設定できます。

FAQ

コミット時に特定のHookをスキップしたい

$ SKIP=RuboCop git commit

コミット時に全てのHookをスキップしたい

OVERCOMMIT_DISABLE=1 git commit