simple-pre-commit的设计与golang实现

背景与动机

在项目开发流程中,代码质量和团队协作是影响项目发版成功的关键因素之一,随着项目规模的扩大、版本的迭代、团队成员的增加,代码库的维护会变得愈发复杂。为此,开发团队常常通过各种工具来进行自动化代码检查、自动化代码格式化等质量控制措施。Gitpre-commit hook就是其中一种方案,它要求在本地代码提交之前执行指定的校验任务,确保提交的代码符合团队代码规范、质量标准。本文探讨的是一个简化版的pre-commit钩子设计方案与golang代码实现。

设计

simple-pre-commit的流程为:

  1. 在项目中配置Makefile文件,定义自动化的任务

比如下面这个例子:

sql 复制代码
install:
  go mod tidy
  go install github.com/brenner8023/simple-pre-commit@latest
  simple-pre-commit
test:
  go test --race -v
pre-commit:
  @make test
  1. 要求团队成员在初次将远程仓库拉取到本地后执行make installmake install任务中包含了simple-pre-commit的执行,会往本地项目的.git/hooks中注入pre-commit

  2. 此后,开发者每次在本地进行提交之前都会触发pre-commit钩子,执行make pre-commitmake pre-commit中具体执行哪些任务由团队制定。

实现细节

首先,需要检查项目配置,检查项目是否存在Makefile文件,Makefile文件中是否编写了pre-commit的配置

go 复制代码
func CheckConfig() error {
    // 读取Makefile文件
    file, err := os.Open("Makefile")
    if err != nil {
        return err
    }
    // 用于延迟执行一个函数,直到包含该defer语句的函数返回时才执行。
    // 通常用于确保在函数执行结束时释放资源
    defer file.Close()
    // 编译正则表达式
    preCommitReg := regexp.MustCompile(`^pre-commit\s*:`)
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        // 逐行读取内容
        line := scanner.Text()
        // 使用表达式进行匹配
        if preCommitReg.MatchString(line) {
                return nil
        }
    }
    msg := fmt.Sprintf("pre-commit config not found in Makefile")
    return errors.New(msg)
}

找到.git目录

go 复制代码
func GetGitProjectRoot() (string, error) {
    // 从当前工作目录开始查找
    startDir, err := os.Getwd()
    if err != nil {
        return "", err
    }
    parentDir := ""
    for {
        gitDir := filepath.Join(startDir, ".git")
        // 判断是否有.git的信息
        if info, err := os.Stat(gitDir); err == nil && info.IsDir() {
            return startDir, nil
        }
        // 找不到就往上一级找
        parentDir = filepath.Dir(startDir)
        if startDir == parentDir {
            msg := fmt.Sprintf(".git folder not found")
            return "", errors.New(msg)
        }
        startDir = parentDir
    }
}

创建pre-commit文件并往该文件写入内容

go 复制代码
func SetHook(gitRoot string) error {
    hookFile := filepath.Join(gitRoot, ".git", "hooks", "pre-commit")
    command := `make pre-commit`
    // 创建pre-commit文件,若文件已存在,文件内容会被清空
    file, err := os.Create(hookFile)
    if err != nil {
        return err
    }
    defer file.Close()
    // 写入任务指令
    _, err = file.WriteString(command)
    if err != nil {
        return err
    }
    err = os.Chmod(hookFile, 0755)
    if err != nil {
        return err
    }
    msg := fmt.Sprintf("%s: Successfully set git hooks", APP_NAME)
    fmt.Println(msg)
    return nil
}

总结

simple-pre-commit是一个轻量级的Git pre-commit钩子管理工具,旨在通过简单的Makefile配置,帮助开发团队在本地代码提交前进行自动化检查,读者可以通过本文了解基本的Git hook工作原理,学习到基本的golang代码语法。

相关推荐
16年上任的CTO3 小时前
git进阶--6---git stash
git·git stash
烛阴4 小时前
Go语言中如何优雅实现单例模式
后端·go
韦德说6 小时前
【开源事故】77.7K Star 的 Hugo 作者亲自回信!但他第一句话就让我彻底慌了……
后端·开源·go
gopher_looklook9 小时前
Go泛型实战:打造优雅的切片工具库
后端·go
计算机毕设定制辅导-无忧学长10 小时前
Git 的安装与基本配置
git
安冬的码畜日常10 小时前
【工欲善其事】利用 DeepSeek 实现复杂 Git 操作:从原项目剥离出子版本树并同步到新的代码库中
git·ai·powershell·cherry-pick·deepseek·deepseek r1·deepseek v3
利刃大大11 小时前
【Git】一、初识Git && Git基本操作详解
大数据·git·elasticsearch
16年上任的CTO11 小时前
t基础使用--6---git常用命令
git·git常用命令
16年上任的CTO1 天前
it基础使用--5---git远程仓库
git·git远程仓库