添加git语法检查钩子

添加pre-commit钩子

vim app_code/.git/hooks/pre-commit
备注:在hooks下默认会有pre-commit.sample等文件存在,.sample拓展名防止它们默认被执行

添加语法检查内容

my $commit_id = `git rev-parse --verify HEAD`;
my $against = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";

if ($commit_id) {
    $against="HEAD";
}

my @list = `git diff-index --cached --name-only $against --`;
my $ret = 0;

for my $x (@list) {
    chomp $x;
    if ($x =~ /\.php$/) {
        if (-f "$x") {
            `php -l $x`;
            if ($? != 0) {
                $ret = 1;
                goto end;
            }
        }
    }
}

end:
exit($ret);

添加执行权限

chmod +x app_code/.git/hooks/pre-commit

添加测试

Alt text

缺点

然而使用pre-commit脚本存在一个很大的缺陷,钩子脚本需要放置在每个开发者的每个项目中,很容易遗漏,同时代码库重新clone后也需要记得重新放置钩子脚本,没有办法从根源上禁止有语法错误的代码的提交

解决方案

统一的在git服务端添加pre-receive钩子,当代码push到中心代码库的时候进行语法检测,如果存在语法错误则拒绝当前的push,这样可以只在服务端配置,不需要在每个人的每个项目中进行配置,Git在接受到push时会调用pre-receive钩子,如果pre-receive返回非0的返回值,则当前的push会被拒绝。在调用pre-receive脚本时,会从标准输入传递三个参数,分别为之前的版本,push的版本和push的分支

添加pre-receive钩子

vim app_code/.git/hooks/pre-receive

添加语法检查内容

#!/usr/bin/perl
use File::Temp;
use strict;

#从标准输入中读取参数信息
my $line = <>;
#处理后得到旧版本,但前版本和分支
my @argv = split(/\s/, $line);
my $old = $argv[0];
my $new = $argv[1];
my $branch = $argv[2];
#比较两个版本的差异
my @out = `git diff --name-status $old $new`;

for my $x (@out) {
    my @param = split(/\s/, $x);
    #如果文件不是被删除且是php文件,则进行语法检测
    if ($param[0] ne 'D' and $param[1] =~ /\.php$/) {
        #取出文件的内容,将其写入到一个临时文件中
        my $content = `git show $new:$param[1]`;
        my $fh = File::Temp->new(SUFFIX => '.php', UNLINK => 1);
        print $fh $content;
        #进行语法检测
        my $result = `php -l $fh 2>&1`;
        #如果出错则输出错误,输出时需要将临时文件名替换称为原来的文件名,并拒绝push
        if ($? != 0) {
            if ($result =~ s#$fh#$param[1]#g) {
                print $result;
            }
            exit(1);
        }
    }
}
exit(0);

添加执行权限

chmod +x app_code/.git/hooks/pre-receive  
坚持原创技术分享,您的支持将鼓励我继续创作!