Git - 基于GitLab服务端、客户端钩子介绍

2019/03/25 Git

Git Hooks

说明

  1. Git钩子是仓库中特定事件发生时Git自动运行的普通脚本.
  2. 钩子在本地或服务端仓库都可以部署,且只会在仓库中事件发生时被执行

钩子如何使用

  1. Git钩子默认在 .git/hooks下面,只需要给后缀.sample去掉,附上chmod +x 权限即可。
  2. 钩子一些内置的脚本语言都是基于shellperl的,如果你要自定义钩子,比如:#!/usr/bin/env python 告诉它如何解析就可以了。

tree .git/hooks/
.git/hooks/
├── applypatch-msg.sample
├── commit-msg.sample
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── prepare-commit-msg.sample
├── pre-push.sample
├── pre-rebase.sample
└── update.sample

本地钩子和服务器钩子

本地钩子

本地钩子只影响它们所在的仓库,它不会随着git clone一起复制到新的仓库。

这里的唯一好处就是:你是项目的开发者,这个完全看开发者心情想不想用钩子为自己做点事情。

当然,本地钩子有哪些?他们都是在什么时候被触发?

pre-commit

在你运行git commit命令时被触发,pre-commit不需要任何参数,以非0状态退出时将放弃整个提交。让我们看一个简化了的(和更详细的)内置pre-commit钩子。只要检测到不一致时脚本就放弃这个提交,就像git diff-index命令定义的那样(只要词尾有空白字符、只有空白字符的行、行首一个tab后紧接一个空格就被认为错误)

脚本示例:


#!/bin/sh

# 检查这是否是初始提交
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    echo "pre-commit: About to create a new commit..."
    against=HEAD
else
    echo "pre-commit: About to create the first commit..."
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# 使用git diff-index来检查空白字符错误
echo "pre-commit: Testing for whitespace errors..."
if ! git diff-index --check --cached $against
then
    echo "pre-commit: Aborting commit due to whitespace errors"
    exit 1
else
    echo "pre-commit: No whitespace errors :)"
    exit 0
fi

prepare-commit-msg

prepare-commit-msg钩子在pre-commit钩子在文本编辑器中生成提交信息之后被调用。这被用来方便地修改自动生成的squash或merge提交。

prepare-commit-msg脚本的参数可以是下列三个:

  • 包含提交信息的文件名。你可以在原地更改提交信息。
  • 提交类型。可以是信息(-m或-F选项),模板(-t选项),merge(如果是个合并提交)或squash(如果这个提交插入了其他提交)。
  • 相关提交的SHA1哈希字串。只有当-c,-C,或–amend选项出现时才需要。

脚本示例:


#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# 收集参数
commit_msg_filepath = sys.argv[1]
if len(sys.argv) > 2:
    commit_type = sys.argv[2]
else:
    commit_type = ''
if len(sys.argv) > 3:
    commit_hash = sys.argv[3]
else:
    commit_hash = ''

print "prepare-commit-msg: File: %s\nType: %s\nHash: %s" % (commit_msg_filepath, commit_type, commit_hash)

# 检测我们所在的分支
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "prepare-commit-msg: On branch '%s'" % branch

# 用issue编号生成提交信息
if branch.startswith('issue-'):
    print "prepare-commit-msg: Oh hey, it's an issue branch."
    result = re.match('issue-(.*)', branch)
    issue_number = result.group(1)

    with open(commit_msg_filepath, 'r+') as f:
        content = f.read()
        f.seek(0, 0)
        f.write("ISSUE-%s %s" % (issue_number, content))

commit-msg

commit-msg钩子和prepare-commit-msg钩子很像,但它会在用户输入提交信息之后被调用。这适合用来提醒开发者他们的提交信息不符合你团队的规范

脚本示例:


#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# 收集参数
commit_msg_filepath = sys.argv[1]

# 检测所在的分支
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "commit-msg: On branch '%s'" % branch

# 检测提交信息,判断是否是一个issue提交
if branch.startswith('issue-'):
    print "commit-msg: Oh hey, it's an issue branch."
    result = re.match('issue-(.*)', branch)
    issue_number = result.group(1)
    required_message = "ISSUE-%s" % issue_number

    with open(commit_msg_filepath, 'r') as f:
        content = f.read()
        if not content.startswith(required_message):
            print "commit-msg: ERROR! The commit message must start with '%s'" % required_message
            sys.exit(1)


post-commit

