likes
comments
collection
share

扩展技能-Git Hooks 了解一下

作者站长头像
站长
· 阅读数 13

一、 什么是Git Hooks

Git Hooks定义

Git Hooks是Git的一个重要特性,它让你可以在Git仓库中定义一些自动化的脚本,这些脚本可以在特定的Git事件(如提交代码、接收代码等)发生时被触发执行。它们是在Git仓库目录中的 .git/hooks/ 下的一组可执行文件。

具体来说,每个Git仓库中都有一个名为".git/hooks"的隐藏目录,该目录中存放了一些示例的hook脚本。这些脚本本质上就是可执行的程序,可以用任何你喜欢的脚本语言来编写(如Bash、Python、Node.js等),只要该语言在你的系统环境中可执行即可。

例如,当你提交commit时,Git会检查 .git/hooks/ 中是否存在一个名为 pre-commit 的脚本。如果存在,并且这个脚本是可执行的,那么Git就会在执行commit操作之前运行这个脚本。

Git Hooks目录

.git 是一个隐藏文件夹,当输入 ll 是看不见的,可是输入ll -a,查看

扩展技能-Git Hooks 了解一下

进入到 .git 目录, ll 查看

扩展技能-Git Hooks 了解一下

在一个 Git 仓库的 .git 目录下,存储了与版本控制相关的所有数据和配置信息。下面是 .git 目录中一些重要文件和文件夹的含义和作用:

  • COMMIT_EDITMSG 文件:是 Git 中的一个临时文件,它用于保存提交消息(commit message)的编辑内容。当你执行 git commit 命令时,Git 会打开一个编辑器,供你输入提交消息。你在编辑器中输入的消息会被保存到 COMMIT_EDITMSG 文件中。一旦你输入完提交消息并保存退出编辑器,Git 就会读取COMMIT_EDITMSG文件中的内容,并将其作为本次提交的消息。

  • HEAD 文件:HEAD 文件记录了当前所在分支的引用。它指向一个引用(通常是分支),表示当前的工作目录状态。

  • ORIG_HEAD 文件:本质上是一个包含提交哈希值的文件,它保存了先前的操作位置的信息。它位于 Git 仓库的 .git 目录中,与 HEAD 文件和其他引用文件存放在一起。你可以使用命令cat .git/ORIG_HEAD来查看ORIG_HEAD引用指向的提交哈希值。在操作完成后,如果你意识到操作有误或需要回退到之前的状态,可以简单地使用 git reset 命令来恢复到 ORIG_HEAD 的位置,即恢复到操作前的状态。

  • config 文件:config 文件是存储了本地仓库的配置信息,例如用户名、用户邮箱、远程仓库等。

  • description 文件:description 文件用于描述仓库的信息。通常用于显示给仓库浏览器或其他工具。

  • hooks 文件夹:hooks 文件夹包含了 Git 钩子脚本的模板。Git 钩子是在特定事件发生时执行的自定义脚本,例如提交代码前后、切换分支前后等。

  • index 文件:index 文件是 Git 暂存区的索引文件。它记录了下一次将要提交的文件列表和状态信息。

  • info: info 是一个目录,用于存储一些 Git 相关的信息, 其中包含了一些配置和辅助文件。

    • exclude:该文件用于指定 Git 忽略不需要跟踪的文件或目录的模式规则。这些规则通常被添加到 .gitignore文件中,但有些情况下,可以将一些特殊规则放在 exclude 文件中。
    • attributes:该文件用于定义 Git 特定文件的属性,比如指定特定文件的 diff 策略、合并策略等。这些属性通常被添加到 .gitattributes 文件中,但有些情况下,可以将一些特殊属性放在 attributes 文件中。
  • logs 文件夹:logs 文件夹包含了提交日志,记录了分支上每次提交的信息。有不同的子文件夹,如 logs/heads 存储了本地分支的日志。

  • objects 文件夹:objects 文件夹包含了所有 Git 对象,例如提交(commit)对象、树(tree)对象和文件(blob)对象。这些对象是 Git 仓库中的最基本单位,用于存储文件内容和历史记录。

  • packed-refs: packed-refs 文件记录了 Git 仓库中的分支、标签和其他引用的详细信息。

    • 提供了一种高效的引用查找机制:将引用信息打包成 packed-refs 文件,可以加快引用查找的速度。相比于遍历 .git/refs 目录下的所有引用文件,直接读取packed-refs 文件更为高效。
    • 保存了历史的引用信息:packed-refs 文件记录了引用的哈希值以及引用的类型(分支还是标签)。这样,在操作历史中,可以追踪每个引用的变化,并了解每次引用的指向。
  • refs 文件夹:refs 文件夹包含了引用(references),如分支、标签等的指针文件。例如,refs/heads 文件夹保存了本地分支的指针,refs/tags 文件夹保存了标签的指针。

