Home > Archives > Git基础之回调(hooks)

Git基础之回调(hooks)

Published on

这个周一直在研究如何整合Jenkins和git,也就是通过git项目的提交来触发Jenkins的部署。Jenkins支持github项目的分支监控,但如果是git Server的话,分支的提交就不能被监控了。

不过在Jenkins的部署触发模块(trigger build)中有一种是通过远程来触发(Trigger builds remotely)的,也就是当远程仓库收到更新之后访问某个URL来触发Jenkins的部署,在Jenkins的控制后台也提到了这一点。

One typical example for this feature would be to trigger new build from the source control system’s hook script, when somebody has just committed a change into the repository, or from a script that parses your source control email notifications.

基于此简单的看了一下git回调的使用,现总结如下:

(1) 无论是裸库还是非裸库,都有一个hooks文件夹,里面提供各种不同阶段的回调。有本地的,有远程的。以Mac下的git为例(version 2.2.1):

-rwxr-xr-x  1 allen  staff   452 Mar  4  2016 applypatch-msg.sample
-rwxr-xr-x  1 allen  staff   896 Mar  4  2016 commit-msg.sample
-rwxr-xr-x  1 allen  staff   189 Mar  4  2016 post-update.sample
-rwxr-xr-x  1 allen  staff   398 Mar  4  2016 pre-applypatch.sample
-rwxr-xr-x  1 allen  staff  1642 Mar  4  2016 pre-commit.sample
-rwxr-xr-x  1 allen  staff  1356 Mar  4  2016 pre-push.sample
-rwxr-xr-x  1 allen  staff  4951 Mar  4  2016 pre-rebase.sample
-rwxr-xr-x  1 allen  staff  1239 Mar  4  2016 prepare-commit-msg.sample
-rwxr-xr-x  1 allen  staff  3611 Mar  4  2016 update.sample

默认都带了后缀sample, 使用的时候只要将后缀去掉即可。

(2) 以update.sample为例,里面提供关于tags,本地分支,远端分支改动的回调:

case "$refname", "$newrev_type" in
	refs/tags/*,commit)
        ...
		;;
	refs/tags/*,delete)
        ...
		;;
	refs/tags/*,tag)
        ...
		;;
	refs/heads/*,commit)
        ...
		;;
	refs/heads/*,delete)
        ...
		;;
	refs/remotes/*,commit)
		# tracking branch
		;;
	refs/remotes/*,delete)
        ...
		;;
	*)
		# Anything else (is there anything else?)
		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
		exit 1
		;;
esac

上面refname指向的就是我们进行改动的分支,如refs/heads/master, refs/heads/feature等。

结合上个例子提到的,我们可以在回调中访问Jenkins的某个地址来触发部署,因为作为共享代码库的一般是裸库,所以当开发人员将改动提交之后,对应的应该是裸库的本地分支的改动

case "$refname","$newrev_type" in
	 refs/heads/*,commit)
		# branch
	    # 获取分支的简称,即去掉refs/heads/前缀 --> master
		branch="$(git rev-parse --symbolic --abbrev-ref $refname)"
		
		# Jenkins build trigger url
		URL="http://xxxx/buildByToken/build?job=JOB_NAME/"${branch}"&token=TOKEN"
		
		echo "${refname} receive some updates"
		curl -sS $URL
		;;
esac

由于hooks文件夹下的内容本质只是一堆脚本,所以我们可以通过修改脚本内容来添加自己所需要的回调

另外要注意虽然叫回调,但它们的执行时机却是发生在真正的改动执行之前,它们会检查相应的更新如果符合要求,就进行反之则阻止。 就拿上面的update来说(如果是向远端分支提交新的改动),实际上是以新的对象(ref)去替换老的对象,如果引进回调, 那么就要在回调执行成功之后才会触发真正的更新。

上面获取分支名的做法对于新建的分支是不适用的,会报如下错误:

atal: ambiguous argument 'refs/heads/branch_name': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

个人的理解是因为结合上面提到的先通过检查再进行相应的操作,因为新建的分支提交上来的时候,refs/heads文件中根本没有这个分支相关的东西,所以rev-parse根本获取不到东西。对于获取分支名也可以采取另外一种方法,虽然有点Hack的味道:

# NF为属性的总个数 也就是输出最后一个
branch=$(echo "${refname}" | awk -F/ '{print $NF}')

实际上每种回调都是由特定的服务触发的,比如说update就是由git-receive-pack来触发的,关于它的底层实现可以参考git的 pack协议git-receive-pack的运行机制

参考

> git中的回调是什么

> 关于git回调的介绍

声明: 本文采用 BY-NC-SA 授权。转载请注明转自: Allen写字的地方