commit時にclang-formatで修正されるファイルの有無の確認を行うhook

# 背景・やりたいこと

c++で開発中、clang-formatを実行しわすれてコミットしてしまうことがあった
[git-clang-format](https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/git-clang-format)や、[ここに書かれているhook](https://gist.github.com/alexeagle/c8ed91b14a407342d9a8e112b5ac7dab)を使えば解決するかもしれないが、この先他者にも使ってもらう場合を考えると、極力コマンドをインストールすることは避けたい
そこでgit commit時にclang-formatを走らせ、変更されるファイルがあればcommitを失敗させるhookを作った

個人の好みだが、git logを見たときに「こんな書き方したっけ？」とならないよう、ソースの修正は自身でやったほうがいいと思っている。
この理由から、git commit時にファイルの修正依頼のみ出力し、ファイルの修正は行わない。

# 動作確認環境

```
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:        18.04
Codename:       bionic
$ git --version
git version 2.19.0
$ clang-format --version
clang-format version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
```

# pre-commitファイル

今回の肝となるファイルを示す
下記ファイルを`git init`を実行した後、`.git/hooks/pre-commit`という名前で保存する

```bash:.git/hooks/pre-commit
#!/usr/bin/env bash

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=$(git hash-object -t tree /dev/null)
fi

exec 1>&2

# Check whether clang-format is exist or not.
if !(type "clang-format" > /dev/null 2>&1); then
    cat << EOS
There is not clang-format.
Please install clang-format.
EOS
    exit 1
fi

sum=0
array=()
for file in $(git diff --cached --name-only --diff-filter=AM $against -- | grep -E '\.(c|cc|cpp|cxx|wh|hpp)$'); do

    diff -q $file <(clang-format $file) 1>/dev/null 2>&1
    if [ $? -ne 0 ]; then
        #diff -u --color=auto $file <(clang-format $file)
        echo -e "\e[1m${file}\e[m"
        diff --color=always -u $file <(clang-format $file) | sed -e '1,2d'
        echo -e "\n"
        sum=$(expr $sum + 1)
        array=("${array[@]}" "$file")
    fi
done

if [ ${sum} -eq 0 ]; then
    exit 0
else
    echo -e "\e[1mPlease modify following files."
    for((i = 0; i < ${#array[@]}; i++)){
        echo "* ${array[i]}"
    }
    echo -ne "\e[m"
    exit 1
fi

```

# 動作

以下ディレクトリ構成を作る

``` bash
$ tree -a sample
sample
├── .clang-format
└── sample.cc
```

ファイルは以下の通り

``` :sample/.clang-format
BasedOnStyle: 'Google'
```
``` cpp:sample/sample.cc
#include <iostream>

int main(int argc, char const* argv[])
{
    std::cout << "Hello World." << std::endl;
    return 0;
}
```
この状態で下記コマンドを実行する

```
$ cd sample
$ git init
$ edit .git/hooks/pre-commit # pre-commitは上記した内容と同じ
$ git add -A
$ git commit
```

`git commit`実行後以下出力が表示され、コミットが失敗する

```diff
sample.cc
@@ -1,7 +1,6 @@
 #include <iostream>

-int main(int argc, char const* argv[])
-{
-    std::cout << "Hello World." << std::endl;
-    return 0;
+int main(int argc, char const* argv[]) {
+  std::cout << "Hello World." << std::endl;
+  return 0;
 }


Please modify following files.
* sample.cc
 
```

上記出力の通りファイルを修正した後、git add と git commitを実行すればよい

# 参考

[git commit 前に cpplint を走らせる - Qiita](https://qiita.com/janus_wel/items/cfc6914d6b7b8bf185b6) 

- git commit時にlinterを走らせるhookに関する記事
- かなり参考にさせていただいた

[git/hooks--pre-commit.sample at v2.19.0 · git/git · GitHub](https://github.com/git/git/blob/v2.19.0/templates/hooks--pre-commit.sample)

- 空コミット時の比較方法（って言えばいいのかな？）の書き方を学んだ
- また `--diff-filter`というオプションはこれで知った気がする

[標準出力をファイルのように扱う方法、例えば2つのコマンドの出力結果のdiffを取るとか - Qiita](https://qiita.com/kawaz/items/98768ba5eea8d6fe6a19)

- 元ファイルとclang-formatの出力の比較の実現のためにProcess Substitutionという機能を学んだ