zoukankan      html  css  js  c++  java
  • git 的 hook 操作

    一、git hook

    和其它版本控制系统一样,Git 能在特定的重要动作发生时触发自定义脚本。有两组这样的钩子:客户端钩子和服务器钩子。客户端钩子由诸如提交和合并这样的操作所调用,而服务器端钩子作用于诸如接收被推送的提交这样的联网操作。

    钩子都被存储在 Git 目录下的 hooks 子目录中。 也即绝大部分项目中的 .git/hooks,默认存在的都是示例,其名字都是以 .sample 结尾,如果你想启用它们,得先移除这个后缀。把一个正确命名且可执行的文件放入 Git 目录下的 hooks 子目录中,即可激活该钩子脚本。这样一来,它就能被 Git 调用。

    关于各种详细的 hook 类型可以参考官方文档 《自定义 Git - Git 钩子》

    了解了这些 hook 钩子,你就可以真的为所欲为了,你可以用来检查消息、检查代码,可以用来触发任意流程,譬如自动规范检查等等,只能说想象空间巨大无比。

    二、commit msg 格式自动检查

    虽然有很多现成的 hook 可用,但是这里还是给出一个简单的例子演示下,这里实现一个提交 message 格式的简单检查,要求提交消息单行且分两部分,且有一定的字数限制。

    2.1 编写 commit-msg 脚本

    #!/usr/bin/env python
    # coding=utf-8
    #
    # commit msg check
    import sys
    import re
    import io
    
    if hasattr(sys.stdout, 'buffer'):
        sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    
    TIPS_INFO = '''
    不符合commit规范,提交失败(当前状态等于没做刚刚的commit操作)!
    
    commit规范:
    类型 详细消息
    
    规范样例:
    git commit -m "xxxxx xxxxxxxxxxxxx"
    
    !!!!提交失败!!!!
    '''
    
    def check_commit_line1_format(msg):
        regOther = r'S{5,} (.){10,100}'
        matchObj = re.match(regOther, msg)
        return matchObj
    
    if __name__=="__main__":
        with open(sys.argv[1], 'r') as f:
            for line in f:
                if (check_commit_line1_format(line)):
                    sys.exit(0)
                else:
                    print(TIPS_INFO)
                    sys.exit(1)
    

    既然编写好了 commit hook 脚本,那新问题就来了,本地 hook 在项目的 .git 目录下,.git 目录又不受版本控制,所以在团队推广时,你不可能让大家主动去放这个文件,一方面可能会放错,另一方面可能有些人压根就不放,爱理不理,故而需要将这件事做成自动的,所以选择在编译项目时为拷贝切入点(因为你拽下来的项目一定会编译的)。对于 android 项目来说,我们可以使用 gradle 编写一个小任务来做这件事,具体如下:

    /**
     * git-hook-copy.gradle 文件
     * 
     * 本地项目 git hook 自动拷贝脚本
     * 用法:
     * apply from: 'git-hook-copy.gradle'
     */
    
    /**
     * 紧急开关
     */
    def forbid = false
    
    project.afterEvaluate {
        if (forbid) {
            preBuild.dependsOn 'resetGitHookConfig'
        } else {
            preBuild.dependsOn 'prepareGitHookConfig'
        }
    }
    
    task prepareGitHookConfig(type: Copy) {
        from getConfigFile()
        into getGitHookDir()
    }
    
    task resetGitHookConfig {
        doFirst {
            File file = getGitHookFile('commit-msg')
            if (file != null) {
                file.delete()
            }
        }
    }
    
    def getGitHookFile(fileName) {
        def dirPath = getGitHookDir()
        if (dirPath != null && dirPath.length() > 0) {
            def file = new File(dirPath, fileName)
            if (file.exists()) {
                return file
            }
        }
        return null
    }
    
    def getConfigFile() {
        File configFile = new File(project.rootDir, "git-hook/commit-msg")
        if (configFile.exists()) {
            return configFile.absolutePath
        }
        return null
    }
    
    def getGitHookDir() {
        File gitHookDir = new File(project.rootDir, ".git/hooks/")
        if (!gitHookDir.exists()) {
            println("Your project can't find .git directory in the ${project.rootDir.absolutePath}," +
                    " please ensure it have been tracked by git VCS!")
            return null
        }
        return gitHookDir.absolutePath
    }
    

    上面脚本有两个任务,一个 reset,一个 config。reset 会将 .git hook 目录下的规则删掉,等于没有规则;config 是把项目根目录下 git-hook 目录下的 commit-msg hook 脚本复制到 .git hook 目录下,这里不用判断是否已经存在文件,直接覆盖即可,因为 gradle task 天生支持 UPDATE 机制,而且我们需要在修改 commit-msg 文件后自动覆盖,所以不建议判断 .git hook 下是否已经存在。

    至此,我们可以进行一波操作了,譬如在命令行提交代码,你会看到如下提示:

    修复 log 格式后再进行 commit 即可。

    三、总结

    上面简单介绍和实战了一个小的 git hook 操作,感兴趣你可以无限想象,和你的 checkstyle 什么的,各种检查什么的结合起来都可以,反正师傅领进门,修行靠自己,需求靠团队。



    参考链接:

  • 相关阅读:
    [转] 面向对象软件开发和过程(六)针对契约设计
    [转] 面向对象软件开发和过程(三)案例实战(下)
    条形码字体下载
    [转] 面向对象软件开发和过程(五)优化代码的组织
    JQuery动画效果
    实时监听文本框状态的方法
    JQuery操作元素的属性与样式及位置
    AJAX XMLHttpRequest对象
    JQuery常用方法技巧
    CSS弹出二级菜单
  • 原文地址:https://www.cnblogs.com/jmcui/p/12785518.html
Copyright © 2011-2022 走看看