前言
如今在多人协作开发的团队中,免不了要用到版本控制系统(Version control system),常见的有SVN
和Git
。但是当公司和团队增长到一定程度时,就需要考虑一些涉及到稳定性的问题。比如
- 如何严格控制线上代码的合并权限
- 如何管理多个并行开发的feature的测试
- 线上产生问题时如何快速回滚
- 如何减少开发人员合并代码的人工疏漏
这篇文章会介绍一种基于git
分支的开发部署流程,大部分思路来自于实习部门的实践,我对其做了一些抽象和补充。相信大公司内部肯定有自己成熟的开发和部署流程,这边只是简单介绍思路,推荐开发还比较原始的小公司或者小团队使用,可以更好地管理项目迭代。
分支管理
项目中将主要分为三种分支
- master —— 线上稳定代码
- dev/x.x.x —— 部署专用feature分支,一个版本号代表一个feature,使用Semantic Versioning 2.0.0 语义化版本规范。
- * —— 个人开发分支,随意命名
两个角色可以对分支进行操作,部署系统和开发者。下面介绍各个分支的具体作用与特点
master
master分支代表已经运行在线上的稳定代码,只有部署系统才有权限修改master分支。其他分支必须在本地合并master的最新修改之后才能发布或者push到远程,保证远程的分支的代码始终领先于master分支。
dev
dev分支命名格式固定为dev/x.x.x(前缀随意,但各dev分支一定要相同),方便脚本识别。版本号根据当前feature决定,应始终大于master的版本号。
在部署系统中可以选择某个版本号的dev分支,将其发布至任意机器(日常/预发/线上)。发布线上时系统会自动将代码合进master分支(因为开发时本地已经合并过master所以不会有代码冲突),线上发布之后会将对应的dev分支删除。
个人分支
分支名随意,所有开发的操作都应该在这个分支下完成(因为dev分支会被删除,master分支没有权限,只有这个是可控的分支吗)
开发流程
- 在个人分支上进行开发,保证领先于master分支并处理相关冲突
- push时由另一名同事进行code review之后才能提交成功
- 往dev分支merge代码,通过部署系统发布测试环境
- 需要修改代码时,重新走一遍步骤1-3
- 最后上线时,在部署系统选择对应版本号的dev分支
部署流程
- 部署系统读取dev分支,检查版本号以及commit是否领先于master
- 部署系统将原有master分支用tag存下来,命名为相应版本号(用于快速回滚)
- 部署系统在master分支进行merge dev分支操作
- 部署系统删除dev分支
回顾问题
我们再来看这个系统是如何解决之前提到的问题:
- 如何严格控制线上代码的合并权限 —— 只有部署系统才有权限修改master分支,减少了开发的误操作。
- 如何管理多个并行开发的feature的测试 —— 多个并行开发的feature现在用不同的版本号代表,各自有独立的dev分支,不同的测试机器可以选择不同的分支部署进行测试。
- 线上产生问题时如何快速回滚 —— 部署线上时始终会产生一个以版本号为名称的tag版本的代码(什么是tag),这样部署系统可以快速找到上个稳定版本的代码进行回滚操作。
- 如何减少开发人员合并代码的人工疏漏 —— 强制远程代码必须领先于master,这样开发在日常迭代时必须多次合并master的改动,每次合并小量代码,更方便解决冲突,防止在部署前一次性合并大量代码导致人工疏漏。
总结
在团队逐渐扩张,项目越来越大的过程中,流程的重要性就渐渐超过了开发者素质的重要性,大型项目迭代中要切记不能过于依赖开发者自身水平,因为这是一个不可控因素。很多问题都能通过完善流程去解决,这边也只是介绍其中一个流程,其他还包括评审,测试等各种流程,未来或许在这几个方面也会做一些研究。
附录:git hook pre-push
因为公司内部部署系统的种种限制,上面的想法我只在我们团队实现了很小一部分。其中如何在提交时强制要求领先于master就是用git hook
的pre-push
实现的。
git hook
是git提供的工具,可以在某些操作的前后触发特定脚本,顾名思义pre-push
就是在push前触发的脚本。一般来说脚本应该放在git服务器上,方便统一管理,但是我并没有公司git服务器的权限,所以只能放在项目本地,但在本地还有一个问题就是git hook
脚本是放在.git
文件夹下的,无法随着项目一起提交维护。所以这里用到了一个npm包husky来帮助我管理git hook
脚本,pre-push检查脚本如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#!/bin/bash
while read local_ref local_sha remote_ref remote_sha
do
commit=`git rev-list -n 1 origin/master ^$local_ref`
echo $commit
if [ -n "$commit" ]
then
echo >&2 -e "Local branch $local_ref is behind origin/master"
echo >&2 -e "Please run ' git merge origin/master ' first"
exit 1
fi
done
exit 0