将 Git 挂钩放入存储库

Posted

技术标签:

【中文标题】将 Git 挂钩放入存储库【英文标题】:Putting Git hooks into a repository 【发布时间】:2011-03-28 15:17:58 【问题描述】:

.git/hooks 放入项目存储库是否被认为是一种不好的做法(例如,使用符号链接)。如果是,向不同 Git 用户提供相同钩子的最佳方式是什么?

【问题讨论】:

您所说的“项目存储库”(或者可能是“项目的存储库”(占有)是什么意思?它是指特定的 IDE 吗?还是别的什么? @PeterMortensen 在这个问题中项目的含义是作为 VCS 根目录存在的东西,可以克隆并开始使用。 在此上下文中使用的术语may have arisen from GitLab。 @PeterMortensen GitLab 于 2014 年推出,而问题是在 2011 年 8 月提出的,因此该术语在 GitLab 存在之前) 【参考方案1】:

我一般同意with Scy,还有一些额外的建议,足以值得单独回答。

首先,您应该编写一个脚本来创建适当的符号链接,尤其是当这些挂钩是关于执行策略或创建有用的通知时。如果人们只需要输入bin/create-hook-symlinks 就更有可能使用这些钩子,而不是必须自己输入。

其次,直接符号链接挂钩可防止用户添加自己的个人挂钩。例如,我更喜欢示例预提交钩子,它确保我没有任何空白错误。解决此问题的一个好方法是在您的存储库中放入一个钩子包装脚本,并将 所有 钩子符号链接到它。

然后包装器可以检查 $0(假设它是一个 Bash 脚本;否则相当于 argv[0])以确定它被调用为哪个钩子,然后在您的存储库中调用适当的钩子,以及适当的用户的钩子,必须重命名,将所有参数传递给每个。快速示例:

#!/bin/bash
if [ -x $0.local ]; then
    $0.local "$@" || exit $?
fi
if [ -x tracked_hooks/$(basename $0) ]; then
    tracked_hooks/$(basename $0) "$@" || exit $?
fi

安装脚本会将所有预先存在的钩子移到一边(将.local附加到它们的名称中),并将所有已知的钩子名称符号链接到上述脚本:

#!/bin/bash
HOOK_NAMES="applypatch-msg pre-applypatch post-applypatch pre-commit prepare-commit-msg commit-msg post-commit pre-rebase post-checkout post-merge pre-receive update post-receive post-update pre-auto-gc"
# assuming the script is in a bin directory, one level into the repo
HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks

for hook in $HOOK_NAMES; do
    # If the hook already exists, is executable, and is not a symlink
    if [ ! -h $HOOK_DIR/$hook -a -x $HOOK_DIR/$hook ]; then
        mv $HOOK_DIR/$hook $HOOK_DIR/$hook.local
    fi
    # create the symlink, overwriting the file if it exists
    # probably the only way this would happen is if you're using an old version of git
    # -- back when the sample hooks were not executable, instead of being named ____.sample
    ln -s -f ../../bin/hooks-wrapper $HOOK_DIR/$hook
done

【讨论】:

