和其它版本控制系统同样,Git 能在特定的重要动做发生时触发自定义脚本。有两组这样的钩子:客户端钩子和服务器钩子。客户端钩子由诸如提交和合并这样的操做所调用,而服务器端钩子做用于诸如接收被推送的提交这样的联网操做。python
钩子都被存储在 Git 目录下的 hooks 子目录中。 也即绝大部分项目中的 .git/hooks,默认存在的都是示例,其名字都是以 .sample 结尾,若是你想启用它们,得先移除这个后缀。把一个正确命名且可执行的文件放入 Git 目录下的 hooks 子目录中,便可激活该钩子脚本。这样一来,它就能被 Git 调用。android
关于各类详细的 hook 类型能够参考官方文档 《自定义 Git - Git 钩子》。git
了解了这些 hook 钩子,你就能够真的随心所欲了,你能够用来检查消息、检查代码,能够用来触发任意流程,譬如自动规范检查等等,只能说想象空间巨大无比。服务器
虽然有不少现成的 hook 可用,可是这里仍是给出一个简单的例子演示下,这里实现一个提交 message 格式的简单检查,要求提交消息单行且分两部分,且有必定的字数限制。app
#!/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 编写一个小任务来作这件事,具体以下: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 下是否已经存在。ui
至此,咱们能够进行一波操做了,譬如在命令行提交代码,你会看到以下提示:
lua
修复 log 格式后再进行 commit 便可。命令行
上面简单介绍和实战了一个小的 git hook 操做,感兴趣你能够无限想象,和你的 checkstyle 什么的,各类检查什么的结合起来均可以,反正师傅领进门,修行靠本身,需求靠团队。版本控制
参考连接: