zxでGitのコミット時に問題があればコミットしない方法

zxとは

Googleが制作したJavaScriptでシェルスクリプトの処理が書けるようにしたもの。

zxを使用するには以下のコマンドでzxをインストールする必要があります。

npm i -g zx

例えばシェルスクリプトでgit statusで表示されるファイルの一覧を取得するには以下のように書きます。

git status -s | sed s/^...//
$ ./test.sh
foo.txt
bar.txt
baz.txt

zxを使用する場合は以下のように書けます。

zxはawait $`<コマンド>` でgit statusなどを実行できます。

// test.mjs
const {stdout} = await $`git status -s`
const gitStatusFiles = stdout.replace(/^.{3}/gm, '')
console.log(gitStatusFiles)
$ zx test.mjs --quiet
foo.txt
bar.txt
baz.txt

※ test.shとtest.mjsは実行前にchmod +xでユーザーに実行権限を与えてください。

※ zxで--quietを付けることでconsole.log以外の結果を表示させないようにできます。

pre-commitについて

pre-commitとはGitフックを使用したコミットが発生した時にカスタムスクリプトを叩く方法。

Gitの不可視フォルダ内に「.git/hooks/pre-commit.sample」というファイルがあるので、これをpre-commitにリネームして使用する。

例えば「.git/hooks/pre-commit」内に以下のコードを記述するとコミット時に実行してgit statusで取得したファイルの一覧をconsole.logで表示することができる。

#!/usr/bin/env zx --quiet

const {stdout} = await $`git status -s | sed s/^...//`
const gitStatusFiles = stdout.split('\n').filter(v => v)
console.log(gitStatusFiles)
$ git commit -m "add bar.txt foo.txt"
[ 'bar.txt', 'foo.txt' ]
[master 2c87d85] add bar.txt foo.txt
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar.txt
 create mode 100644 foo.txt

特定のファイル名が含まれる場合コミットしない

前述の通りJavaScriptの記法でファイルの一覧を取得できるので特定のファイル名が含まれる場合コミットしない処理も簡単に書けます。

やり方はincludesメソッドで取得したファイル一覧に特定のファイル名が含まれている場合は「await $`exit 1`」を返すようにします。

例えば以下のように書けばfoo.txtがgit statusの一覧に含まれている場合はgit commit時にコミットしないようにできます。

zxはconsole.logでchalkを使うと色を変更できるのでこちらも追加しています。

#!/usr/bin/env zx --quiet

const {stdout} = await $`git status -s | sed s/^...//`
const gitStatusFiles = stdout.split('\n').filter(v => v)

if (gitStatusFiles.includes('foo.txt')) {
  console.log(chalk.red('コミット禁止のファイルが含まれています。'))
  await $`exit 1`
}
zxはconsole.logでchalkを使うと色を変更できる

複数ファイルの場合はfor ... ofでループさせる。

#!/usr/bin/env zx --quiet

const {stdout} = await $`git status -s | sed s/^...//`
const gitStatusFiles = stdout.split('\n').filter(v => v)
const forbiddenFiles = ['foo.txt', 'bar.txt', 'baz.txt']

for (const forbiddenFile of forbiddenFiles) {
  if (gitStatusFiles.includes(forbiddenFile)) {
    console.log(chalk.red('コミット禁止のファイルが含まれています。'))
    await $`exit 1`
  }
}

これを応用すればファイル名ではなく特定の拡張子やディレクトリが含まれる場合はコミットしないようにできる。

ファイルサイズが大きい場合はコミットしない

ターミナルで「ls -hl」を使用すればファイルサイズが取得できるので応用すればファイルサイズが大きい場合はコミットしないこともできる。

例えば1MB以上の場合はコミットしない場合は以下のように書くことで実装できます。

#!/usr/bin/env zx --quiet

const {stdout} = await $`git status -s | awk '{print $2}' | xargs ls -hl | awk '{print $5 "\t" $9}'`
const gitStatusFiles = stdout.split('\n').filter(v => v)

for (const file of gitStatusFiles) {
  if (/^[\d\.]+M\t/.test(file)) {
    console.log(chalk.red('1MB以上のファイルが含まれています。'))
    console.log(file)
    await $`exit 1`
  }
}