git自定义项目钩子和全局钩子
Posted 得意莫骄傲,失意莫沮丧。
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了git自定义项目钩子和全局钩子相关的知识,希望对你有一定的参考价值。
钩子介绍
自定义钩子分为:项目钩子和全局钩子
钩子脚本:
#!/usr/bin/env python # coding=utf-8 ‘‘‘ 该脚本在pre-receive或post-receive钩子中被调用,也可以直接将该文件作为git的钩子使用 若钩子为shell脚本,则需要加入以下代码调用该脚本: while read line;do echo $line | python $PATH/pre-receive.py done 当用户执行git push的时候会在远程版本库上触发此脚本 该脚本的主要作用:获取用户提交至版本库的文件列表,提交者及时间信息 ‘‘‘ import sys, subprocess import re import os __author__ = "zhanghuiwen" excludPath ="/opt/gitlab/embedded/service/gitlab-shell/custom_hooks/excludes/excludes.txt"; baseGitUrl="http://172.26.0.80:8081" class Trigger(object): def __init__(self): ‘‘‘ 初始化文件列表信息,提交者信息,提交时间,当前操作的分支 ‘‘‘ self.pushAuthor = "" self.pushTime = "" self.fileList = [] self.ref = "" def __getGitInfo(self): ‘‘‘ ‘‘‘ self.oldObject = sys.argv[2] self.newObject = sys.argv[3] self.ref = sys.argv[1] # 跳过排除的项目 def _skipExcludeProjects_(self): ‘‘‘ 跳过扫描的项目 ‘‘‘ rev = subprocess.Popen("pwd", shell=True, stdout=subprocess.PIPE); gitServerRepoPath = rev.stdout.readline(); # 路径‘/var/opt/gitlab/git-data/repositories/alpha/testhook.git‘ paths = gitServerRepoPath.split("repositories"); projectPath = paths[1]; # /alpha/testhook.git rev.stdout.close(); # 读取配置中的文件 lines = open(excludPath, "r"); for line in lines: realLine = line.strip("\n"); result = realLine.replace(baseGitUrl,"") if projectPath.strip(" ").strip("\n") == result.strip(" ").strip("\n"): lines.close() print ("例外项目允许不经过dev和test直接提交") exit(0) else: pass lines.close() # 继续执行 def __getPushInfo(self): ‘‘‘ git show命令获取push作者,时间,以及文件列表 文件的路径为相对于版本库根目录的一个相对路径 ‘‘‘ rev = subprocess.Popen(‘git rev-list ‘ + self.oldObject + ‘..‘ + self.newObject, shell=True, stdout=subprocess.PIPE) pushList = rev.stdout.readlines() pushList = [x.strip() for x in pushList] # 循环获取每次提交的文件列表 for pObject in pushList: p = subprocess.Popen(‘git show ‘ + pObject, shell=True, stdout=subprocess.PIPE) pipe = p.stdout.readlines() pipe = [x.strip() for x in pipe] self.pushAuthor = pipe[1].strip("Author:").strip() self.pushTime = pipe[2].strip("Date:").strip() self.fileList.extend([‘/‘.join(fileName.split("/")[1:]) for fileName in pipe if fileName.startswith("+++") and not fileName.endswith("null")]) uBranch = self.ref.split(‘/‘)[len(self.ref.split(‘/‘)) - 1] print ‘提交分支: %s‘ % uBranch print ‘提交变动from:%s to:%s‘ % (self.oldObject, self.newObject) print ‘提交的commit:%s‘ % pushList # if uBranch == ‘dev‘: # return # 循环获取每次提交的文件列表 for pObject in pushList: # 判断是否是merge commit,如果是merge commit则忽略 gitCatFileCmd = (‘git cat-file -p %s‘) % (pObject) p = subprocess.Popen(gitCatFileCmd, shell=True, stdout=subprocess.PIPE) pipe = p.stdout.readlines() pipe = [x.strip() for x in pipe] i = 0 for branch in pipe: if branch.startswith(‘parent ‘): i += 1 if i >= 2: continue # 如果提交的带上的msg是FIX_MERGE_ERROR则可以通行(避免合错分支引起的问题) msgLine = pipe[-1] print msgLine if msgLine == ‘FIX_MERGE_ERROR‘: continue # if not re.match(r‘^(\w+)-(\d+)‘, msgLine): # print ‘\033[1;35m %s 提交的信息没有带上jira编号,请确认添加 \033[0m‘ % pObject # exit(-1) listCmd = (‘git branch --contains %s‘) % (pObject) p = subprocess.Popen(listCmd, shell=True, stdout=subprocess.PIPE) pipe = p.stdout.readlines() pipe = [x.strip() for x in pipe] print ‘commit:%s->所属分支:%s‘ % (pObject, pipe) # 如果是master分支push提交,必须先提交dev、test if ‘master‘ == uBranch: if ‘dev‘ not in pipe or ‘test‘ not in pipe: print ‘\033[1;35m 合并到master的分支必须先在dev、test上经过验证合并才能提交,具体错误提交的hash:%s \033[0m‘ % pObject exit(-1) elif ‘test‘ == uBranch: if ‘dev‘ not in pipe: print ‘\033[1;35m 合并到test的分支必须先在dev上经过验证合并才能提交,具体错误提交的hash:%s \033[0m‘ % pObject exit(-1) branchs = set() isMaster = True for branch in pipe: branch = branch.replace(‘* ‘, ‘‘) if ‘master‘ == branch: isMaster = False break if ‘test‘ == branch or ‘dev‘ == branch or ‘dev-permission‘ == branch or ‘test-permission‘ == branch: continue # elif uBranch != ‘master‘ and uBranch != ‘test‘ and uBranch != ‘dev‘ and branch != uBranch: # print ‘\033[1;35m 提交失败!你合并提交的分支来自于多个分支,请确认,你的分支%s,其他分支%s \033[0m‘ % (uBranch, branch) # exit(-1) branchs.add(branch) if len(branchs) >= 2 and isMaster: print ‘\033[1;35m 提交失败!你合并提交的分支来自于多个分支,请确认%s \033[0m‘ % pipe exit(-1) def getGitPushInfo(self): ‘‘‘ 返回文件列表信息,提交者信息,提交时间 ‘‘‘ self.__getGitInfo() self._skipExcludeProjects_() self.__getPushInfo() print ‘=========================================‘ print "Time:", self.pushTime print "Author:", self.pushAuthor print "Ref:", self.ref print "Files:", self.fileList print ‘=========================================‘ if __name__ == "__main__": t = Trigger() t.getGitPushInfo()
自定义全局钩子:
全局钩子目录结构:
(注意:excludes目录结构是我们自定义的目录,规则逻辑在update.d/update.py脚本里实现的,非gitlab官方提供功能)
/opt/gitlab/embedded/service/gitlab-shell/custom_hooks
├── excludes
│ └── excludes.txt
└── update.d
└── update.py
钩子目录:
钩子目录: /opt/gitlab/embedded/service/gitlab-shell/custom_hooks ,其中 update.d/update.py 是我们自定义的钩子脚本, 脚本使用python语言。
如何修改全局钩子指定目录?
1.修改 /etc/gitlab/gitlab.rb
配置文件中的配置项:gitlab_shell[‘custom_hooks_dir‘] = "/opt/gitlab/embedded/service/gitlab-shell/custom_hooks"
2. 执行 sudo gitlab-ctl reconfigure 命令来使配置生效。
如何对单个项目排除全局钩子?
在 /opt/gitlab/embedded/service/gitlab-shell/custom_hooks/excludes/excludes.txt 文件中新加一行你要排除的git的地址即可 。
自定义项目钩子
项目钩子的目录为固定目录:
<project>.git/custom_hooks/ 例如我们的项目自定义目录为: /var/opt/gitlab/git-data/repositories/frontend/iLawWeb.git/custom_hooks/update ,update是我们自定义的钩子脚本。
钩子的执行顺序
钩子将按照以下的顺序执行
<project>.git/hooks/
- symlink togitlab-shell/hooks
global dir<project>.git/hooks/<hook_name>
- executed bygit
itself, this isgitlab-shell/hooks/<hook_name>
<project>.git/custom_hooks/<hook_name>
- per project hook (this is already existing behavior)<project>.git/custom_hooks/<hook_name>.d/*
- per project hooks<project>.git/hooks/<hook_name>.d/*
OR<custom_hooks_dir>/<hook_name.d>/*
- global hooks: all executable files (minus editor backup files)
钩子校验规则
1、提交合并代码到test分支前,需要先合并代码到dev验证通过后方能push到test
2、提交合并代码到master分支前,需要先合并代码到dev、test验证通过后方能push到master
3、不同分支不能相互合并(如果需要合并,请合并后删除一个不需要的分支方能提交)
4、合并代码到dev、test、master,merge过程出错了怎么办?
可以直接在dev、test、master分支上修改代码,但是需要在备注comment里填写FIX_MERGE_ERROR 就可以直接提交(但请不要正常的代码也使用这个命令!!!!!否则后果很严重)
以上是关于git自定义项目钩子和全局钩子的主要内容,如果未能解决你的问题,请参考以下文章
服务端CVS本地Git的版本控制:利用git钩子自定义工作流