git钩子_Git钩子简介

git钩子

Git hooks are simple scripts that run before or after certain actions. They are useful for a variety of tasks, but primarily I find them useful for client-side validation so simple mistakes can be prevented. For example, you can test syntax on files being commited, you can even have tests run. I have written hooks that validate Twig syntax, run JSHint to a standard, and a lot more.

Git挂钩是在某些操作之前或之后运行的简单脚本。 它们对各种任务很有用,但主要是我发现它们对客户端验证很有用,因此可以避免简单的错误。 例如,您可以测试要提交的文件的语法,甚至可以运行测试。 我写了一些钩子来验证Twig语法,将JSHint运行到一个标准,等等。

Git hooks are also extremely simple by design. Git will run these hooks if the script is executable and Git will allow the action (e.g. commit or push) to occur as long as the hook exits with no errors (status 0). Hooks can be written in any language the environment can work with.

吊钩在设计上也非常简单。 如果脚本是可执行的,那么Git将运行这些钩子,并且只要钩子没有错误退出(状态0),Git就会允许执行该操作(例如,提交或推送)。 挂钩可以用环境可以使用的任何语言编写。

There are two types of hooks:

钩子有两种类型:

  • Client-side – These run on the developer’s system

    客户端–它们在开发人员的系统上运行
  • Server-side – These run on the server hosting the Git repository

    服务器端–它们在托管Git存储库的服务器上运行

Server-side hooks will not be covered in this article. However, do note that if your project is on a service like GitHub, server-side hooks are generally not applicable. On GitHub, the equivalent to server-side hooks is to use services and Webhooks which can be found in your project settings.

本文不涉及服务器端挂钩。 但是,请注意,如果您的项目位于GitHub之类的服务上,则服务器端挂钩通常不适用。 在GitHub上,等效于服务器端挂钩的是使用可以在项目设置中找到的服务和Webhooks。

挂钩文件 (The Hook Files)

Every repository including those you clone by default will have example hooks in the .git/hooks directory:

每个存储库(包括默认情况下克隆的存储库)在.git/hooks目录中都具有示例挂钩:

git clone git@github.com:symfony/symfony.git
cd symfony
ls .git/hooks

In that directory, you will see something like:

在该目录中,您将看到类似以下内容的内容:

applypatch-msg.sample
commit-msg.sample
post-update.sample
pre-applypatch.sample
pre-commit
pre-commit.sample
prepare-commit-msg.sample
pre-push.sample
pre-rebase.sample
update.sample

We will focus on the pre-commit hook which runs prior to allowing a commit.

我们将专注于在允许提交之前运行的pre-commit挂钩。

挂钩示例:验证PHP语法 (An Example Hook: Validating PHP Syntax)

We will begin with a very simple hook, written in Bash, that validates PHP code being committed has valid syntax. This is to prevent a “quick” but broken commit from happening. Of course I discourage “simple commits” that have little to no testing, but that does not mean they will not happen.

我们将以用Bash编写的非常简单的钩子开始,该钩子将验证提交PHP代码具有有效的语法。 这是为了防止发生“快速”但中断的提交。 当然,我不鼓励很少甚至没有测试的“简单提交”,但这并不意味着它们不会发生。

In .git/hooks we can start a new file called pre-commit. It must have permissions to execute:

.git/hooks我们可以启动一个名为pre-commit的新文件。 它必须具有执行权限:

cd .git/hooks
touch pre-commit
chmod +x pre-commit

You can use your favourite editor to begin writing. First we need the shebang. My favoured way is to use /usr/bin/env as this uses the correct path to the application we want rather than a hard-coded and possibly invalid path. For now we will have it continuously fail so we can easily test.

您可以使用自己喜欢的编辑器开始编写。 首先,我们需要shebang。 我偏爱的方法是使用/usr/bin/env因为它使用了我们想要的应用程序的正确路径,而不是硬编码且可能无效的路径。 目前,我们将使它不断失败,因此我们可以轻松进行测试。

#!/usr/bin/env bash
# Hook that checks PHP syntax

# Override IFS so that spaces do not count as delimiters
old_ifs=$IFS
IFS=$'\n'

# Always fail
exit 1

PHP has a useful option for syntax validation: -l. It takes a single file argument, so we will have to loop through whatever PHP files are being changed. For simplicity we’ll assume any PHP files being committed always end in .php. Since the hook is run from the root of the repository, we can use standard Git commands to get information about the changes, like git status.

