git hooks
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
的钩子,分别是isort
、autopep8
、flake8
、auto-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 commit
到git
钩子中
执行如下命令:
$ 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
校验不通过,则无法提交代码。
如上我们看到isort
和auto requirements
两个任务失败了,就能知道我们的头文件导入被isort
自动调整了,依赖项更新了,但是我们没手动更新依赖项,auto-requirements
为我们自动更新了依赖项。
整理头文件
使用isort在git commit
时,整理头文件。
更多参考:整理头文件
格式校验
在Python
中我们可以使用flake8
来对我们的.py
文件进行格式校验,可以集成在git hooks
中,通过校验后才能提交。
更多参考:Python 格式校验
格式自动修复
在Python
中我们可以使用autopep8
来对我们的.py
文件进行一些格式的自动修复,可以修复一些flake8
校验出的问题。
更多参考:Python 格式自动修复
生成依赖文件
在Python
项目中,在git commit
时自动生成依赖文件。
更多参考:Python项目如何优雅的生成依赖文件