直接查看 .git/hooks 就能看到对应的 hook文件

扩展技能-Git Hooks 了解一下

当你想要启用某个钩子时,只需要去掉对应脚本文件后缀名中的.sample,然后添加你的代码,再赋予脚本文件可执行权限即可, 后面会详细说一下

Git Hooks的作用和用途

Git Hooks的作用非常广泛,它可以用来自动化和自定义你的Git工作流程。

以下是Git Hooks的常见应用

  • 代码风格检查:可以在 pre-commit Hook中运行一个lint工具对代码进行格式检查,确保提交的代码符合代码规范。
  • 自动化测试:可以在 pre-push Hook中自动运行测试用例,如果测试失败,那么Git就不会执行push操作,确保代码的质量。
  • 邮件通知:可以在 post-commit 或 post-receive Hook中发送一个邮件通知,告知其他开发者你已经提交了新的更改。
  • 自动生成文档:可以在 post-merge Hook中运行一个脚本,自动从最新的源代码生成API文档。

二、Git Hooks工作原理

Git Hooks的工作流程

Git Hooks的工作流程相当直观。当你执行一个Git命令(如git commit或git push)时,Git会首先检查".git/hooks"目录下是否存在对应的hook脚本,如果存在,则会触发执行这个脚本。

这个脚本的执行结果会决定是否继续执行Git命令。如果脚本执行成功(返回状态码为0),则继续执行Git命令;如果脚本执行失败(返回状态码非0),则停止执行Git命令。

需要注意的是,这种行为只适用于部分hooks,如pre-commit、pre-receive等。对于其他的一些hooks,如post-commit、post-receive等,即使hook脚本执行失败,也不会影响Git命令的执行。

Git Hooks的触发时机

Git Hooks的触发时机取决于它的类型。每种类型的hook都与一个特定的Git事件相关联。下面是一些常见的Git Hooks类型和对应的触发时机:

  • pre-commit:在执行"git commit"命令并生成提交对象之前被触发。
  • post-commit:在提交成功后被触发。
  • pre-push:在"git push"命令发送数据到远程仓库之前被触发。
  • post-receive:在远程仓库接收并处理完数据后被触发。

除了上述的几种类型,Git还提供了很多其他类型的hooks,你可以根据需要选择使用。

三、Git Hooks的种类

客户端钩子

githook调用时机说明
pre-commit在执行git commit命令但在生成提交对象之前被触发。常用来检查即将提交的快照,比如运行lint工具检查代码格式如果脚本以非零状态退出,Git会中断提交。
prepare-commit-msgprepare-commit-msg钩子在提交信息编辑器显示之前,提交对象创建完毕之后被触发。常用于给提交信息编辑器提供默认的提交信息,如修复bug时引用bug号等。
commit-msgcommit-msg钩子在提交信息编辑器关闭后、提交对象生成之前被触发。常用于检查提交信息是否符合格式要求如果脚本以非零状态退出,Git将中断提交。
post-commitpost-commit钩子在整个提交过程完成后被触发,常用于通知其他工具提交已经完成。此钩子不能影响提交过程的结果。
pre-rebasepre-rebase钩子在git rebase命令执行前被触发,常用于阻止对已经推送的提交进行rebase操作。如果脚本以非零状态退出,Git将中断rebase。
post-checkoutpost-checkout钩子在git checkoutgit switch成功运行后被触发,常用于提醒用户工作目录已经改变。
post-mergepost-merge钩子在git merge成功运行后被触发,常用于通知用户有文件被合并。
pre-pushpre-push钩子在git push命令发送数据到远程仓库之前被触发,常用于确保不会推送错误的提交到远程仓库。如果脚本以非零状态退出,Git将中断push。

服务器端钩子

githook调用时机说明
1. pre-receivepre-receive钩子在远程仓库接收到git push数据并开始更新处理之前被触发,常用于实现权限控制和引用(branch、tag等)的规则检查。
2. updateupdate钩子在远程仓库接收到git push数据,每个引用更新前被触发。与pre-receive类似,常用于实现权限控制和引用的规则检查。
3. post-receivepost-receive钩子在远程仓库接收到git push数据并完成所有更新后被触发,常用于触发持续集成、部署等后续任务,或者向外部系统发送有关新提交的通知。

四、如何配置Git Hooks

