前言

使用 git 的同学想必都有这样的工作场景 - 保证生产环境的 ci 不挂。也就是检查 python 是否符合 pep8/csslint/jslint/pylint/pyflake8 等. 我在我的 emacs 配置中加入了这一项 py-autopep8 , 就是在保存缓存区的时候把当前缓存区的文本放到一个临时文件,然后执行 autopep8, 再检查 pep8/flake8

但是不能对 css/js/html 做规范检查。而且也不通用。周末看到了 Yelp 的 pre-commit . 感觉是个很有意思的东西,虽然之前也写过类似的 hook. 但是没有它灵活。看完他的源码后,我今天给大家介绍下这个东西

pre-commit

玩过 svn/git 的同学应该都知道他们有各种的 hook. 也就是准备 / 完成什么事件的时候做些额外的工作。一般是 shell 脚本,版本控制工具会判断脚本的退出码,如果不是 0, 就不会继续完成. pre-commit 顾名思义就是在 commit 之前做的准备,也就是每次执行

git commit -m 'xxx'

的时候去做一些检查。启用的插件都放到这个版本库目录的根目录下,名字叫做.pre-commit-config.yaml -> 详细文档请看: http://pre-commit.com/

这里有我的一个配置例子:

-   repo: https://github.com/pre-commit/pre-commit-hooks
    sha: b03733bc86d9e8b2564a5798ade40d64baae3055
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: autopep8-wrapper
    args: ['-i', '--ignore=E265,E309,E501']
    -   id: check-docstring-first
    -   id: check-json
    -   id: check-yaml
    -   id: debug-statements
    -   id: name-tests-test
    -   id: requirements-txt-fixer
    -   id: flake8
-   repo: https://github.com/pre-commit/pre-commit
    sha: 86c99c6b870a261d2aff0b4cdb36995764edce1b
    hooks:
    -   id: validate_config
    -   id: validate_manifest
-   repo: https://github.com/asottile/reorder_python_imports
    sha: ea9fa14a757bb210d849de5af8f8ba2c9744027a
    hooks:
    -   id: reorder-python-imports

安装使用

pip install pre-commit
pre-commit install
# PS: 第一次执行commit会比较慢,因为他会clone对应的源, 之后就会用这个缓存的源
# 其他的可选源和用法直接参照[https://github.com/pre-commit](https://github.com/pre-commit)里面的项目或者[http://pre-commit.com/hooks.html](http://pre-commit.com/hooks.html)

看一个失败的例子 (有颜色效果,不能展示出来)

$git commit -m 'test'

Trim Trailing Whitespace.................................................................................................................Passed
Fix End of Files.........................................................................................................................Passed
autopep8 wrapper.........................................................................................................................Passed
Check docstring is first.................................................................................................................Passed
Check JSON..........................................................................................................(no files to check) Skipped
Check Yaml..........................................................................................................(no files to check) Skipped
Debug Statements (Python)................................................................................................................Passed
Tests should end in _test.py........................................................................................(no files to check) Skipped
Fix requirements.txt................................................................................................(no files to check) Skipped
Flake8...................................................................................................................................Failed
hookid: flake8

pre_commit/__init__.py:2:1: F401 'os' imported but unused
pre_commit/__init__.py:3:1: F401 'sys' imported but unused

Validate Pre-Commit Config..........................................................................................(no files to check) Skipped
Validate Pre-Commit Manifest........................................................................................(no files to check) Skipped
Reorder python imports...................................................................................................................Passed
# 因为我的flake8有问题 所以commit失败了

pre-commit 的问题

我觉得对每次 commit 做一次审查,第一是需要时间,第二是没有必要,因为经常一个 pr 有多个 commit, 我只保证整体结果是正确的就好了 - 也就是说应该是在 push 的时候。整个过程我可能对 commit 做多次 rebase/--amend 等等。某一次的检查失败其实完全不 影响我做后的结果 - 我是手快党

so. 我基于它修改了一个版本 pre-push , 只是我对 push 做了拦截。并且我会经常和它保持同步

pre-commit install -t pre-commit # 默认安装pre-commit钩子, 每次commit触发
pre-commit install -t pre-push # 默认安装pre-push钩子, 每次push触发

其他用法完全一样.

假如 push 的时候想要不检查而强制 push, 可以加上--no-verify参数

Update from 2015-01-15

我的这个分支已经合并到 pre-commit . pull189

大家可以不要用我的分支了. PS: 这是我见到测试覆盖最高的项目.