post-commit钩子在commit-msg钩子之后立即被运行 。它无法更改git commit的结果,所以这主要用于通知用途。这个脚本没有参数,而且退出状态不会影响提交。对于大多数post-commit脚本来说,你只是想访问你刚刚创建的提交。你可以用git rev-parse HEAD来获得最近一次提交的SHA1哈希字串,或者你可以用git log -l HEAD获取完整的信息。比如说,如果你需要每次提交快照时向老板发封邮件(也许对于大多数工作流来说这不是个好的想法),你可以加上下面这个post-commit钩子

脚本示例:


#!/usr/bin/env python

import smtplib
from email.mime.text import MIMEText
from subprocess import check_output

# 获得新提交的git log --stat输出
log = check_output(['git', 'log', '-1', '--stat', 'HEAD'])

# 创建一个纯文本的邮件内容
msg = MIMEText("Look, I'm actually doing some work:\n\n%s" % log)

msg['Subject'] = 'Git post-commit hook notification'
msg['From'] = 'mary@example.com'
msg['To'] = 'boss@example.com'

# 发送信息
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587

session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
session.login(msg['From'], 'secretPassword')

session.sendmail(msg['From'], msg['To'], msg.as_string())
session.quit()

post-checkout

post-checkout钩子和post-commit钩子很像,但它在你用git checkout查看引用的时候被调用。这是用来清理你的工作目录中可能会令人困惑的生成文件

这个钩子接受三个参数,它的返回状态不影响git checkout命令。

  • HEAD前一次提交的引用
  • 新的HEAD的引用
  • 1或0,分别代表是分支checkout还是文件checkout。

脚本示例:


#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# 收集参数
previous_head = sys.argv[1]
new_head = sys.argv[2]
is_branch_checkout = sys.argv[3]

if is_branch_checkout == "0":
    print "post-checkout: This is a file checkout. Nothing to do."
    sys.exit(0)

print "post-checkout: Deleting all '.pyc' files in working directory"
for root, dirs, files in os.walk('.'):
    for filename in files:
        ext = os.path.splitext(filename)[1]
        if ext == '.pyc':
            os.unlink(os.path.join(root, filename))


pre-rebase

pre-rebase钩子在git rebase发生更改之前运行,确保不会有什么糟糕的事情发生。这个钩子有两个参数:frok之前的上游分支,将要rebase的下游分支。如果rebase当前分支则第二个参数为空。以非0状态退出会放弃这次rebase

脚本示例:


#!/bin/sh

# 禁用所有rebase
echo "pre-rebase: Rebasing is dangerous. Don't do it."
exit 1

服务器端钩子

对于运维来说,主要还是配置服务器端钩子,这里的钩子是存在于服务器端的,可以约束所有git clone此项目的人

服务器端的钩子主要为:

  • pre-receive
  • post-receive
  • update

这些钩子都允许你对git push的不同阶段做出响应

pre-receive

pre-receive钩子在有人用git push向仓库推送代码时被执行

示例脚本:


#!/usr/bin/env python

import sys
import fileinput

# 读取用户试图更新的所有引用
for line in fileinput.input():
    print "pre-receive: Trying to push ref: %s" % line

# 放弃推送
# sys.exit(1)

update

update钩子在pre-receive之后被调用,用法也差不多。它也是在实际更新前被调用的,但它可以分别被每个推送上来的引用分别调用。也就是说如果用户尝试推送到4个分支,update会被执行4次。和pre-receive不一样,这个钩子不需要读取标准输入。

事实上,它接受三个参数:

  • 更新的引用名称
  • 引用中存放的旧的对象名称
  • 引用中存放的新的对象名称

示例脚本:


#!/usr/bin/env python

import sys

branch = sys.argv[1]
old_commit = sys.argv[2]
new_commit = sys.argv[3]

print "Moving '%s' from %s to %s" % (branch, old_commit, new_commit)

# 只放弃当前分支的推送
# sys.exit(1)

上面这个钩子简单地输出了分支和新旧提交的哈希字串。当你向远程仓库推送超过一个分支时,你可以看到每个分支都有输出

post-receive

post-receive钩子在成功推送后被调用,适合用于发送通知。对很多工作流来说,这是一个比post-commit更好的发送通知的地方,因为这些更改在公共的服务器而不是用户的本地机器上。给其他开发者发送邮件或者触发一个持续集成系统都是post-receive常用的操作。这个脚本没有参数,但和pre-receive一样通过标准输入读取

PS: Git钩子里面凡是POST都是用来通知xxx

最后,感谢以下原文链接作者,涉权请联系作者删除。

参考原文链接:https://www.open-open.com/lib/view/open1449721833879.html#articleHeader3

Search

    扫码加微信进入OpenDevOps群

    Yangxiaofei

    Table of Contents