实现 Git 挂钩

已完成

确定开发过程中代码质量优先级时,应首先从本地代码开发开始。 应确定该操作的时机,即使是在启动拉取请求以检测和纠正潜在的代码质量问题之前。

Git 挂钩为该操作提供了很好的时机。 它们作为一种机制,用于在 Git 生命周期中的重要事件(如提交、合并和推送)发生时执行自定义脚本。 这些脚本位于存储库的 .git\hooks 目录中,为自动执行软件开发任务和执行开发标准提供了几乎无限的灵活性。

如何实现 Git 挂钩

首先,让我们浏览客户端 Git 挂钩。 导航到存储库 .git\hooks 目录 – 那里有许多扩展名为 sample 的文件。 此扩展名不仅指示了其用途,而且有效阻止了这些文件运行。 文件名指定了在删除 sample 扩展后触发执行过程的 Git 操作。

用于自动化的 Git 挂钩文件的屏幕截图。

将预提交 sample 文件重命名为 pre-commit。 正如文件的名称所示,每当调用 git 提交操作时,该文件包含的脚本都将运行。 仅当预提交脚本以 0 返回值退出时,才会执行提交。

但请务必注意,默认情况下,在任何 Windows 操作系统中该操作无法按预期执行。 通常被忽视的此行为的原因是脚本的第一行:

#!/bin/sh

在 Linux 操作系统上,#! 前缀向程序加载程序指示,文件的其余部分包含要解释的脚本,并且 /bin/sh 是应使用的解释器的完整路径。

虽然 Git for Windows 支持 Bash 命令和 shell 脚本,但它在指定文件系统路径时不遵循相同的约定。 而是需要提供以驱动器号为起始的 sh.exe 文件的完整路径。

但还有一个问题,Git for Windows 会默认安装在 C:\Program Files 目录中。 由于此目录的名称中包含一个空格,因此所得到的 sh.exe 文件路径将被解释为两个单独的路径,从而导致失败。 为了避免这种情况,需要在空格前面添加单个反斜杠 (\) 作为转义字符。 实际上,使用 64 位版本的 Git for Windows 时,脚本的第一行应采用以下格式:

#!C:/Program\ Files/Git/usr/bin/sh.exe

如何实现

可如何使用新发现的 Git 预提交脚本功能? 如何防止意外将机密泄露给 GitHub?

现在使用 Git 挂钩扫描提交到本地存储库中的特定关键字的代码。 将预提交 shell 文件中的内容替换为以下代码:

#!C:/Program\ Files/Git/usr/bin/sh.exe
matches=$(git diff-index --patch HEAD | grep '^+' | grep -Pi 'password|secret')
if [ ! -z "$matches" ]
then
  cat <<\EOT
Error: Words from the blocked list were present in the diff:
EOT
echo $matches
exit 1
fi

此示例旨在说明概念,而不是完整的解决方案,因此只提供了简单的关键字列表。 通过使用正则表达式,可以显著扩展其范围和灵活性。 还可以选择引用外部文件,可极大简化正在进行的维护。

工作原理

调用后,预提交挂钩脚本使用 git diff 和 grep 命令来标识正在提交的代码的增量更改中的关键字或模式。 如果检测到任何匹配项,该脚本将生成错误消息并阻止提交发生。

更多信息:

其他预提交挂钩脚本的常见用例包括代码格式设置、linting 或运行自定义测试,以确保提交符合项目标准。 提交消息编辑器启动之前会运行 Prepare-commit-msg。 可实现动态生成提交消息,以强制实施命名约定,例如使用指定的前缀(例如,功能用 feat:,或 bug 修复用 fix:)。

例如,以下 prepare-commit-msg 脚本在创建新提交时自动将当前分支名称追加到提交消息。 它通过在文件开头添加分支名称后跟冒号和空格来修改提交消息文件 ($1)。

#!C:/Program\ Files/Git/usr/bin/sh.exe
# Get the current branch name
branch_name=$(git branch --show-current)
# Check if the commit message file exists
if [[ -f "$1" ]]; then
  # Prepend the branch name to the commit message
  sed -i "1s/^/$branch_name: /" "$1"
fi

提交后脚本在提交完成后执行。 它可用于触发通知或生成文档。

例如,以下脚本在每个提交后向指定收件人发送电子邮件通知。 可以通过修改收件人电子邮件地址、SMTP 服务器以及电子邮件的主题和正文来自定义脚本。 此外,可能需要将系统配置为使用 Send-MailMessage PowerShell cmdlet 发送电子邮件,或使用其他方法来发送通知,具体取决于环境和要求。

#!C:/Program\ Files/Git/usr/bin/sh.exe
# Set the recipient email address
$recipient="your@email.com"
# Set the subject of the email
$subject="Git Commit Notification"
# Set the body of the email
$body="A new commit has been made to the repository."
# Send the email notification
Send-MailMessage -To $recipient -Subject $subject -Body $body -SmtpServer "your.smtp.server"

值得注意的是,存储库 .git\hooks 文件夹不提交到源代码管理中。 你可能会想知道是否有办法与开发团队其他成员共享自己开发的脚本。 好消息是,从 Git 版本 2.9 开始,可以将 Git 挂钩映射到可提交到源代码管理中的文件夹。 可更新 Git 存储库的全局设置配置来执行此操作:

Git config --global core.hooksPath '~/.githooks'

如果你需要覆盖已在客户端上设置的 Git 挂钩,则可使用 no-verify 开关来实现此目的:

Git commit --no-verify

服务器端挂钩

虽然客户端 Git 挂钩提供了增强开发工作流的强大功能,但 Azure Repos 还提供服务器端挂钩来进一步增强开发过程,包括支持创建拉取请求。 有关详细信息,请参阅 Azure Repos 服务挂钩事件参考。