PHP有一个用于语法验证的有用选项: -l 。 它只需要一个文件参数,因此我们将不得不遍历所有要更改PHP文件。 为简单起见,我们假定提交的任何PHP文件始终以.php 。 由于挂钩是从存储库的根目录运行的,因此我们可以使用标准的Git命令来获取有关更改的信息,例如git status

Above the #Always fail line we can use the following to get all PHP files being modified:

#Always fail行上方,我们可以使用以下命令修改所有PHP文件:

php_files=$(git status --short | grep -E '^(A|M)' | awk '{ print $2 }' | grep -E '\.php$')

Explanation:

说明:

  • php_files= In Bash assignment is done with no delimiter but note that referencing a variable requires the $ delimiter

    php_files=在Bash中,分配没有分隔符,但请注意,引用变量需要$分隔符

  • $() is syntax for ‘get output’. Quotes are not required to use this.

    $()是“获取输出”的语法。 不需要使用引号。

  • grep is being used to check for added (A) and modified files (M)

    grep用于检查添加的文件( A )和修改的文件( M )

  • Awk is being used here to print $2. A complete git status --short line has extra space and extra data at the beginning, so we want to remove that. Awk also performs automatic stripping.

    这里使用Awk打印$2 。 完整的git status --short行开头具有额外的空间和额外的数据,因此我们希望将其删除。 Awk还执行自动剥离。

  • grep is again being used, but now is checking to make sure the lines end in .php

    grep再次被使用,但是现在正在检查以确保行以.php

Now we can verify each file with a for loop:

现在我们可以使用for循环来验证每个文件:

for file in $php_files; do
  if ! php -l "$i"; then
    exit 1
  fi
done

This may seem a bit strange but ! php -l "$i" (note the quotes to avoid issues with spaces) is actually checking for a return value of 0, not true or any of the sort of values we normally expect in other languages. Just for reference, the approximately equivalent PHP code would be:

看来这有点奇怪! php -l "$i" ! php -l "$i" (请注意使用引号来避免空格问题)实际上是在检查返回值0 ,不是true还是其他语言通常期望的任何值。 仅供参考,大约等效PHP代码为:

foreach ($php_files as $file) {
  $retval = 0;
  $escapedFile = escapeshellarg($file);
  exec('php -l ' . $escapedFile, $retval); // $retval passed in as out parameter reference
  if ($retval !== 0) {
    exit(1);
  }
}

I made a bad change to src/Symfony/Component/Finder/Glob.php on purpose to test this and the output from git commit -m 'Test' is like so:

src/Symfony/Component/Finder/Glob.phpsrc/Symfony/Component/Finder/Glob.php进行了错误的更改以进行测试,而git commit -m 'Test' src/Symfony/Component/Finder/Glob.php git commit -m 'Test'的输出如下所示:

PHP Parse error:  syntax error, unexpected 'namespace' (T_NAMESPACE) in src/Symfony/Component/Finder/Glob.php on line 12

Parse error: syntax error, unexpected 'namespace' (T_NAMESPACE) in src/Symfony/Component/Finder/Glob.php on line 12

Errors parsing src/Symfony/Component/Finder/Glob.php

I made the loop exit the entire script as early as possible and this ultimately may not be what we want. We may in fact want a summary of things to fix as opposed to having to continue to try to commit. Anyone would easily get frustrated eventually and might even learn to use git commit --no-verify to bypass the hook altogether.

我使循环尽早退出整个脚本,而这最终可能不是我们想要的。 实际上,我们可能想要总结一些要解决的问题,而不是继续尝试提交。 任何人最终都会很容易沮丧,甚至可能学会使用git commit --no-verify来完全绕过钩子。

So instead, let’s not exit on the error with php -l but I still would like to keep things easy to read:

因此,相反,我们不要退出php -l错误,但我仍然想让事情易于阅读:

for file in $php_files; do
  php_out=$(php -l "$file" 2>&1)
  if ! [ $? -eq 0 ]; then
    echo "Syntax error with ${file}:"
    echo "$php_out" | grep -E '^Parse error'
    echo
  fi
done

Here we capture the output for php -l (and force standard error output to standard output). We check the exit status of php -l using the special variable $? (which is the exit status code) and the operator -eq. We state that a syntax error occurred (note the use of ${} for a variable in a string). Finally, we give the relevant line for error to keep output a little more brief (grepping for '^Parse error'), and we give one blank line to keep this a little more readable.

在这里,我们捕获了php -l的输出(并将标准错误输出强制为标准输出)。 我们使用特殊变量$?检查php -l的退出状态$? (这是退出状态代码)和运算符-eq 。 我们声明发生语法错误(请注意,在字符串中使用${}表示变量)。 最后,我们为错误提供了相关的行,以使输出保持简短(对'^Parse error'填充),并为空行提供了一行以使其更具可读性。

