何璇

2018-06-03 16:43

git hook实践心得

本文作者:IMWeb 何璇 原文出处:IMWeb社区 未经同意,禁止转载

优秀的团队必不可缺少源代码的质量管理,比如eslint、sasslint等代码检测工具,借助git hook能力,我们可以将这些工具无缝地整合到git开发工作流中。

git hook介绍

Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur

Git hook能够在发生某特定行为的时机,触发执行自定义的脚本。

git hook分类

Git hook分为客户端hooks(Client-Side Hooks)和服务端hooks(Server-Side Hooks),下面列出了所有可以触发hook的时机,可以在官方文档中查询:

  • Client-Side Hooks
    • pre-commit: 执行git commit命令时触发,常用于检查代码风格
    • prepare-commit-msg: commit message编辑器呼起前default commit message创建后触发,常用于生成默认的标准化的提交说明
    • commit-msg: 开发者编写完并确认commit message后触发,常用于校验提交说明是否标准
    • post-commit: 整个git commit完成后触发,常用于邮件通知、提醒
    • applypatch-msg: 执行git am命令时触发,常用于检查命令提取出来的提交信息是否符合特定格式
    • pre-applypatch: git am提取出补丁并应用于当前分支后,准备提交前触发,常用于执行测试用例或检查缓冲区代码
    • post-applypatch: git am提交后触发,常用于通知、或补丁邮件回复(此钩子不能停止git am过程)
    • pre-rebase: 执行git rebase命令时触发
    • post-rewrite: 执行会替换commit的命令时触发,比如git rebasegit commit --amend
    • post-checkout: 执行git checkout命令成功后触发,可用于生成特定文档,处理大二进制文件等
    • post-merge: 成功完成一次 merge行为后触发
    • pre-push: 执行git push命令时触发,可用于执行测试用例
    • pre-auto-gc: 执行垃圾回收前触发
  • Server-Side Hooks
    • pre-receive: 当服务端收到一个push操作请求时触发,可用于检测push的内容
    • update: 与pre-receive相似,但当一次push想更新多个分支时,pre-receive只执行一次,而此钩子会为每一分支都执行一次
    • post-receive: 当整个push操作完成时触发,常用于服务侧同步、通知

如何使用git hook

hook脚本会存放在仓库.git/hooks文件夹中,git提供了一些shell样例脚本以作参考。

pre-push.sample:

#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

while read local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]
    then
        # Handle delete
        :
    else
        if [ "$remote_sha" = $z40 ]
        then
            # New branch, examine all commits
            range="$local_sha"
        else
            # Update to existing branch, examine new commits
            range="$remote_sha..$local_sha"
        fi

        # Check for WIP commit
        commit=`git rev-list -n 1 --grep '^WIP' "$range"`
        if [ -n "$commit" ]
        then
            echo >&2 "Found WIP commit in $local_ref, not pushing"
            exit 1
        fi
    fi
done

exit 0

你只需要在.git/hooks文件夹中新建以钩子名命令的脚本文件(比如pre-push),这个脚本就会在适当的时机被触发。

一些实践

husky

husky是用node实现的一个快速安装git hooks的工具,在项目中安装后,就可以在package.json中指定相关钩子执行的npm scripts。

// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "npm test",
      "pre-push": "npm test",
      "...": "..."
    }
  }
}

但它存在一些些限制,后续说明。

imlint

imlint是团队正在使用的一款git hook工作流生成工具,可以快速方便地实现eslintsasslint等校验能力。

interactive hook

近期想在husky的基础上实现询问用户输入的hook,代码如下:

console.log(`\n确认执行命令\n${question}?(y/n)`));
process.stdin.on('data', (data) => {
  // do something...
});

但是git hook进程完全不管你是否在等待用户输入,直接就退出了,继续执行了相关操作。Google后发现,原来git hook被设计为不可交互的行为。

非要询问用户输入,可以监听非标准输入/dev/tty,代码如下:

# ...
exec < /dev/tty
# ...
if (process.platform === 'win32') {
  // husky不支持配置.git/hooks/xx 中的sh脚本
  // 需要手动加入 exec < /dev/tty 开启交互
  stream = process.stdin;
} else {
  stream = fs.createReadStream('/dev/tty');
}

stream.setEncoding('utf-8');
stream.on('error', () => {
  // do something...
});
stream.on('data', (data) => {
  // do something...
});

windows系统的兼容问题,请参考

https://nodejs.org/docs/latest-v8.x/api/tty.html

Reference

0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。