• Welcome to Journal web site.

我是 PHP 程序员

- 开发无止境 -

Next
Prev

版本控制规范

Data: 2020-08-25 04:29:50Form: JournalClick: 8

基本准则

  • 合理控制 commit 的粒度,每次 commit 只包含一个功能或修复
  • 正确设置 commit 时的用户信息,默认 user.name 为姓名拼音、user.email 为公司邮箱
  • 在个人开发分支上,通常会有很多临时 commit,在推送分支前需要使用 git rebase 合并 commit 以保持主分支干净整洁,方便浏览提交记录
  • 非国际化项目,因此 Commit Message 均采用中文,避免一些英文语法及翻译理解上的问题

Commit Message 规范

注意:以下规范中的符号(如冒号、括号)均为中文字符

 

基础格式

<类型><标题>
// 空行
<说明> // 选填

类型

限定只能使用以下关键词:

  • 特性
  • 修复
  • 优化
  • 文档
  • 重构
  • 其他

标题

  • 通常 commit 列表只显示第一行文字,因此要用一句话概括本次提交的内容
  • 如果 commitissue 相关,则在末尾关闭 issue(close #id)

说明

选填,对本次 commit 的详细说明,使用 Markdown 无序列表的格式书写


Commit Message 示例

以下均为符合规范的示例:

文档:Commit Message 规范
修复:用户注册验证码发送失败(close #123)
特性:用户中心模块(close #123)

- 用户注册功能
- 用户登录功能
- 用户信息接口

自动生成工具

tool/commitMsg.html

——————————————————————————————————————————

什么是 Git Hooks?

详见:自定义 Git - Git 钩子


commit-msg 脚本

将脚本放入项目 .git/hooks/commit-msg 中,提交代码时会自动执行 Commit Message 格式检查

#!/bin/sh

FILE='.git/COMMIT_EDITMSG'
TYPE=('特性' '修复' '优化' '文档' '重构' '其他')

function ERROR() {
    divider=$(printf %.s- {1..150})
    echo -e $divider
    echo -e "[ 提交信息错误 ] $1"
    echo -e $divider
    exit 1
}

# 检查长度
msg=$(cat $FILE)

if [ "$msg" == 'no message' ]; then
    ERROR '不能为空!'
fi

if [ ${#msg} -lt 10 ]; then
    ERROR "字数 < 10(当前 ${#msg} 个字)"
fi

# 检查类型
title=$(cat $FILE | awk 'NR==1')
item=(${title//':'/' '})
if [[ ${TYPE[*]/${item[0]}/} == ${TYPE[*]} ]]; then
    ERROR "类型(${item[0]})错误!(仅限使用:${TYPE[*]})"
fi

# 检查空行
blank=$(cat $FILE | awk 'NR==2')
if [[ "$blank" != '' ]]; then
    ERROR "第 2 行请留空!"
fi

# 检查说明
num=$(cat $FILE | awk 'END{print NR}')
for ((n = 3; n <= $num; n++)); do
    desc=$(cat $FILE | awk "NR==$n")
    if [[ "$desc" =~ ^(- .+) ]]; then
        continue
    fi
    ERROR "说明($desc)格式错误!"
done

pre-commit 脚本

配置 PHP 参数并将脚本放入项目 .git/hooks/pre-commit 中,提交代码时会自动执行 PHP 语法及文档排版检查

#!/bin/sh

# 请确保 php 命令可正常执行(配置环境变量)
PHP_SYNTAX_CHECK='true' # PHP 语法检查开关
DOC_TYPE_SETTING_CHECK='true' # 文档排版检查开关

read -r -d '' DOC_TYPE_SETTING_CHECK_SCRIPT <<'EOF'
function format($text)
{
    $cjk[] = '\x{2e80}-\x{2eff}';
    $cjk[] = '\x{2f00}-\x{2fdf}';
    $cjk[] = '\x{3040}-\x{309f}';
    $cjk[] = '\x{30a0}-\x{30ff}';
    $cjk[] = '\x{3100}-\x{312f}';
    $cjk[] = '\x{3200}-\x{32ff}';
    $cjk[] = '\x{3400}-\x{4dbf}';
    $cjk[] = '\x{4e00}-\x{9fff}';
    $cjk[] = '\x{f900}-\x{faff}';
    $cjk = implode('', $cjk);

    $patterns = [
        'cjk_quote' => ['(['.$cjk.'])(["\'])', '$1 $2'],
        'quote_cjk' => ['(["\'])(['.$cjk.'])', '$1 $2'],
        'fix_quote' => ['(["\']+)(\s*)(.+?)(\s*)(["\']+)', '$1$3$5'],
        'cjk_hash' => ['(['.$cjk.'])(#(\S+))', '$1 $2'],
        'hash_cjk' => ['((\S+)#)(['.$cjk.'])', '$1 $3'],
        'cjk_operator_ans' => ['(['.$cjk.'])([A-Za-zΑ-Ωα-ω0-9])([\+\-\*\/=&\\|<>])', '$1 $2 $3'],
        'ans_operator_cjk' => ['([\+\-\*\/=&\\|<>])([A-Za-zΑ-Ωα-ω0-9])(['.$cjk.'])', '$1 $2 $3'],
        'bracket' => [
            ['(['.$cjk.'])([<\[\{\(]+(.*?)[>\]\}\)]+)(['.$cjk.'])', '$1 $2 $4'],
            [
                'cjk_bracket' => ['(['.$cjk.'])([<>\[\]\{\}\(\)])', '$1 $2'],
                'bracket_cjk' => ['([<>\[\]\{\}\(\)])(['.$cjk.'])', '$1 $2'],
            ]
        ],
        'fix_bracket' => ['([<\[\{\(]+)(\s*)(.+?)(\s*)([>\]\}\)]+)', '$1$3$5'],
        'cjk_ans' => ['(['.$cjk.'])([A-Za-zΑ-Ωα-ω0-9`@&%\=\$\^\*\-\+\\/|\\\])', '$1 $2'],
        'ans_cjk' => ['([A-Za-zΑ-Ωα-ω0-9`~!%&=;\|\,\.\:\?\$\^\*\-\+\/\\\])(['.$cjk.'])', '$1 $2'],
    ];

    foreach ($patterns as $key => $value) {
        if ($key !== 'bracket') {
            $text = preg_replace('/'.$value[0].'/iu', $value[1], $text);
            continue;
        }

        $old = $text;
        $new = preg_replace('/'.$value[0][0].'/iu', $value[0][1], $text);
        $text = $new;

        if ($old !== $new) {
            continue;
        }
        foreach ($value[1] as $val) {
            $text = preg_replace('/'.$val[0].'/iu', $val[1], $text);
        }
    }

    return $text;
}

error_reporting(0);
$file = $argv[1];
$source = file_get_contents($file);
$result = format($source);
if ($source == $result) {
    exit;
}

$pos = strspn($source ^ $result, "\0");
$fragment['source'] = mb_strcut($source, $pos - 30, 100);
$fragment['result'] = mb_strcut($result, $pos - 30, 100);
$data = [
    sprintf('%s(第 %d 字节)', $file, $pos),
    sprintf('原文:%s', str_replace(["\r\n", "\n"], ' ', $fragment['source'])),
    sprintf('优化:%s', str_replace(["\r\n", "\n"], ' ', $fragment['result'])),
];
echo implode("\n", $data);
exit(1);
EOF

divider=$(printf %.s- {1..150})
files=$(git diff --cached --name-only --diff-filter=ARM)

for file in ${files[@]}; do
    extension=${file##*.}

    # 检查 PHP 语法
    if [[ $PHP_SYNTAX_CHECK == 'true' && $extension == 'php' ]]; then
        result=$(php -l $file)
        if [ $? -eq 0 ]; then
            continue
        fi
        echo -e "${divider}\n[ PHP 语法错误 ] ${result}\n${divider}"
        exit 1
    fi

    # 检查文档排版
    if [[ $DOC_TYPE_SETTING_CHECK == 'true' && $extension == 'md' ]]; then
        result=$($PHP -r "$DOC_TYPE_SETTING_CHECK_SCRIPT" "$file")
        if [ $? -eq 0 ]; then
            continue
        fi
        echo -e "${divider}\n[ 文档排版错误 ] ${result}\n${divider}"
        exit 1
    fi
done

——————————————————————————————————

master 分支

包含所有正式发布的版本,在 master 分支上的 commit 都应该打上版本 tag(此分支只允许合并,不能直接提交)

master


develop 分支

主开发分支(此分支只允许合并,不能直接提交)


feature 分支

命名规范:feature/功能名称

基于 develop 分支创建,主要用来开发一个新功能,开发完成后合并回 develop, 合并完成后可删除此 feature 分支

feature


release 分支

命名规范:release/版本号

当需要发布新版本时,基于 develop 分支创建一个 release 分支,并在此分支上测试、修复问题,完成后合并至 masterdevelop 分支

release


hotfix 分支

命名规范:hotfix/问题名称

当发现问题时,基于 master 分支创建 hotfix 分支,在此分支修复完成后合并至 masterdevelop 分支,同时在 master 分支打上版本 tag

hotfix


基于 Sourcetree + GitLab 实现

仓库 GitFlow 初始化

在 Sourcetree 进入仓库后,点击右上角 Git工作流 按钮,使用默认配置初始化:

Sourcetree init


创建 feature 分支

初始化完毕后依次点击 Git工作流 - 建立新的功能 按钮创建分支:

Sourcetree action

功能开发完成并检查测试后,先切到 develop 分支拉取最新代码,再切回 feature 分支把 develop 分支合并进来


推送至 GitLab 远程分支

由于代码需要审核,此时应当使用 推送 功能,将此分支推送至 GitLab 远程分支:

Sourcetree push


在 GitLab 上发起 Merge Request

发起合并请求将 feature 分支合并至 develop 分支

GitLab merge request

Name:
<提交>