I made two bad modifications and the output for an attempt at a commit looks like this:

我做了两个错误的修改,尝试提交的输出看起来像这样:

Syntax error with src/Symfony/Component/Finder/Finder.php:
Parse error: syntax error, unexpected '}' in src/Symfony/Component/Finder/Finder.php on line 118

Syntax error with src/Symfony/Component/Finder/Glob.php:
Parse error: syntax error, unexpected 'namespace' (T_NAMESPACE) in src/Symfony/Component/Finder/Glob.php on line 12

Now the course of action is to fix these problems, test, and try to commit again.

现在,解决方案是修复这些问题,进行测试,然后尝试再次提交。

To complete the hook script, remove the exit 1 at the bottom of the script. Try to commit valid PHP files and it should work as normal.

要完成钩子脚本,请删除脚本底部的exit 1 。 尝试提交有效PHP文件,它应该可以正常工作。

共享钩子 (Sharing Hooks)

Hooks are not distributed with your project nor can they be automatically installed. So your best course of action is to create a place for you hooks to live (could be in the same repository) and tell your collaborators to use them. If you make this easy for them, they are more likely to do so.

挂钩不随您的项目一起分发,也不能自动安装。 因此,最好的做法是为钩子创建一个生存的场所(可能位于同一存储库中),并告诉协作者使用它们。 如果您对他们方便,那么他们更有可能这样做。

One simple way to do this would be to create a hooks directory and a simple installer install-hooks.sh that links them (rather than copying):

一种简单的方法是创建一个hooks目录和一个链接它们的简单安装程序install-hooks.sh (而不是复制):

#!/usr/bin/env bash
for i in hooks/*; do ln -s "${i}" ".git/hooks/${i}"; done

Anyone who clones your project can simply run bash install-hooks.sh after cloning.

克隆项目的任何人都可以在克隆后直接运行bash install-hooks.sh

This also has the benefit of keeping your hooks under version control.

这还具有使钩子受版本控制的好处。

其他挂钩 (Other Hooks)

  • prepare-commit-msg – Provide a default commit message if one is not given.

    prepare-commit-msg –如果未提供默认提交消息,则提供默认提交消息。

  • commit-msg – Commit message validation.

    commit-msg –提交消息验证。

  • post-commit – Runs after a successful commit.

    post-commit –成功提交后运行。

  • pre-push – Runs before git push after the remote is verified to be working. It takes 2 arguments: the name of the remote, and the URL to it.

    pre-push –在验证遥控器正常工作后,在git push之前运行。 它包含2个参数:远程服务器的名称和URL。

  • pre-rebase – Runs before git rebase.

    pre-rebase –在git rebase之前运行。

  • post-checkout – Runs after a successful checkout.

    post-checkout –成功结帐后运行。

  • post-merge – Runs after a successful merge.

    post-merge –成功合并后运行。

These hooks generally work the same as pre-commit although they take in arguments. One use case for post-checkout is to ensure that a file always gets proper permissions (because Git only tracks executable, not executable and symbolic link):

尽管这些挂钩接受参数,但它们通常与pre-commit相同。 post-checkout一种用例是确保文件始终获得适当的权限(因为Git仅跟踪可执行文件,而不跟踪可执行文件和符号链接):

#!/usr/bin/env bash
# Make sure only I can read this file
chmod 0600 my-file-with-secrets

For commit-msg you may want to ensure all commit messages conform to a standard, like [subproject] Message. Here is one in PHP:

对于commit-msg您可能需要确保所有提交消息均符合标准,例如[subproject] Message 。 这是PHP中的一个:

#!/usr/bin/env php
<?php
// The message is passed as the the last argument.
$message = file_get_contents($argv[count($argv) - 1]);
if (!preg_match('/^\[[a-z_\-]+\]\s[A-Z]/', $message)) {
  echo 'Message must be of format: [subproject] Message';
  exit(1);
}
?>

结论 (Conclusion)

Git hooks are a powerful means to automate the workflow of your project. You can validate code, commit messages, ensure environment is proper, and a whole lot more. Is there something interesting you are using Git hooks for? Let us know in the comments!

Git挂钩是自动化项目工作流程的强大工具。 您可以验证代码,提交消息,确保环境合适以及更多。 您正在使用Git挂钩进行有趣的事情吗? 让我们在评论中知道!

翻译自: https://www.sitepoint.com/introduction-git-hooks/

git钩子

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值