Git钩子脚本可以和存储库一起管理吗?

我们想做一些基本的钩子脚本,大家都可以共享——比如预先格式化提交消息。Git有钩子脚本,通常存储在<project>/.git/hooks/下。但是,当人们进行克隆时,这些脚本不会传播,并且它们不受版本控制。

是否有一种好方法可以帮助每个人获得正确的钩子脚本?我能不能让那些钩子脚本指向我的repo中的版本控制脚本?

120116 次浏览

理论上,你可以在你的项目目录中创建一个包含所有脚本的hooks目录(或任何你喜欢的名称),然后将它们符号链接到.git/hooks中。当然,克隆该回购的每个人都必须设置这些符号链接(尽管您可以有一个非常出色的部署脚本,克隆程序可以运行该脚本半自动地设置它们)。

要在*nix上执行符号链接,你所需要做的就是:

root="$(pwd)"
ln -s "$root/hooks" "$root/.git/hooks"

如果你准备覆盖.git/hooks中的内容,请使用ln -sf

使用git-hooks。它将.git/hooks调用路由到项目目录githooks下的脚本。

还有很多功能,使您能够最大限度地减少复制和符号链接钩子到处。

如果你的项目是一个JavaScript项目,并且你使用npm作为包管理器,你可以使用shared-git-hooksnpm install上强制Git钩子。

完全披露:这个包是我写的

Git 2.9中 配置选项core.hooksPath指定一个自定义钩子目录

将钩子移动到存储库中的hooks跟踪目录。然后,配置存储库的每个实例以使用跟踪的hooks而不是$GIT_DIR/hooks:

git config core.hooksPath hooks

通常,路径可以是绝对的,或者相对于钩子运行的目录(通常是工作树的根;参见man githooks的DESCRIPTION部分)。

我们使用的Visual Studio解决方案(以及项目)具有构建前和构建后事件。我正在添加一个名为“GitHookDeployer”的额外项目。项目在生成后事件中自我修改文件。该文件被设置为复制到构建目录。因此,项目每次都被构建,并且从未被跳过。在构建事件中,它还确保所有git钩子都到位。

请注意,这不是一个通用的解决方案,因为有些项目当然没有任何东西要构建。

大多数现代编程语言,或者说它们的构建工具,都支持插件来管理Git钩子。这意味着你所需要做的就是配置你的package.jsonpom.xml等文件,你团队中的任何人都没有选择,除非他们更改构建文件。

该插件将为您添加内容到.git目录。

例子:

Git Build Hook Maven Plugin

githook-maven-plugin

git-hooks-js

你可以让你的钩子文件夹成为另一个Git存储库,并将其链接为子模块…

我想,只有当你有很多成员和钩子定期改变时,这是值得的。

你可以使用托管解决方案来进行预提交钩子管理,如未雨绸缪。 或者为服务器端git-hook提供集中式解决方案,如Datree.io。 它有内置的策略,如:

  1. 检测和防止秘密合并
  2. 强制正确的Git用户配置
  3. 强制Jira票务整合 -在拉请求名称/提交消息中提到票号。
它不会替换你所有的钩子,但是它可以帮助你的开发人员使用最明显的钩子,而不用在每个钩子上都安装钩子 开发人员的电脑/回购。< / p >

免责声明:我是Datrees创始人之一

如果遵循示例文件,理想情况下,钩子是用Bash编写的。但是您可以用任何可用的语言编写它,只要确保它具有可执行标志。

因此,您可以编写Python或Go代码来实现您的目标,并将其放在hooks文件夹下。它可以工作,但不能与存储库一起进行管理。

两个选项

a)多脚本

你可以在帮助中编写钩子的代码,并在钩子中添加一小段代码,以调用你的完美脚本,如下所示:

$ cat .git/hooks/pre-commit
#!/bin/bash
../../hooks/myprecommit.js

b)单一脚本

一个更酷的选择是只添加一个脚本来管理所有脚本,而不是多个脚本。因此,你创建了一个钩子/ mysuperhook.go文件,并将你想要拥有的每个钩子指向它。

$ cat .git/hooks/pre-commit
#!/bin/bash
../../hooks/mysuperhook.go $(basename $0)

参数将为你的脚本提供哪个钩子被触发,你可以在你的代码中区分它。为什么?例如,有时您可能希望对提交和推送执行相同的检查。

