Git技巧:Pre-commit

引言

在软件开发过程中,保持代码的一致性和高质量是非常重要的。pre-commit 是一个强大的工具,它可以帮助我们在提交代码到版本控制系统(如 Git)之前自动运行一系列的代码检查和格式化操作。通过这种方式,我们可以确保每次提交的代码都是干净的、格式化的并且符合项目的编码标准。

本文将详细介绍 pre-commit 的工作原理、如何安装 pre-commit,如何配置它,并提供一些实用的示例。

什么是 Hook?

在版本控制系统中,hook 是一种脚本,用于在某些事件发生时触发。例如,在 Git 中,你可以设置一个 hook 在提交前运行。pre-commit 利用了 Git 的这一特性,允许用户定义一组钩子(hook),这些钩子会在提交之前自动执行。

Hook 的类型

pre-commit 支持以下几种类型的 hook:

  • pre-commit:这是最常用的一种 hook,它会在提交之前运行。
  • pre-push:这种 hook 会在 push 操作之前运行。
  • post-merge:这种 hook 会在合并分支之后运行。
  • prepare-commit-msg:这种 hook 会在创建提交信息时运行。

本文主要关注 pre-commit 类型的 hook。

安装

在开始之前,请确保环境中已经安装了 Python 和 Git。通过以下命令安装 pre-commit

pip install pre-commit

配置

一旦安装完成,接下来就是配置 pre-commit 的过程。
首先,在你的项目根目录下创建一个 .pre-commit-config.yaml 文件。
这个文件定义了你的 pre-commit 钩子的行为。

示例配置文件

下面是一个简单的 .pre-commit-config.yaml 文件示例,它包含了几个常用的钩子:

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: debug-statements
  - repo: https://github.com/psf/black
    rev: stable
    hooks:
      - id: black
  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8

这里,我们定义了四个来自 pre-commit-hooks 仓库的钩子,以及两个分别来自 blackflake8 的钩子。这些钩子的功能分别是:

  • trailing-whitespace: 移除行尾的空白字符。
  • end-of-file-fixer: 确保文件以单个新行结束。
  • check-yaml: 检查 YAML 文件的格式是否正确。
  • debug-statements: 检查 Python 代码中的调试语句(如 print)。
  • black: 自动格式化 Python 代码。
  • flake8: 进行 Python 代码的静态分析和格式检查。

选择 Hook

选择合适的 hook 来满足对应的项目需求

  1. 编程语言规范:选择与项目编程语言兼容的 hook。例如,对于 Python 项目,可以选择 blackflake8
  2. 编码标准:选择符合项目编码标准的 hook。例如,如果项目使用 PEP 8 编码标准,可以使用 flake8
  3. 自动化需求:选择能够自动修复常见错误的 hook。例如,end-of-file-fixer 可以自动添加缺失的新行。

限制 Hook 的作用范围

你可以通过以下方式限制 hook 的作用范围:

  • 使用 types:限制 hook 应用于特定类型的文件。例如,只对 .py 文件应用 black

    - repo: https://github.com/psf/black
      rev: stable
      hooks:
        - id: black
          types: [python]
    
  • 使用 exclude:排除特定的文件或目录。例如,不检查 .venv 目录下的文件:

    - repo: https://github.com/pre-commit/pre-commit-hooks
      rev: v4.4.0
      hooks:
        - id: trailing-whitespace
          exclude: ^\.venv/
    
  • 使用 files:只检查匹配正则表达式的文件。例如,只检查以 test_ 开头的 Python 文件:

    - repo: https://github.com/psf/black
      rev: stable
      hooks:
        - id: black
          files: ^test_.*\.py$
    

安装钩子

创建完配置文件后,需要安装这些钩子才能让它们生效。在项目根目录下运行以下命令:

pre-commit install

这将在你的 .git/hooks 目录下生成一个可执行的 pre-commit 脚本。

如果之后有修改,不需要再次安装,会自动采用最新的yaml或者自定义hooks来运行。

使用 pre-commit

现在当你尝试提交更改时,pre-commit自动运行配置好的钩子。如果任何钩子失败,则提交会被阻止,并显示错误信息。

如果你想要手动运行所有的钩子,可以使用以下命令:

pre-commit run --all-files

如果你只想运行特定的一个钩子,可以这样做:

pre-commit run black --files path/to/file.py

自定义 Hook

除了使用已有的 hook 之外,pre-commit 还允许你定义自己的 hook。例如,假设你有一个 Python 脚本 my_custom_hook.py,它可以检查文件中的特定模式:

# my_custom_hook.py
import sys
import re

def main():
    pattern = r'pattern_to_search'
    for filename in sys.argv[1:]:
        with open(filename, 'r') as file:
            if any(re.search(pattern, line) for line in file):
                print(f"Found pattern in {filename}")
                return 1
    return 0

if __name__ == '__main__':
    sys.exit(main())

然后,你可以在 .pre-commit-config.yaml 文件中添加这个自定义钩子:

repos:
  - repo: local
    hooks:
      - id: custom-pattern-check
        name: Custom pattern check
        entry: python my_custom_hook.py
        language: system
        files: \.py$

使用了 local 作为 repo,因为这个钩子是直接在项目中定义的。

Eg:commit消息规范

希望每一次commit的message是符合一定的规则的,首先创建一个自定义的hook,写一个python脚本

# commit_message_validator.py
import sys
import re

def validate_commit_message(message):
    # 第一行以大写字母开头
    if not message.startswith(tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ')):
        print("Error: Commit message must start with a capital letter.")
        return False

    # 第一行不能超过 50 个字符
    if len(message.split('\n')[0]) > 50:
        print("Error: The first line must not exceed 50 characters.")
        return False

    # 检查是否包含关键字
    valid_types = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert']
    type_pattern = re.compile(r'^(' + '|'.join(valid_types) + ')')
    if not type_pattern.match(message.split(':')[0].split(' ')[0]):
        print("Error: Commit message should start with one of the following keywords: ", ', '.join(valid_types))
        return False

    return True

if __name__ == "__main__":
    message = sys.stdin.read().strip()
    if not validate_commit_message(message):
        sys.exit(1)

将该自定义的hook写入到yaml文件中
.pre-commit-config.yaml

repos:
  - repo: local
    hooks:
      - id: commit-message-validator
        name: Validate commit messages
        entry: python commit_message_validator.py
        language: system
        files: ''
        stages: [commit-msg]

stages: [commit-msg],告诉 pre-commit 这个 hook 应该在 commit-msg 阶段运行,即在提交消息被创建之后,但实际提交还没有发生之前
最后就是安装这一个hook

pre-commit install --hook-type commit-msg

结论

使用 pre-commit,可以确保每次提交的代码都是高质量的。这也就可以帮助我们避免常见的编码错误,还可以提高团队成员之间的协作效率。
无论你是个人开发者还是团队的一员,pre-commit 都是一个值得使用的工具。
笔者记录pre-commit的一大原因也是希望能够在笔记本仓库中,规范每次commit message的内容,让commit message起到一个更好的摘要作用。

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值