1.背景
使用Git做项目的版本控制时,在版本系统中会有很多的代码的提交记录,我们使用git log命令就会得到如下图中的提交记录:
当我们的项目比较简单,规模较小、开发人员也只有一两个的时候,其实可以不用定义代码的提交记录模板,但是当我们的项目开始变得庞大,有很多的开发者都在参与开发的时候,我们会发现每个人提交的信息都是千奇百怪的,出现了一个问题,要定位具体的提交时更是如大海捞针。特别痛苦。所以Git提供了代码提交记录模板,和对提交记录模板的验证,让每个提交代码的开发者按照模板填写提交的信息,提交后再验证下开发者是否按照模板的要求填写提交信息,如果没有就不让其执行接下来的
git push
命令。
2.解决方案
2.1 技术可行性
2.1.1 如何实现
其实当我们创建了一个Git的本地仓库后,项目的根目录下看到一个
.git
文件夹,在文件夹下的
hooks
目录下有很多的
.sample
为后缀的文件,如下图所示
这些文件就是我们要改造的脚本,这个以
.sample
后缀结束的脚本文件是不会执行的,如果需要执行,我们需要去掉
.sample
后缀。我们要实现的功能是定义Git提交模板和校验模板的正确性。所以我们可以参考
prepare-commit-msg.sample
(提交模板)和
commit-msg.sample
(验证模板)完成我们的功能,修改完这个模板后我们就可以使用模板了,当我们需要使用命令
git commit
提交代码时,在出现的提交信息编辑器中会出现我们在
prepare-commit-msg
脚本中定义好的模板,我们填写完模板后保存模板,这时会使用
commit-msg
脚本对模板做校验。这样的话我们的本地提交代码就能使用我们定义好的模板了。
2.1.2 如何同步配置到项目中其他开发者
现在还有一个问题,就是这些配置都是在我们的本地,项目中的其他开发者并没有这个环境,我们如果让他们去配置显然不合适,因为有的人可能不会搭理你。所以我们需要做到让开发者无感知的就装上了我们的环境,原理其实就是我们将模板提交到代码仓库中,然后通过脚本将配置的模板拷贝到用户的".git/hooks"目录下,当用户触发某个操作时,就执行脚本。本文我们以Android项目为例子,使用Gradle脚本,当用户执行构建操作的时候,我们执行配置提交模板的脚本
2.2 实现方案
2.2.1 提交模板的脚本示例
在
.git/hooks
目录下,复制
prepare-commit-msg.sample
文件,重命名为
prepare-commit-msg
(注意这里没有“
.sample
后缀名”),我们定义一个模板:
项目名称: [MountTai] 部门名称: [] 禅道BUGID: [无]
原因分析: {}
解决方案: {}
prepare-commit-msg:
(脚本是用perl语言和shell语言搭配写的,比较简单就不细讲了),逻辑就是在开发者进入编辑器之前,往编辑器写入我们的模板。这样编辑器打开后显示的就是我们写的模板啦。
#!/bin/sh
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
case "$2,$3" in
merge,)/usr/bin/perl -i.bak -ne 's/^/# /,s/^# #/#/if/^Conflicts/../#/;print' "$1";;,|template,)/usr/bin/perl -i.bak -pe 'print"项目名称: [MountTai] 部门名称: [] 禅道BUGID: [无]\n
#部门名称: Android开发部 / 三维软件部 \n\n原因分析: {}\n解决方案: {}\n"if/^#/&&$first++==0' "$1";;*);;
esac
这个文件编写好后,直接放到
.git/hooks
目录下,然后我们执行
git commit
命令后就会出现下图中的模板
提交编辑界面:这里需要重点注意下,如上图所示,在编辑器的最上面会有一个空格,这个空格需要我们手动删除,然后再填写我们的提交信息。若是无法编辑,请将输入法切换为英文,按下键盘的
i
键,进入编辑模式,要保存的时候,切换输入法到英文,然后按下
esc
键,可以按下
shift+zz
组合键直接保存,或者按下
shift + :
进入指令模式,再输入
wq
,回车就行了,放弃则输入
q!
。
删除空格后,第一行会显示黄色,如下图:
2.2.2 校验模板示例
复制
.git/hooks
下的
commit-msg.sample
文件,重命名为
commit-msg
,然后编写模板校验规则,使用正则表达式验证开发者提交的代码信息。下面的逻辑就是获取提交的信息后,使用正则表达式去匹配。成功的话就可以继续提交,否则的话提示用户消息提交格式不合法,重新编辑提交
commit_msg=`cat $1`
msg_re="^(项目名称|部门名称|禅道BUGID|原因分析|解决方案)(\(.+\))?: .{1,100}"if[[!$commit_msg=~$msg_re]]
then
echo -e"不合法的 commit 消息提交格式,请使用正确的格式:\n
详情请查看 git commit 提交规范:https://t3hx1u77li.feishu.cn/docx/CJr7d7aHJofPcYxOA1NcISVBn0d"# 异常退出
exit 1
fi
提交成功会展示:
失败会显示
标出的部分,就是提交规范文档,让开发者可以在失败的时候通过这个文档查看提交规范。
2.2.3 配置同步到项目的其他开发者脚本示例
(1)本地配置好了后,我们就可以把
prepare-commit-msg和commit-msg
两个脚本放到你的项目根目录下
(2)创建一个gradle脚本,做环境拷贝工作
Git配置提交模板和校验模板的gradle脚本示例:
def useGitTemplate =false
project.afterEvaluate {if(useGitTemplate){
preBuild.dependsOn('resetGitHookConfig')}else{println("exec tasks")
preBuild.dependsOn('prepareCommitMsgConfig')}}
task prepareCommitMsgConfig(type:Copy){from(getCommitMsgConfigFile().toString())into(getGitHookDir().toString())
File file =newFile(getGitHookDir())println("GitHookDir: "+getGitHookDir()+" ,permission: "+ file.exists()+" ,readable: "+ file.canRead()+" ,writable: "+ file.canWrite())into(getGitHookDir().toString())from(getPrePareCommitMsgConfigFile().toString())}
task resetGitHookConfig{
doFirst {
File commitMsgFile =getGitHookFile('commit-msg')if(commitMsgFile != null){
commitMsgFile.delete()}
File prepareCommitMsgFile =getGitHookFile('prepare-commit-msg')if(commitMsgFile != null){
prepareCommitMsgFile.delete()}}}defgetGitHookFile(fileName){def dirPath =getGitHookDir()println("getGitHookFile:dirPath: "+ dirPath)if(dirPath != null && dirPath.length()>0){def file =newFile(dirPath, fileName)println("getGitHookFile: file path: "+ file.absolutePath)if(file.exists()){return file
}}return null
}defgetCommitMsgConfigFile(){
File configFile =newFile(project.rootDir,"git-hook/commit-msg")println("getCommitMsgConfigFile: configFile: "+ configFile.absolutePath
+" ,isExist: "+ configFile.exists())if(configFile.exists()){return configFile.absolutePath
}return null
}defgetPrePareCommitMsgConfigFile(){
File configFile =newFile(project.rootDir,"git-hook/prepare-commit-msg")println("getPrePareCommitMsgConfigFile: prepareConfigFile: "+ configFile.absolutePath
+" ,isExist: "+ configFile.exists())if(configFile.exists()){return configFile.absolutePath
}return null
}defgetGitHookDir(){
File gitHookDir =newFile(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
}
脚本的意思就是当我们运行Android的构建时,就会执行我们的这个脚本,把项目中的
prepare-commit-msg
和
commit-msg
脚本拷贝到开发者的
.git/hooks
目录下,然后开发者就可以使用提交模板和验证功能了
提交代码的时候若是使用图形界面,可以把模板复制到界面中提交
Git 模板改造完后,我们的提交记录就会变得很规整了
3.总结
Git Hook技术可以用来实现很多功能,比如在push操作之前想做一些其他操作,和Java的hook技术差不多,都是希望执行某个操作之前或者之后先执行我们定义的操作,使用这个技术可以做代码的规范验证,提交模板的定义,模板的校验等功能,更多的功能后面用到的时候再做分享。
版权归原作者 职场007 所有, 如有侵权,请联系我们删除。