当你初始化一个新的Git仓库时,Git会在.git目录下创建一个名为hooks的子目录。这个hooks目录包含了一些示例钩子,它们是以.hook作为后缀的脚本文件。当你想要启用某个钩子时,只需要去掉对应脚本文件后缀名中的.sample,然后添加你的代码,再赋予脚本文件可执行权限即可。

例如,如果你想要创建一个pre-commit钩子,你可以在.git/hooks目录下创建一个名为pre-commit的脚本文件,并添加如下代码:

#!/bin/sh
echo "这是一个pre-commit钩子"

然后,你需要使用chmod命令赋予脚本文件可执行权限:

chmod +x .git/hooks/pre-commit

此后,每次执行git commit命令时,你的pre-commit钩子都会被触发。

钩子脚本文件的内容并不会被版本控制,也不会随着仓库的克隆而复制。因此,如果你希望在团队中共享钩子,你可能需要借助于额外的工具或技术。一种常见的做法是在项目的根目录下创建一个hooks目录,并将这个目录纳入版本控制。然后,每个团队成员可以通过创建符号链接或复制文件的方式将这些钩子添加到他们的本地仓库。

如果你想要防止自己或其他人误删或误改钩子脚本,你可以在.gitignore文件或.gitattributes文件中添加钩子脚本的路径,使其被Git忽略。

五、Git Hooks的示例

下面我们来看两个具体的Git Hooks示例。

1. pre-commit钩子示例

这是一个简单的pre-commit钩子示例,它会在每次提交前检查JavaScript代码的格式:

#!/bin/sh
files=$(git diff --cached --name-only --diff-filter=ACM | grep '.js$')
if [ "$files" = "" ]; then
  exit 0
fi

pass=true
for file in ${files}; do
   result=$(jshint $file)
   if [ "$result" != "" ]; then
      echo "\nJSHint check failed for:\n$file\n$result\n"
      pass=false
   fi
done

if [ "$pass" = "false" ]; then
  echo "\nCOMMIT FAILED:\nYour commit contains files that should pass JSHint but do not. Please fix the JSHint errors and try again.\n"
  exit 1
else
  exit 0
fi

这个钩子会检查即将提交的所有JavaScript文件,如果有任何一个文件未能通过JSHint的检查,提交就会被中断。

2. post-receive钩子示例

这是一个简单的post-receive钩子示例,它会在每次接收到新提交后自动部署代码:

#!/bin/sh
GIT_WORK_TREE=/var/www/myapp git checkout -f

这个钩子会在远程仓库接收到新提交后,自动将代码检出到/var/www/myapp目录,实现代码的自动部署。

六、三方管理库

pre-commit

要在JavaScript项目中使用pre-commit库,你需要遵循以下步骤:

  1. 安装pre-commit库:可以使用npm(或yarn)进行安装:
npm install pre-commit --save-dev

或者使用yarn:

yarn add pre-commit --dev

2. 在你的package.json文件中添加pre-commit字段,然后在里面列出你希望在提交前运行的npm脚本:

"pre-commit": [
    "lint",
    "test"
]

在这个例子中,linttest都是在package.json文件中定义的npm脚本。

例如:

"scripts": {
    "lint": "eslint .",
    "test": "jest"
}

现在每当你尝试提交代码时,pre-commit都会首先运行linttest这两个脚本。如果任何一个脚本失败了(返回非零退出代码),提交就会被阻止。

这种方法可以保证在代码提交到仓库之前都已经过了lint检查和单元测试。

husky

Git 钩子(hooks)是一种强大的工具,可以在你执行某些特定的 git 操作时自动执行一些任务。例如,你可以在每次提交代码时自动运行代码格式化器,或者在推送代码之前自动运行测试套件。以下是如何使用 git 钩子来改进你的工作流程的一些例子:

  1. 自动格式化代码:使用lint-stagedhusky库自动格式化代码。

下面是具体步骤:

首先,你需要在项目中安装这两个库:

npm install --save-dev lint-staged husky

或者如果你正在使用yarn:

yarn add --dev lint-staged husky

然后,需要在package.json文件中配置huskylint-staged。你可以使用Prettier和ESLint作为代码格式化工具。假设已经安装了Prettier和ESLint,配置如下:

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.js": ["prettier --write", "eslint --fix", "git add"]
  }
}

这个配置中,husky设置了一个在提交前运行的钩子。这个钩子会运行lint-staged

lint-staged会对所有 staged 的JavaScript文件进行处理。处理步骤包括运行prettier来自动格式化代码,运行eslint来自动修复可能的问题,然后再次添加到git中(git add)。