我已将 chmod +x .git/hooks/* 添加到您的 bin/create-hook-symlinks 以进行处理。 @guneysus 你不应该需要那个,因为钩子应该已经是可执行的(应该以这种方式检查它们)并且链接不需要任何特殊权限,只需要它们链接到的文件. 获取钩子目录的更好方法是HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks 我已经在此基础上构建了一个简单的系统来管理我项目中的钩子:ell.io/tt$Paws.js/blob/Master/Scripts/install-git-hooks.sh 我只拿了必需品并把它放在了一个 repo github.com/sjungwirth/githooks【参考方案2】:

不,将它们放入存储库就可以了。我什至建议这样做(如果它们对其他人也有用的话)。用户必须明确启用它们(如您所说,例如通过符号链接),这一方面有点痛苦,但另一方面它可以保护用户在未经他们同意的情况下运行任意代码。

【讨论】:

如果这是公司政策的事情,那么代码不是“任意的”这是必需的代码,因此这将被视为 GIT 中的限制,因为没有另一个(预定义)目录,它被跟踪,它也与常规钩子一起执行 自动交付钩子是一个安全问题,我很高兴 Git 没有直接这样做 - 执行团队/公司策略,在服务器端使用钩子或让用户手动决定启用它们正如@scy 所描述的:) “保护用户 [...] 在未经他们同意的情况下运行任意代码”。如果开发人员愿意按照您的建议(符号链接),那么钩子可以由其他人更改,并运行“未经他们同意的任意代码” 小神:当然。如果你足够多疑,你可以复制钩子而不是符号链接,然后审核它们,然后才启用它们。但是,大多数(需要引用)Git 存储库将包含要在用户机器上运行的源代码,因此您可能会运行不断变化的、未经审计的代码。但是,是的,你说得有道理。 ;)【参考方案3】:

现在您可以执行以下操作来将受版本控制的目录设置为您的 Git 挂钩目录,例如,MY_REPO_DIR/.githooks 将是

git config --local core.hooksPath .githooks/

它仍然不能直接强制执行,但是,如果您在自述文件(或其他任何内容)中添加注释,则每个开发人员都需要付出最少的努力。

【讨论】:

我在viget.com/articles/two-ways-to-share-git-hooks-with-your-team 上发现的一个技巧是从您的 Makefile/CMake config/whatever 中设置选项。 请注意,这仅适用于 2.9.0 以上的 git 版本。在发帖时,这不是“事情”。【参考方案4】:

存储在项目中并安装在构建中

正如其他人在回答中所说,如果你的钩子是特定于你的特定项目的,那么将它们包含在项目本身中,由 Git 管理。我会更进一步地说,鉴于使用单个脚本或命令构建项目是一种很好的做法,因此应该在构建期间安装挂钩。

我写了一篇关于managing Git hooks 的文章,如果您有兴趣更深入地阅读此内容。

Java 和 Maven

完全免责声明;我编写了下面描述的Maven 插件。

如果您使用 Maven 为您的 Java 项目处理构建管理,则以下 Maven 插件将处理从项目中的某个位置安装挂钩。

https://github.com/rudikershaw/git-build-hook

将所有 Git 挂钩放在项目中的一个目录中,然后配置您的 pom.xml 以包含以下插件声明、目标和配置。

<build>
  <plugins>
    <plugin>
      <groupId>com.rudikershaw.gitbuildhook</groupId>
      <artifactId>git-build-hook-maven-plugin</artifactId>
      <configuration>
        <gitConfig>
          <!-- The location of the directory you are using to store the Git hooks in your project. -->
          <core.hooksPath>hooks-directory/</core.hooksPath>
        </gitConfig>
      </configuration>
      <executions>
        <execution>
          <goals>
            <!-- Sets git config specified under configuration > gitConfig. -->
            <goal>configure</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
      <!-- ... etc ... -->
  </plugins>
</build>

当您运行项目构建时,该插件会将 Git 配置为在指定目录之外运行挂钩。这将为从事您项目的每个人有效地设置该目录中的挂钩。

javascript 和 NPM

对于 NPM,有一个名为 Husky 的依赖项,它允许您安装钩子,包括用 JavaScript 编写的钩子。

// package.json

  "husky": 
    "hooks": 
      "pre-commit": "npm test",
      "pre-push": "npm test",
      "...": "..."
    
  

其他

此外,还有许多不同的钩子管理应用程序/插件,包括用于 Python 项目的 pre-commit、用于 Ruby 项目的 Overcommit 和用于 Ruby 的 Lefthook Node.js 项目。

【讨论】:

感谢创建这个插件,它让我的预提交文件的集成变得超级容易。 赫斯基真的很棒。我什至参与过使用 Husky 来管理运行 Composer 安装的工具(例如 phpstan 和 phpcs)的预提交挂钩的 PHP 项目,我对这种设置非常满意。据我所知,作曲家并没有完全相同的东西。【参考方案5】:

TEMPLATE DIRECTORY,您可以使用其中一种机制来更新每个新创建的 Git 存储库的 .git/hooks 目录:

模板目录包含的文件和目录将被 创建后复制到 $GIT_DIR。

模板目录将是以下之一(按顺序):

--template 选项给出的参数;

$GIT_TEMPLATE_DIR 环境变量的内容;

init.templateDir 配置变量;或

默认模板目录:/usr/share/git-core/templates.

【讨论】:

【参考方案6】:

pre-commitnpm 包可以优雅地处理这个问题,允许您在 package.json 文件中指定预提交挂钩。

【讨论】:

【参考方案7】:

对于基于PHP Composer的PHP项目,可以自动分发给工程师。这是 pre-commit 和 commit-msg 钩子的示例。

创建一个hooks 文件夹,然后在您的 composer.json 文件中:

 ,
 "scripts": 
     "post-install-cmd": [
         "cp -r 'hooks/' '.git/hooks/'",
         "php -r \"copy('hooks/pre-commit', '.git/hooks/pre-commit');\"",
         "php -r \"copy('hooks/commit-msg', '.git/hooks/commit-msg');\"",
         "php -r \"chmod('.git/hooks/pre-commit', 0777);\"",
         "php -r \"chmod('.git/hooks/commit-msg', 0777);\"",
     ],

然后您甚至可以在项目继续进行时更新它们,因为每个人都在定期运行 composer install

【讨论】:

【参考方案8】:

这是一个脚本,add-git-hook.sh,您可以将其作为存储库中的常规文件提供,并且可以执行以将 Git 挂钩附加到脚本文件。调整使用哪个hook(pre-commit、post-commit、pre-push等)和cat heredoc中hook的定义。

#!/usr/bin/bash
# Adds the git-hook described below. Appends to the hook file
# if it already exists or creates the file if it does not.
# Note: CWD must be inside target repository

HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks
HOOK_FILE="$HOOK_DIR"/post-commit

# Create script file if doesn't exist
if [ ! -e "$HOOK_FILE" ] ; then
        echo '#!/usr/bin/bash' >> "$HOOK_FILE"
        chmod 700 "$HOOK_FILE"
fi

# Append hook code into script
cat >> "$HOOK_FILE" <<EOF

########################################
# ... post-commit hook script here ... #
########################################

EOF

此脚本可能具有可执行权限或用户可以直接运行它。提交后,我用它在其他机器上自动 git-pull。

我回答了一个更简单的问题,这不是被问到的,也不是 OP 想要的。我对在存储库中交付钩子脚本而不是在下面的 cmets 中从外部管理它们的用例和论点提出了意见。

【讨论】:

感谢您的努力,但我相信这里有有价值的信息 - 它不能回答所述问题。 在我看来,如果挂钩是特定于特定存储库的,或者是所用工作流的组成部分,那么它们作为文件属于存储库。很难将它们放在其他任何地方而不会产生比它解决的问题更多的问题。您可以将通用挂钩存储在它自己的存储库或共享驱动器上,这可以使项目存储库保持干净,但代价是不那么实用。我同意其他用户的说法,即挂钩必须易于添加。符号链接可能会对特定系统或文件结构产生不必要的依赖。 此外,符号链接会破坏用户添加自己的钩子的能力。 .git/hooks 目录没有被跟踪,所以源代码应该从存储库开始,并进入 hooks 脚本,而不是相反。我认为反驳的观点是 git 挂钩与工作流或团队而不是项目更相关,因此不属于存储库。根据您的具体用例,您是否更愿意使用相关性较低的钩子污染 git 存储库,还是宁愿放弃一堆复杂性将它们放在其他地方?【参考方案9】:

您可以使用托管解决方案进行预提交挂钩管理,例如 pre-commit。 或者像 Datree.io 这样的服务器端 git-hooks 的集中式解决方案。

它具有以下内置策略:

    检测并阻止merging of secrets。 强制执行正确的Git user configuration。 强制执行Jira ticket integration - 在拉取请求名称/提交消息中提及票号。

它不会取代您所有的钩子,但它可能会帮助您的开发人员使用最明显的钩子,而无需在每个开发人员的计算机/存储库上安装钩子。

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

【讨论】:

我认为您正在制作一个有趣的产品,但我也认为这并不能回答问题,基本上是一种自我推销,仅此而已。

以上是关于将 Git 挂钩放入存储库的主要内容,如果未能解决你的问题,请参考以下文章

使用 Amazon Linux 2 挂钩配置 Elastic Beanstalk 以通过 SSH 访问私有 git 存储库

如何转换 git 存储库中的大量提交 [重复]

git,Heroku:预接收挂钩被拒绝

git-clone 和结帐后挂钩

git-svn 的钩子

如何从 Docker 容器克隆 Git 存储库