git-hook简介与使用

195 阅读5分钟

hook

hook,钩子:即某个时期或事件之前、之后执行和一些操作或脚本。与我们常说的中间件很相似。

git-hooks

git有很多hook,每当我们git init初始化一个git仓库时,都会创建一个.git的隐藏文件夹,git的hook就放在.git/hooks下。hooks以.sample为后缀的是示例文件。git-hooks有客户端hooks和服务端的hooks。

这里介绍客户端会用到的hooks

  • pre-commit: 钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。
  • commit-msg: 钩子在启动提交信息编辑器之前,默认信息被创建之后运行。 它允许你编辑提交者所看到的默认信息。

git仓库根目录的隐藏文件夹.git里的git-hooks是不被git管理的,只存在于本地。所以无法让仓库中所有成员使用同样的hooks。这时候就需要通过工具库来使用git-hooks,就是husky。

husky

  • 安装 husky
pnpm add --save-dev husky
  • 添加 git hooks
pnpm exec husky init

init命令简化了在项目中设置husky的过程。它在.husty/中创建了一个预提交脚本,并在package.json中更新了prepare脚本

  • 添加新钩子
# echo方式,是适用于linux/mac的。
echo "command" > .husky/pre-[hook_name]
#window
node --eval "fs.writeFileSync('./.husky/.pre-[hook-name]','commoand')" 

这样就可以在hooh_name钩子期间触发command命令

接下来我们就可以借助commitlint校验commit信息。借助lint-staged来实现对暂存区的代码进行风格校验和格式化。

commitlint

commitlint是对commit信息校验的一款npm包。

  • 安装
pnpm i @commitlint/cli @commitlint/config-conventional -D
  • 添加git-hook
#linux/mac
echo 'npx --no-install commitlint --edit "$1"' > .husky/commit-msg
#window
node --eval "fs.writeFileSync('./.husky/commit-msg','npx --no-install commitlint --edit "$1"')" 

现在我们只是增加了对commit message的校验,但是在使用 Git 过程中,填写 commit message其实是比较麻烦的。如果没有良好的 commit message 规范,commit历史就会很乱。这时就需要 commitizen 来协助开发者填写 commit 信息。

commitizen

commitizen是基于 Node.js 开发的 git commit 命令行工具,用来生成标准化的commit-message。commitizen 本身只提供命令行交互框架以及一些 git 命令的执行,实际的规则则需要通过适配器来定义,commitizen 留有对应的适配器接口。市面上有很多适配器,比如: cz-conventional-changelogcz-git。常见适配器生产commit-message模板

<type>(<scope>): <subject>
<空行>
<body>
<空行>
<footer>
  • 安装 commitizen
pnpm install commitizen -D
  • 初始化,参数含义:使用cz-git适配器初始化commitizen,并用pnpm安装依赖。
npx commitizen init cz-git --save-dev --save-exact --pnpm
commitizen init 做了什么
1.  安装 `cz-git` 适配器 npm 模块
2.  将其保存到 package.json 的 devDependencies 中
3.   添加配置到 package.json 中 如下:
    "config": {  
        "commitizen": {  
            "path": "./node_modules/cz-git"  
            }  
        }
  • 新建 commitlint.config.js 文件:(对提交信息做格式检验的
/*
 * @Author: daiend
 * @Date: 2024-02-29 10:48:00
 * @Last Modified by: daiend
 * @Last Modified time: 2024-02-29 10:48:00
 * @Description: 代码提交message规范
 */

export default {
  ignores: [(commit) => commit === 'init'],
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 108],
    'subject-empty': [2, 'never'],
    'type-empty': [2, 'never'],
    'subject-case': [0],
    'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert', 'wip', 'workflow', 'types', 'release']],
  },
  prompt: {
    messages: {
      type: '选择你要提交的类型 :',
      scope: '选择一个提交范围(可选):',
      customScope: '请输入自定义的提交范围 :',
      subject: '填写简短精炼的变更描述 :\n',
      body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
      footerPrefixsSelect: '选择关联issue前缀(可选):',
      customFooterPrefixs: '输入自定义issue前缀 :',
      footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
      confirmCommit: '是否提交或修改commit ?',
    },
    types: [
      { value: 'feat: 特性', name: '特性: ✨ 新增功能', emoji: '✨' },
      { value: 'fix: 修复', name: '修复: 🧩 修复缺陷', emoji: '🧩' },
      { value: 'docs: 文档', name: '文档: 📚 文档变更', emoji: '📚' },
      { value: 'style: 格式', name: '格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)', emoji: '🎨' },
      { value: 'refactor: 重构', name: '重构: ♻️ 代码重构(不包括 bug 修复、功能新增)', emoji: '♻️' },
      { value: 'perf: 性能', name: '性能: ⚡️ 性能优化', emoji: '⚡️' },
      { value: 'test: 测试', name: '测试: ✅ 添加疏漏测试或已有测试改动', emoji: '✅' },
      { value: 'chore: 杂务', name: '杂务: ☕ 零星工作(如升级 npm 包、修改配置等)', emoji: '☕' },
      { value: 'ci: 集成', name: '集成: 🛠️ 修改 CI 配置、脚本', emoji: '🛠️' },
      { value: 'revert: 回退', name: '回退: ⏪️ 回滚 commit', emoji: '⏪️' },
      { value: 'build: 打包', name: '打包: 📦️ 项目打包发布', emoji: '📦️' },
    ],
    useEmoji: true,
  },
};

  • 配置 package.json 命令
"scripts": {
    "commit": "git status && git add -A && git-cz" 
}

运行pnpm run commit以触发git-hook规范commit message

lint-staged

一般情况下 lint-staged 搭配着 Husky 一起使用。需要保证lint-staged 会在 pre-commit hook 中被运行。在使用lint-staged校验代码和代码风格时这里使用的是prettiereslint

  • 安装
pnpm install lint-staged --save-dev
  • 添加 git hooks
#mac/linux
echo "npx lint-staged" > .husky/pre-commit
#window
node --eval "fs.writeFileSync('.husky/pre-commit','npx lint-staged\n')"
  • 配置

形式一:package.json 中添加一个 lint-staged 项。

"lint-staged": {
   "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.json": ["prettier --write"],
    "*.vue": ["eslint --fix', 'prettier --write', 'stylelint --fix"],
    "*.{scss,css,sass,less,html}": ["stylelint --fix ", "prettier --write"],
    "*.md": ["prettier --write"]
  }

形式二:根目录添加一个 .lintstagedrc.json 文件。

//.lintstagedrc.json
{
    "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.json": ["prettier --write"],
    "*.vue": ["eslint --fix", "prettier --write"],
    "*.{scss,css,sass,less,html}": [ "prettier --write"],
    "*.md": ["prettier --write"]
}

之后每次的pre-commit钩子触发都会执行代码校验和代码格式化。