LoginSignup
8
8

More than 3 years have passed since last update.

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

Posted at

背景・やりたいこと

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という名前で保存する

.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

ファイルは以下の通り

sample/.clang-format
BasedOnStyle: 'Google'
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実行後以下出力が表示され、コミットが失敗する

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という機能を学んだ
8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8