git hook

客户端钩子

pre-commit(commit前 客户端进行验证)

下面的脚本copy自:https://gist.github.com/wangkuiyi/7379a242f0d4089eaa75

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/bin/bash
# 不加此行的话source tree就不能正常执行clang-format
export PATH=/usr/local/bin:$PATH
# 是否删除老的patch文件
DELETE_OLD_PATCHES=true

# 是否需要检验文件后缀
PARSE_EXTS=true

# 只校验哪些文件
FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m"

# 校验文件后缀
matches_extension() {
    local filename=$(basename "$1")
    local extension=".${filename##*.}"
    local ext

    for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done

    return 1
}

# 获取提交
if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
    against=HEAD
else
    # 如果是空的树
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# 判断clang-format 是否安装
command -v clang-format -version >/dev/null 2>&1 || { echo >&2 "clang-format 未安装"; exit 1; }
# 创建临时文件
prefix="pre-commit-clang-format"
suffix="$(date +%s)"
patch="/tmp/$prefix-$suffix.patch"

# 删除之前的patch文件
$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch

# 获取更改的文件名
git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
do
    # 判断是否是指定的后缀
    if $PARSE_EXTS && ! matches_extension "$file"; then
        continue;
    fi

    # 进行clang-format,并将改动放入patch的临时文件
    clang-format -style=file "$file" | \
        diff -u "$file" - | \
        sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch"
done

# 如果发现没有patch
if [ ! -s "$patch" ] ; then
    printf "没有发现需要clang-format的,直接提交.\n"
    rm -f "$patch"
    exit 0
fi

# a patch has been created, notify the user and exit
printf "\n发现需要clang-format的提交:\n\n "
cat "$patch"

printf "\nYou can apply these changes with(in git repo root directory):\n git apply $patch\n"
printf "(may need to be called from the root directory of your repository)\n"
printf "Aborting commit. Apply changes and commit again or skip checking with"
printf " --no-verify (not recommended).\n"
printf "\n如果想批量对所有文件进行格式化,可以执行仓库根目录下git_hook文件夹下的脚本格式化指定文件夹:\n"
printf "sh ./clang-format-allfile.sh /users/xxx/blibee\n"

exit 1

服务器钩子

pre-receive(push前 服务端 进行 clang-format验证)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/bin/bash
# clang-format 的路径
# CLANG_FORMAT="/usr/local/bin/clang-format"

# 是否只验证需要匹配的文件
PARSE_EXTS=true

#匹配哪些文件
FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m"

#记录一个全局变量,来标记clang-format 检测结果
CLANG_FORMAT_CHECK_RESULT=""

# 强制提交标识符 不做clang-format校验
COMMIT_WITHOUT_CLANG_FORMAT_VERIFICATION="COMMIT_WITHOUT_CLANG_FORMAT_VERIFICATION"

#获取 git提交的 sha-1
read -a infos <<< $(cat)
old=${infos[0]}
new=${infos[1]}


#创建临时文件夹,用来存放clang-format后的文件
dir="tmp_clangformat"
mkdir ${dir}

#主要是为了解决while read line 再新的进程,导致更改全局变量不能传递过来的坑,改用文件做判断
errorFile="$dir/errorfile"

# 匹配文件后缀
matches_extension() {
    local filename=$(basename "$1")
    local extension=".${filename##*.}"
    local ext

    for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done

    return 1
}


# 校验 文件合适是否正确
checkBlob() {
    echo "checkBoob 参数:$1,$2,$3"
    if [ ${2:0:1} == "\"" ] ; then
        echo "$2 文件名有误,可能是中文命名"
        CLANG_FORMAT_CHECK_RESULT="Error"
    else
        if $PARSE_EXTS && ! matches_extension "$2"; then
            echo "$2 文件格式不匹配,忽略"
        else
            echo "开始校验:$2"
            # 声明要存放的路径
            originFile="${dir}/tmp_originTmp.m"
            clangFormatFile="${dir}/cat.m"

            git cat-file blob $1 > $originFile
            clang-format -style=file $originFile > $clangFormatFile
            
            # 将两个文件进行比较
            if cmp -s $originFile $clangFormatFile
            then
                echo "$2    文件检验成功"
            else
                echo "$2    文件检验失败,请先用clang-format工具进行格式化"
                rm -f $originFile
                rm -f $clangFormatFile
                CLANG_FORMAT_CHECK_RESULT="Error"
            fi
        fi
    fi
    
}

# 解析sha1 得到正确的文件数据,然后传递给checkBlob
getBlob() {
    echo "解析sha1:$1"
    type=`git cat-file -t $1 | awk '{print $1}'`
    echo "sha1类型:$type"
    if [ "$type" = "tree" ];
    then
        echo "解析该sha1:$1"
        git cat-file -p $1 | while read info
        do
            echo "发现内容:$info"
            sha1=`echo "$info" | awk '{print $3}'`
            fileName=`echo "$info" | awk '{print $4}'`
            subType=`git cat-file -t $sha1 | awk '{print $1}'`
            if [ "$subType" = "tree" ];then
                echo "类型为tree,进行递归解析:$sha1"
                getBlob $sha1
            elif [ "$subType" = "blob" ]; then
                echo "类型为blob,进入checkBlob流程:$sha1"
                checkBlob $sha1 $fileName
                if [ ! "$CLANG_FORMAT_CHECK_RESULT" = "" ] ; then
                    echo "Error" > $errorFile
                    echo "得到格式校验失败的返回值,结束check循环"
                    break
                fi
            fi
        done
    elif [ "$type" = "blob" ];
    then
        echo "getBlob 参数传递错误,应该是tree对象"
    fi
}


# 检验clang-format 是否安装
command -v clang-format -version >/dev/null 2>&1 || { echo >&2 "clang-format 未安装"; exit 1; }

# 通过git对象,获取到更改后的文件内容
echo "找到commit:${new}"
# 获取commit 信息
commitMessage=`git log ${new} -n 1 --pretty='%s%b'`
echo "获取到commit message:${commitMessage}"
if [ $commitMessage = $COMMIT_WITHOUT_CLANG_FORMAT_VERIFICATION ]; then
    echo "发现特殊字符串,忽略clang-format直接进行提交"
    exit 0
fi
# 读取commit信息,获取到更新的tree
tree=`git cat-file -p ${new} | grep tree | awk '{print $2}'`
# 读取更新tree的信息
echo "找到commit对应的sha1:$tree"
# 读取commit 信息,如果读取到特殊的commit信息,则不进行校验,直接提交

getBlob $tree
    
    
# 检查结束
if [ -s "$errorFile" ] ; then
    rm -rf ${dir}
    echo "格式校验失败,done"
    exit 1
fi
# 校验完毕,可以提交
rm -rf ${dir}
echo "格式校验成功,done"
exit 0

--EOF--

若无特别说明,本站文章均为原创,转载请保留链接,谢谢