然后呢?

然后,你可能想要更多的功能,比如:

  • 手动触发钩子,在提交或推送之前检查是否一切正常。如果你只是调用你的脚本(选项a或b)就可以做到这一点。
  • 触发CI上的钩子,这样你就不需要为CI重写相同的检查。例如,它只是调用commit和push触发器。同上面的方法应该可以解决。
  • 调用外部工具,如markdown验证器或YAML验证器。您可以进行系统调用,并需要处理标准输出和标准错误。
  • 确保所有开发人员都有一种简单的方法来安装钩子,因此需要向存储库中添加一个漂亮的脚本,用正确的钩子替换默认的钩子
  • 使用一些全局助手,比如检查以阻止提交以开发和控制分支,而不必将其添加到每个存储库。您可以通过另一个具有全局脚本的存储库来解决这个问题。

还能简单点吗?

是的,有几个工具可以帮助您管理Git钩子。每一种方法都是从不同的角度解决问题,您可能需要了解所有方法,才能找到最适合您或您的团队的方法。GitHooks.com提供了很多关于钩子的阅读,以及一些可用的工具。

到目前为止,这里列出了21个使用不同策略管理Git钩子的项目。有些只针对单个钩子,有些针对特定的语言,等等。

其中一个由我编写并作为开源项目免费提供的工具叫做hooks4git。它是用Python编写的(因为我喜欢它),但其思想是在一个名为.hooks4git.ini的配置文件中处理上面列出的所有项,该配置文件位于存储库中,可以用任何语言调用任何你想调用的脚本。

使用Git钩子绝对很棒,但是提供它们的方式通常只会让人们远离它。

对于node . js用户,一个简单的解决方案是更新package.json with

{
"name": "name",
"version": "0.0.1",
......
"scripts": {
"preinstall": "git config core.hooksPath hooks",

预安装将在此之前运行

npm安装

并将Git重定向到\钩子。(或任何你选择的名称)目录中寻找钩子。这个目录应该在文件名(减去.sample)和结构方面模仿\ . \钩子

想象一下Maven和其他构建工具将具有与预安装等价的对象。

它还应该适用于所有平台。

如果需要更多信息,请参见与你的团队共享Git钩子的两种方法

对于Gradle用户

我发现这些脚本对Gradle项目非常有用。

build.gradle

apply from: rootProject.file('gradle/install-git-hooks.gradle')

gradle / install-git-hooks.gradle

tasks.create(name: 'gitExecutableHooks') {
doLast {
Runtime.getRuntime().exec("chmod -R +x .git/hooks/");
}
}
task installGitHooks(type: Copy) {
from new File(rootProject.rootDir, 'pre-commit')
into { new File(rootProject.rootDir, '.git/hooks') }
}
gitExecutableHooks.dependsOn installGitHooks
clean.dependsOn gitExecutableHooks

未雨绸缪

.... your pre commit scripts goes here

我想把几个答案合并成一个。假设你在你的project/目录下:

设置自定义钩子

  1. 创建.githooks目录并将钩子放在其中。(例子见.git/hooks)

  2. 创建一个指向目录¹.gitconfig文件:

    git config -f .gitconfig core.hooksPath .githooks
    
  3. Makefile中创建以下规则

    enable-git-hooks:
    git config --local include.path ../.gitconfig
    $(warning REMEMBER, YOU MUST HAVE REVIEWED THE CUSTOM HOOKS!)
    

启用自定义钩子

每个开发人员都应该在检查这些自定义钩子之后显式地启用它们。在README中添加一个指令,就像这样:

通过make enable-git-hooks检查自定义钩子后启用它们。

我目前正在我们的代码库中工作,我遇到了一个名为husky的库,它简化了如何在你的团队中使用和共享GitHub Hooks。我强烈建议你调查一下。

灵感来自a3765910的回答 for Gradle,但修改为运行每个构建。

将以下内容添加到应用程序的构建中。Gradle(假设你已经创建了你想要保存在源代码控制中的githooks/pre-commit文件):

copy {
from new File(rootProject.rootDir, 'githooks/pre-commit')
into { new File(rootProject.rootDir, '.git/hooks') }
}

要对hooks目录进行版本化(假设它存储在hooks/中),在工作副本根目录下创建一个包含以下内容的.gitconfig文件:

[core]
hooksPath = hooks

它将覆盖.git/config