背景・やりたいこと
c++で開発中、clang-formatを実行しわすれてコミットしてしまうことがあった
git-clang-formatや、ここに書かれているhookを使えば解決するかもしれないが、この先他者にも使ってもらう場合を考えると、極力コマンドをインストールすることは避けたい
そこで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
という名前で保存する
#!/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
動作
以下ディレクトリ構成を作る
$ tree -a sample
sample
├── .clang-format
└── sample.cc
ファイルは以下の通り
BasedOnStyle: 'Google'
#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
実行後以下出力が表示され、コミットが失敗する
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
- git commit時にlinterを走らせるhookに関する記事
- かなり参考にさせていただいた
git/hooks--pre-commit.sample at v2.19.0 · git/git · GitHub
- 空コミット時の比較方法(って言えばいいのかな?)の書き方を学んだ
- また
--diff-filter
というオプションはこれで知った気がする
標準出力をファイルのように扱う方法、例えば2つのコマンドの出力結果のdiffを取るとか - Qiita
- 元ファイルとclang-formatの出力の比較の実現のためにProcess Substitutionという機能を学んだ