最后,需要在.eslintrc文件中(或者你的ESLint配置文件)添加以下规则,让ESLint和Prettier能够很好地协同工作:

{
  "rules": {
    "prettier/prettier": "error"
  },
  "plugins": ["prettier"],
  "extends": ["plugin:prettier/recommended"]
}

现在,每次提交代码时,所有的JavaScript文件都会被自动格式化,同时还会修复所有的ESLint可以自动修复的问题。如果有任何错误无法被自动修复,提交会被阻止,你需要手动修复这些错误。

  1. 自动运行测试:如果你有一个自动化的测试套件,你可以在每次提交或推送代码时自动运行它。这可以确保你不会意外地破坏任何东西。
"husky": {
  "hooks": {
    "pre-push": "npm test"
  }
}

这会在每次推送之前运行你的测试套件。

  1. 自动检查代码质量:你可以使用像 ESLint 这样的工具来检查你的代码质量,并在你提交代码时自动运行它。
"husky": {
  "hooks": {
    "pre-commit": "eslint ."
  }
}

这会在每次提交前运行 ESLint。

通过这些方式,git 钩子可以帮助你打造一个更精致、自动化的工作流程,它可以确保你的代码质量,并帮助你避免错误。 当然,除了上述的一些基本用法,Git 钩子还有许多其他的使用场景,例如:

  1. 自动更新版本号:使用 npm version 或者类似的工具,可以在每次提交或推送代码时自动更新版本号。例如,以下的钩子在每次提交时自动更新版本号:
"husky": {
  "hooks": {
    "pre-commit": "npm version patch"
  }
}

5. 自动部署:如果你有一个自动化的部署系统,可以设置一个钩子,在每次推送代码到部署分支时,自动触发部署过程。例如,以下的钩子在每次推送到 master 分支时,自动运行部署脚本:

"husky": {
  "hooks": {
    "pre-push": "npm run deploy"
  }
}

6. 自动更新文档:如果项目有自动生成文档的工具,例如 JSDoc,你可以在每次提交或推送代码时自动更新文档。例如,以下的钩子在每次提交时自动更新 JSDoc 文档:

"husky": {
  "hooks": {
    "pre-commit": "jsdoc -c jsdoc.json"
  }
}

7. 自动某些脚本 custom.js:例如你可以设置一个钩子,在每次提交或推送代码时自动运行某些脚本,比如代码分析等。例如,以下的钩子在每次提交时自动运行 custom.js:

"husky": {
  "hooks": {
    "pre-commit": "node custom.js"
  }
}

以上就是一些 Git 钩子的使用场景,这些都可以帮助你打造一个更精致、自动化的工作流程。然而,需要注意的是,虽然 Git 钩子有很多用途,但是它们并不能替代一个完整的 CI/CD 系统,因为 Git 钩子只在本地运行,而 CI/CD 系统可以在服务器上运行,从而提供更全面的自动化解决方案。

七、Git Hooks的注意事项和限制

虽然Git Hooks是一个非常强大的工具,但在使用它们时,也需要注意一些问题。

Git Hooks的权限和安全性

当Git触发一个钩子时,这个钩子脚本是以仓库的所有者的身份运行的。这意味着钩子脚本具有对仓库的完全访问权限,包括修改文件,执行命令等。因此,需要确保钩子脚本只能被信任的用户修改。

另外,由于钩子脚本具有较高的权限,所以需要避免在钩子中运行不安全的代码。在编写钩子脚本时,你需要谨慎对待所有的输入,特别是那些来自用户的输入。应该避免执行任何不安全的命令,例如,使用来自用户的输入构造和执行shell命令。

Hooks的跨平台问题

钩子脚本是在服务器的shell环境中运行的,这可能会引起一些跨平台问题。例如,如果你的团队中有些人使用的是Linux,有些人使用的是Windows,那么你可能需要在钩子脚本中处理这两种操作系统的差异。

为了解决这个问题,你需要针对不同的环境编写和测试你的hooks。例如,你可以使用条件语句检测运行钩子的操作系统,然后执行适当的命令。你还需要在不同的操作系统和环境中测试你的钩子,确保它们能在所有环境中正常工作。

另外,你也可以考虑使用一些跨平台的脚本语言编写钩子,例如Python或Node.js。这些语言的运行环境在不同的操作系统中是一致的,因此可以减少跨平台问题。

总的来说,虽然Git Hooks有一些需要注意的问题,但只要你正确地使用它们,它们就能为你的开发工作带来很大的便利。

转载自:https://juejin.cn/post/7249281117169614904
评论
请登录