如何使用 Git Hooks 简化团队的开发工作流程
已发表: 2022-03-10为团队或开源项目工作的主要要求之一是使用版本控制系统 (VCS)。 Git 是一个免费的开源分布式版本控制系统,用于在软件开发过程中跟踪源代码更改。 它由 Linus Torvalds 在 2005 年创建,用于开发 Linux 内核。 它易于学习,占用空间小,性能快如闪电。
您很有可能已经使用过 Git(因为它是开发社区中最流行和采用最广泛的 VCS 工具之一),并且您很可能已经了解了通过推送和拉取来暂存和提交代码的知识它来自远程存储库。 本文不会讨论 git 工作流程的基础知识,但主要关注 git 钩子以及如何利用它们以在团队中实现更好的协作。 随着团队规模的扩大,让贡献者保持一致并维护有关代码的不同规则变得更加重要。
什么是 Git 钩子?
Git 挂钩是在 git 存储库中执行特定操作或事件时触发的脚本。 这些操作与版本控制工作流程的一部分有关,例如提交和推送。 通过自动化 git 工作流程中的任务,Hooks 非常有用。 例如,它们可以帮助我们根据某些特定规则验证代码库的语法,或者在提交更改之前运行一些测试。
如何设置它们?
Git 钩子是一个内置功能,这意味着只要初始化 git 存储库,我们就可以访问它们并开始使用它们。 让我们通过尝试设置它们来更详细地了解这意味着什么。
使用您最喜欢的终端,创建一个新的 git 存储库。
mkdir my-new-repository && cd my-new-repository git init ls -la
您会注意到刚刚创建了一个新的隐藏目录。 此文件夹.git
用于从 git 存储存储库相关信息,例如版本控制哈希、有关提交的信息、远程存储库地址等。 这也是 git .git/hooks
实际存在的钩子的文件夹。 您可以找到一些在初始化期间自动创建的预填充示例脚本。 这些实际上是特定操作后将触发的脚本。
ls .git/hooks
您可以找到的一些示例是:
-
pre-commit.sample
:在提交之前调用。 -
commit-msg.sample
:就地编辑消息文件。 -
post-receive.sample
:在远程存储库更新后调用。
引擎盖下
现在我们知道了在哪里可以找到钩子,让我们退后一步来了解它们实际上是如何工作的。
Git hooks 是基于事件的,所以只要我们在开发流程中执行一个 git 命令,git 就会检查 hooks 文件夹以查找是否有关联的脚本可以运行。 其中一些脚本将在这些开发流程操作之前或之后运行。
对于我们来说,一个很好的例子是提交工作流,这是一个非常熟悉的用例,可以更具体地了解触发钩子的流程。
每当我们对代码库提交任何更改时,其中一些相关的钩子都会按以下顺序触发:
-
pre-commit
:检查即将提交的快照并验证要提交的内容。 -
prepare-commit-msg
:允许您在提交作者看到之前编辑默认消息。 -
commit-msg
:将提交消息设置为模板。 -
post-commit
:在提交完成后运行一个动作,例如发送一个通知。
在上面的存储库中,现在让我们尝试添加一些自定义的提交前和提交后脚本,以进一步可视化 git 挂钩的实际工作方式。
nano .git/hooks/pre-commit
添加以下代码段:
#!/bin/sh echo Changes are about to be committed
确保我们的脚本是可执行的:
chmod +x .git/hooks/pre-commit
对post-commit
脚本重复上述过程:
nano .git/hooks/post-commit
#!/bin/sh echo Changes have been committed
chmod +x .git/hooks/post-commit
现在我们可以添加一个带有小 HTML 片段的新文件nano index.html
,仅用于演示目的(无需让 HTML 验证器知道这一点)。
<h1>Hello world from our new repository!</h1>
我们将通过 staging 在我们的代码库中添加更改,然后提交:
git add . git commit
提交成功后,我们可以看到上面添加的两个脚本的输出如下:
Changes are about to be committed Changes have been committed
正如预期的那样,git 在提交流程中触发了钩子。 添加的pre-commit
和post-commit
脚本正在运行,并将以正确的顺序执行(基于我们前面提到的顺序)。
这是一个简单的演示,目的是了解提交工作流脚本的工作方式以及它们是如何执行的。 有关此工作流程的更多详细信息,您可以在文档中阅读更多信息。
在上面的示例中,我们选择用 bash 编写这两个脚本,但事实是 git 支持可以用我们想要的任何脚本语言编写的钩子。 Ruby、Python 或 JavaScript 是很好的选择,只要我们在可执行脚本的第一行设置正确的 shebang。
例如,我们可以将pre-commit
钩子重写为 Node.js 脚本,如下所示:
#!/usr/bin/env node console.log("Changes are about to be commited")
本地和远程挂钩
钩子在本地和远程(或客户端和服务器)之间是分开的。 虽然本地挂钩在本地存储库上的特定操作之前或之后运行,但远程挂钩在推送到服务器之前或之后运行。 本地的不能用于执行策略,因为它们的性质使开发人员可以轻松地更改它们。 它们主要用于遵守我们希望在团队中应用的一些特定准则。 如果我们想要更严格并为我们的存储库执行一些策略,我们将驻留在远程挂钩中。
本地挂钩
pre-commit
-
prepare-commit-msg
-
commit-msg
-
post-commit
-
applypatch-msg
-
pre-applypatch
-
post-applypatch
-
pre-rebase
-
post-rewrite
-
post-checkout
-
post-merge
-
pre-push
远程挂钩
pre-receive
-
update
-
post-receive
共享挂钩
Git 钩子都是关于在团队内共享它们。 这是它们存在的主要原因:促进更好的团队协作,自动化有害流程,让我们只关注代码库的重要部分。
如前所述, .git/hooks
是托管我们自定义挂钩的文件夹,但是当我们需要在团队中共享这些脚本时,这并没有真正的帮助,因为 git 不会跟踪该文件夹。
解决这个问题的一个好方法是将我们所有的自定义钩子添加到我们存储库中的一个单独的文件夹中。 例如,我们可以添加一个.githooks
文件夹并将可执行脚本保存在那里。 然后,在项目初始化时,我们可以将这些脚本显式复制或符号链接到原始文件夹,以保留我们的钩子.git/hooks
。
find .git/hooks -type l -exec rm {} \\; find .githooks -type f -exec ln -sf ../../{} .git/hooks/ \\;
或者,如果您使用的是最新的 git 版本( 2.9
及更高版本),我们可以直接将 git hooks 路径配置到我们的自定义文件夹:
git config core.hooksPath .githooks
Git Hooks Made Easy(一个 JavaScript 代码库用例)
有一些工具可以帮助我们进一步将 git 钩子集成到我们的代码库的需求中。 特别是对于 JavaScript 代码库,我们可以使用 Husky 通过配置轻松自定义 git 事件的操作。
例如,我们可以轻松地对代码进行 lint 或在pre-commit
事件中运行一些测试,然后根据 linting、测试或两者是否成功继续提交。
这可以通过简单地扩展package.json
配置来实现:
{ "scripts": { "test": "echo Running tests" }, "devDependencies": { "eslint": "5.16.0", }, "husky": { "hooks": { "pre-commit": "eslint . && npm test", } } }
结论
在本文中,我们发现对 git 存储库执行的不同操作可以选择性地触发自定义脚本运行。 这些脚本可以在本地由开发人员控制,也可以在远程为团队或项目更集中地管理。 我们还了解到,脚本通常用 bash 之类的 shell 脚本编写,但实际上几乎可以使用任何脚本语言,甚至是 JavaScript。
Git 挂钩可以成为精心设计的工作流程中非常强大的一部分,我鼓励您尝试一下,看看您可以为自己的项目做些什么。