Skip to content

git hooks

约 2009 字大约 7 分钟

git

2025-01-18

Git Hooks 是一种强大的机制,用于在 Git 的特定事件发生时触发自定义脚本。通过 Git Hooks,可以在提交代码、合并分支或推送代码等操作之前或之后运行特定的脚本,来实现自动化的工作流、质量检查或其他定制需求。

使用

Git Hooks 位于每个 Git 仓库的 .git/hooks 目录中。在这个目录中,你可以找到一些示例脚本,它们都以 .sample 结尾。这些示例脚本可以用于了解 Git Hooks 的基本结构和用法。

要启用一个 Git Hook,你需要将 .sample 后缀去掉。例如,要启用 pre-commit Hook,你需要将 pre-commit.sample 重命名为 pre-commit。一般我们会使用其他的三方库来实现一些常用的hooks,如:pre-commit

我们来看一下示例文件里面的内容:

$ cd .git/hooks
$ ll
total 120
-rwxr-xr-x  1 matias  staff   478B Jan 16 18:13 applypatch-msg.sample
-rwxr-xr-x  1 matias  staff   896B Jan 16 18:13 commit-msg.sample
-rwxr-xr-x  1 matias  staff   4.6K Jan 16 18:13 fsmonitor-watchman.sample
-rwxr-xr-x  1 matias  staff   189B Jan 16 18:13 post-update.sample
-rwxr-xr-x  1 matias  staff   424B Jan 16 18:13 pre-applypatch.sample
-rwxr-xr-x  1 matias  staff   1.6K Jan 16 18:13 pre-commit.sample
-rwxr-xr-x  1 matias  staff   416B Jan 16 18:13 pre-merge-commit.sample
-rwxr-xr-x  1 matias  staff   1.3K Jan 16 18:13 pre-push.sample
-rwxr-xr-x  1 matias  staff   4.8K Jan 16 18:13 pre-rebase.sample
-rwxr-xr-x  1 matias  staff   544B Jan 16 18:13 pre-receive.sample
-rwxr-xr-x  1 matias  staff   1.5K Jan 16 18:13 prepare-commit-msg.sample
-rwxr-xr-x  1 matias  staff   2.7K Jan 16 18:13 push-to-checkout.sample
-rwxr-xr-x  1 matias  staff   3.6K Jan 16 18:13 update.sample

看一下pre-commit.sample 文件的内容:

$ cat pre-commit.sample
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

if git rev-parse --verify HEAD >/dev/null 2>&1
then
        against=HEAD
else
        # Initial commit: diff against an empty tree object
        against=$(git hash-object -t tree /dev/null)
fi

# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --type=bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
        # Note that the use of brackets around a tr range is ok here, (it's
        # even required, for portability to Solaris 10's /usr/bin/tr), since
        # the square bracket bytes happen to fall in the designated range.
        test $(git diff --cached --name-only --diff-filter=A -z $against |
          LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
        cat <<\EOF
Error: Attempt to add a non-ASCII file name.

This can cause problems if you want to work with people on other platforms.

To be portable it is advisable to rename the file.

If you know what you are doing you can disable this check using:

  git config hooks.allownonascii true
EOF
        exit 1
fi

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

编写pre-commit文件,即可实现commit实现一些自动化的功能。

实例

Python项目中添加git commit是对.py文件自动校验、自动修复、头文件自动整理、依赖文件自动生成。

我们使用pre-commit来实现挂载。

安装pre-commit

$ pip3 install pre-commit

编写pre-commit的配置文件.pre-commit-config.yaml

repos:
  - repo: https://github.com/pycqa/isort
    rev: 5.13.2
    hooks:
      - id: isort
  - repo: https://github.com/pre-commit/mirrors-autopep8
    rev: 'v2.0.4'
    hooks:
      - id: autopep8
        # autopep8 only takes one filename as argument unless the "--in-place" or "--diff" args are used
        args: [--max-line-length=120, --diff]
  - repo: https://github.com/PyCQA/flake8
    rev: 7.0.0
    hooks:
      - id: flake8
        args:
            - --max-line-length=120
  - repo: local
    hooks:
      - id: auto-requirements
        name: auto requirements
        entry: /bin/zsh ./hooks/auto-requirements.sh
        language: system
        pass_filenames: false
        always_run: true

这里添加了几个pre-commit的钩子,分别是isortautopep8flake8auto-requirements

  • isort:对.py文件进行头文件自动整理。
  • autopep8:对.py文件进行格式自动修复。
  • flake8:对.py文件进行格式校验。
  • auto-requirements:在git commit时自动生成依赖文件。

只有auto-requirements是我们自定义的,其他都是比较常用的三方库。如何自定义可以参考Python项目如何优雅的生成依赖文件

提示 这里我们在.pre-commit-config.yaml中只添加一些基础的设置,更多设置等着我们去探索。

注意 .pre-commit-config.yaml文件里面的配置有一个细节,我们是先设置autopep8帮我们修复错误,再使用flake8去校验。这个顺序重要吗?

添加pre commitgit钩子中

执行如下命令:

$ pre-commit install 
pre-commit installed at .git/hooks/pre-commit

添加完之后,我们看一下.git/hooks下面的内容:

$ ll
total 128
-rwxr-xr-x  1 matias  staff   478B Jan 16 18:13 applypatch-msg.sample
-rwxr-xr-x  1 matias  staff   896B Jan 16 18:13 commit-msg.sample
-rwxr-xr-x  1 matias  staff   4.6K Jan 16 18:13 fsmonitor-watchman.sample
-rwxr-xr-x  1 matias  staff   189B Jan 16 18:13 post-update.sample
-rwxr-xr-x  1 matias  staff   424B Jan 16 18:13 pre-applypatch.sample
-rwxr-xr-x  1 matias  staff   632B Jan 18 13:46 pre-commit
-rwxr-xr-x  1 matias  staff   1.6K Jan 16 18:13 pre-commit.sample
-rwxr-xr-x  1 matias  staff   416B Jan 16 18:13 pre-merge-commit.sample
-rwxr-xr-x  1 matias  staff   1.3K Jan 16 18:13 pre-push.sample
-rwxr-xr-x  1 matias  staff   4.8K Jan 16 18:13 pre-rebase.sample
-rwxr-xr-x  1 matias  staff   544B Jan 16 18:13 pre-receive.sample
-rwxr-xr-x  1 matias  staff   1.5K Jan 16 18:13 prepare-commit-msg.sample
-rwxr-xr-x  1 matias  staff   2.7K Jan 16 18:13 push-to-checkout.sample
-rwxr-xr-x  1 matias  staff   3.6K Jan 16 18:13 update.sample

可以看出,对比之前多了一个pre-commit文件,我们看看里面的内容:

$ cat pre-commit       
#!/usr/bin/env bash
# File generated by pre-commit: https://pre-commit.com
# ID: 138fd403232d2ddd5efb44317e38bf03

# start templated
INSTALL_PYTHON=/Users/matias/.pyenv/versions/3.8.10/bin/python3.8
ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-commit)
# end templated

HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")

if [ -x "$INSTALL_PYTHON" ]; then
    exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}"
elif command -v pre-commit > /dev/null; then
    exec pre-commit "${ARGS[@]}"
else
    echo '`pre-commit` not found.  Did you forget to activate your virtualenv?' 1>&2
    exit 1
fi

这就是pre-commit自动生成的文件。看一下ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-commit),我们就知道,git commit时,会执行这个文件,pre-commit会根据.pre-commit-config.yaml文件中的配置,执行相应的钩子。

如果已经添加过,但是更新了.pre-commit-config.yaml文件,可以直接更新。

$ pre-commit autoupdate

或者,清除或卸载之后再添加。

$ pre-commit clean
$ pre-commit uninstall
$ pre-commit install

执行完上面的命令后,pre commit就添加到git钩子中了。后续执行git commit命令时,就会触发pre commit,然后自动执行我们的配置。

$ git commit -m '
quote> feat: - 实现接口转发到微软必应最新背景图功能'
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/mirrors-autopep8.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/PyCQA/flake8.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
isort....................................................................Failed
- hook id: isort
- files were modified by this hook

Fixing /Users/matias/matias/MT/MTGithub/bingwallpaper/app/errors/app_error.py
Fixing /Users/matias/matias/MT/MTGithub/bingwallpaper/app/errors/http_error.py
Fixing /Users/matias/matias/MT/MTGithub/bingwallpaper/app/errors/validation_error.py
Fixing /Users/matias/matias/MT/MTGithub/bingwallpaper/app/router/wallpaper/api.py
Fixing /Users/matias/matias/MT/MTGithub/bingwallpaper/app/utils/time_util.py
Fixing /Users/matias/matias/MT/MTGithub/bingwallpaper/main.py

autopep8.................................................................Passed
flake8...................................................................Passed
auto requirements........................................................Failed
- hook id: auto-requirements
- files were modified by this hook

添加完成之后,提交代码时就会自动执行pre-commit,如果pre-commit校验不通过,则无法提交代码。

如上我们看到isortauto requirements两个任务失败了,就能知道我们的头文件导入被isort自动调整了,依赖项更新了,但是我们没手动更新依赖项,auto-requirements为我们自动更新了依赖项。

整理头文件

使用isortgit commit时,整理头文件。

更多参考:整理头文件

格式校验

Python中我们可以使用flake8来对我们的.py文件进行格式校验,可以集成在git hooks中,通过校验后才能提交。

更多参考:Python 格式校验

格式自动修复

Python中我们可以使用autopep8来对我们的.py文件进行一些格式的自动修复,可以修复一些flake8校验出的问题。

更多参考:Python 格式自动修复

生成依赖文件

Python项目中,在git commit时自动生成依赖文件。

更多参考:Python项目如何优雅的生成依赖文件

参考

Git Hooks