git hook相关内容

一、Git钩子

Git 能在特定的重要动作发生时触发自定义脚本,它能完成下列一些很常用的场景:

1.多人开发代码语法、规范强制统一
2.commit message 格式化、是否符合某种规范
3.如果有需要,测试用例的检测
4.服务器代码有新的更新的时候通知所有开发成员
5.代码提交后的项目自动打包(git receive之后) 等等...

每一个使用了 git 的工程下面都有一个隐藏的 .git 文件夹。

挂钩都被存储在 .git 目录下的 hooks 子目录中,即大部分项目中的 .git/hooks。 如下图:

Git 默认会放置一些脚本样本在这个目录中,除了可以作为挂钩使用,这些样本本身是可以独立使用的。所有的样本都是shell脚本,其中一些还包含了Perl的脚本。不过,任何正确命名的可执行脚本都可以正常使用 ,也可以用Ruby或Python,或其他脚本语言。

上图是git 初始化的时候生成的默认钩子,已包含了大部分可以使用的钩子,但是 .sample 拓展名防止它们默认被执行。为了安装一个钩子,你只需要去掉 .sample 拓展名。或者你要写一个新的脚本,你只需添加一个文件名和上述匹配的新文件,去掉.sample拓展名。把一个正确命名且可执行的文件放入 Git 目录下的 hooks子目录中,可以激活该挂钩脚本,之后他一直会被 Git 调用。

钩子分为客户端和服务端的,客户端的常用的钩子如下:

  • pre-commit钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。 如果该钩子以非零值退出,Git 将放弃此次提交,不过你可以用git commit --no-verify来绕过这个环节。 你可以利用该钩子,来检查代码风格是否一致(运行类似lint的程序)、尾随空白字符是否存在(自带的钩子就是这么做的),或新方法的文档是否适当。
  • prepare-commit-msg钩子在启动提交信息编辑器之前,默认信息被创建之后运行。 它允许你编辑提交者所看到的默认信息。 该钩子接收一些选项:存有当前提交信息的文件的路径、提交类型和修补提交的提交的 SHA-1 校验。
  • commit-msg钩子接收一个参数,此参数即上文提到的,存有当前提交信息的临时文件的路径。 如果该钩子脚本以非零值退出,Git 将放弃提交,因此,可以用来在提交通过前验证项目状态或提交信息。 在本章的最后一节,我们将展示如何使用该钩子来核对提交信息是否遵循指定的模板。
  • post-commit钩子在整个提交过程完成后运行。 它不接收任何参数 该钩子一般用于通知之类的事情。

对于任何Git仓库来说钩子都是本地的,而且它不会随着git clone一起复制到新的仓库。而且,因为钩子是本地的,任何能接触得到仓库的人都可以修改。在开发团队中维护钩子是比较复杂的,因为.git/hooks目录不随你的项目一起拷贝,也不受版本控制影响。一个简单的解决办法是把你的钩子存在项目的实际目录中(在.git外)。这样你就可以像其他文件一样进行版本控制。作为备选方案,Git同样提供了一个模板目录机制来更简单地自动安装钩子。每次你使用git init 或git clone时,模板目录文件夹下的所有文件和目录都会被复制到.git文件夹。

二、node项目下的git hook

husky 是一个 Git Hook 工具。husky 其实就是一个为 git 客户端增加 hook 的工具。将其安装到所在仓库的过程中它会自动在.git/目录下增加相应的钩子实现在pre-commit阶段就执行一系列流程保证每一个commit 的正确性。部分 cd在 commit stage执行的命令可以挪动到本地执行,比如 lint 检查、比如单元测试。当然,pre-commit阶段执行的命令当然要保证其速度不要太慢,每次 commit 都等很久也不是什么好的体验。

husky Github

下面使用husky来实现在提交前检查是否有测试快照,提交信息是否符合书写规范的例子

npm i husky happy-git-commit-message-checker -D

 package.json中增加如下代码(husky版本大于0.14的写法):

"husky": {
    "hooks": {
        "pre-commit": "node ./scripts/pre-commit.js",
        "commit-msg": "node ./scripts/commit-msg.js",
    }
}

./scripts/pre-commit.js(检查是否有测试快照用的)

const childProcess = require('child_process');
const fs = require('fs');
const path = require('path');
const fileMaxSize = 6 * 1024 * 1024; // 最大6MB

childProcess.exec('git status --short -u', (error, stdout) => {
    console.log('检测.snap快照文件体积');
    if (error) {
        console.log(error);
        process.exitCode = 1; // 禁止提交
    } else {
        const paths = stdout.split(/s/);
        for (let i = 0; i < paths.length; i++) {
            const pt = path.resolve(process.cwd(), paths[i]);
            if (/.snap$/.test(pt)) {
                // 是快照文件
                let stats;
                try {
                    stats = fs.statSync(pt);
                    if (stats.size > fileMaxSize) {
                        console.log(`x ${paths[i]} ${stats.size}Byte (单个快照文件体积最大6MB)`);
                        process.exitCode = 1; // 禁止提交
                        return;
                    }
                    console.log(`√ ${paths[i]} ${stats.size}Byte`);
                } catch (e) {
                    //
                }
            }
        }
    }
});
./scripts/commit-msg.js(提交信息规范检查)

我们要求的提交信息的规则如下:
  • 一律以【xx页】描述【xx模块】描述的形式书写代码注释

  • 优先使用【xx页】描述

  • 描述需尽可能详细

  • 涉及多个页面,每个页面一行,分开描述

const fs = require('fs');
const runner = require('happy-git-commit-message-checker').runner;

console.log('检测message书写规范');
try {
    let message = fs.readFileSync('./.git/COMMIT_EDITMSG', 'utf-8');
    const lines = message.split('
');
    if (!lines[lines.length - 1]) {
        lines.pop();
    }
    message = lines.join('
');
    runner(message, ['[bB]uild(.*?)']);
} catch (e) {
    console.log('检测程序运行出错...', e);
}

如何获取commit message?

需要调用者自己读取/.git/COMMIT_EDITMSG文件内容,在commit-msg钩子触发时,Git会自动将message写入COMMIT_EDITMSG文件,读取即可

配合lint-staged做代码检查

原文地址:https://www.cnblogs.com/94pm/p/13860799.html