图文讲解,团队开发中的 Git 最佳实践
公众号发现了一篇非常好的有关Git的实践文章,特地转载过来,非常感谢作者 芋道源码 分享的宝贵经验。
在 2005 年的某一天,Linux 之父 Linus Torvalds 发布了他的又一个里程碑作品——Git。它的出现改变了软件开发流程,大大地提高了开发流畅度!直到现在仍十分流行,完全没有衰退的迹象。
本文不是一篇 Git 入门教程,这样的文章一搜一大把,我是要从具体实践角度,尤其是在团队协作中,阐述如何去好好地应用 Git。既然是讲在团队中的应用实践,我就尽可能地结合实际场景来讲述。
一、习惯养成
如果一个团队在使用 Git 时没有一些规范,那么将是一场难以醒来的噩梦!然而,规范固然重要,但更重要的是个人素质,在使用 Git 时需要自己养成良好的习惯。
提交
如何去写一个提交信息,《Git: 教你如何在Commit时有话可说》中做了很好的说明。在具体开发工作中主要需要遵守的原则就是「使每次提交都有质量」,只要坚持做到以下几点就 OK 了:
- 1、提交时的粒度是一个小功能点或者一个 bug fix,这样进行恢复等的操作时能够将「误伤」减到最低;
- 2、用一句简练的话写在第一行,然后空一行稍微详细阐述该提交所增加或修改的地方;
- 3、不要每提交一次就推送一次,多积攒几个提交后一次性推送,这样可以避免在进行一次提交后发现代码中还有小错误。
假如已经把代码提交了,对这次提交的内容进行检查时发现里面有个变量单词拼错了或者其他失误,只要还没有推送到远程,就有一个不被他人发觉你的疏忽的补救方法——
首先,把失误修正之后提交,可以用与上次提交同样的信息。
然后,终端中执行命令 git rebase -i [SHA]
,其中 SHA 是上一次提交之前的那次提交的,在这里是 3b22372。
最后,这样就将两次提交的节点合并成一个,甚至能够修改提交信息!
谁说历史不可篡改了?前提是,想要合并的那几次提交还没有推送到远程!
推送
当自己一个人进行开发时,在功能完成之前不要急着创建远程分支。
拉取
请读张文钿所写的《使用 git rebase 避免無謂的 merge》。
合并
在将其他分支的代码合并到当前分支时,如果那个分支是当前分支的父分支,为了保持图表的可读性和可追踪性,可以考虑用 git rebase 来代替 git merge;反过来或者不是父子关系的两个分支以及互相已经 git merge 过的分支,就不要采用 git rebase 了,避免出现重复的冲突和提交节点。
二、分支管理
Git 的一大特点就是可以创建很多分支并行开发。正因为它的灵活性,团队中如果没有一个成熟的分支模型的话,那将会是一团糟。
要是谁真把这么乱的提交图表摆在我面前,就给他一个上勾拳!
分支模型
有个很成熟的叫「Git Flow」的分支模型,它能够应对 99% 的场景,剩下的那 1% 留给几乎不存在的极度变态的场景。
需要注意的是,它只是一个模型,而不是一个工具;你可以用工具去应用这个模型,也可以用最朴实的命令行。所以,重要的是理解概念,不要执着于实行的手段。
简单说来,Git Flow 就是给原本普普通通的分支赋予了不同的「职责」:
- master——最为稳定功能最为完整的随时可发布的代码;
- hotfix——修复线上代码的 bug;
- develop——永远是功能最新最全的分支;
- feature——某个功能点正在开发阶段;
- release——发布定期要上线的功能。
看到上面的「master」和「develop」加粗了吧?代表它们是「主要分支」,其他的分支是基于它们派生出来的。主要分支每种类型只能有一个,派生分支每个类型可以同时存在多个。各类型分支之间的关系用一张图来体现就是:
更多信息可参考 xirong 所整理的《Git工作流指南》。
工具选择
一直不喜欢「**最好用」这种命题,主观性太强,不会有一个结论。对于工具的选择,我一直都是秉承「哪个能更好地解决问题就用哪个」这个原则。所以,只要不影响到团队,用什么工具都是可以接受的。但根据多数开发人员的素质情况来看,建议使用图形化工具,例如 SourceTree。如果想用命令行,可以啊!先在心里问下自己:「我 Git 牛逼不?会不会惹麻烦给别人?」
在团队中应用 Git Flow 时,推荐使用 SourceTree
与 GitLab
配合的形式:
- 1、用 SourceTree 创建 feature 等分支以及本地的分支合并、删除;
- 2、用 GitLab 做代码审核和远程的分支合并、删除。
SourceTree 和 GitLab 应该是相辅相成的存在,而不是互相取代。
三、事前准备
为了将一些规范性的东西和 Git Flow 的部分操作自动化处理,要对 SourceTree 和 GitLab 进行一下配置。
SourceTree
按下 command + , 调出「Preferences」界面并切换到「Git」标签,勾选「Use rebase instead of merge by default for tracked branches」和「Do not fast-forward when merging, always create commit」。
这样设置之后,在点「Pull」按钮拉取代码时会自动执行 git pull --rebase;并且,每次合并时会自动创建新的包含分支信息的提交节点。
接下来,点击工具栏中的「Git Flow」按钮将相关的流程自动化。如果没有特殊需求,直接按下对话框中的「OK」就好了。初始化完成后会自动切换到 develop 分支。
这下再点「Git Flow」按钮所弹出的对话框就是选择创建分支类型的了。
GitLab
在创建项目仓库后一定要把主要分支,也就是 master 和 develop 给保护起来。为它们设置权限,只有项目负责人可以进行推送和删除等操作。
被保护的分支在列表中会有特殊的标记进行区分。
四、开发流程
在引入 Git Flow 之后,所有工作都要围绕着它来展开,将原本的流程与之结合形成「基于 Git Flow 的开发流程」。
开发功能
在确定发布日期之后,将需要完成的内容细分一下分配出去,负责某个功能的开发人员利用 SourceTree 所提供的 Git Flow 工具创建一个对应的 feature 分支。如果是多人配合的话,创建分支并做一些初始化工作之后就推送创建远程分支;否则,直到功能开发完毕要合并进 develop 前,不要创建远程分支。
功能开发完并自测之后,先切换到 develop 分支将最新的代码拉取下来,再切换回自己负责的 feature 分支把 develop 分支的代码合并进来。合并方式参照上文中的「合并」,如果有冲突则自己和配合的人一起解决。
然后,到 GitLab 上的项目首页创建合并请求(merge request)。
「来源分支」选择要被合并的 feature 分支且「目标分支」选择 develop 分支后点击「比较分支」按钮,在出现的表单中将处理人指派为项目负责人。
项目负责人在收到合并请求时,应该先做下代码审核看看有没有明显的严重的错误;有问题就找负责开发的人去修改,没有就接受请求并删除对应的 feature 分支。
在将某次发布的所需功能全部开发完成时,就可以交付测试了。
测试功能
负责测试的人创建一个 release 分支部署到测试环境进行测试;若发现了 bug,相应的开发人员就在 release 分支上或者基于 release 分支创建一个分支进行修复。
发布上线
当确保某次发布的功能可以发布时,负责发布的人将 release 分支合并进 master 和 develop 并打上 tag,然后打包发布到线上环境。
建议打 tag 时在信息中详细描述这次发布的内容,如:添加了哪些功能,修复了什么问题。
修复问题
当发现线上环境的代码有小问题或者做些文案修改时,相关开发人员就在本地创建 hotfix 分支进行修改,具体操作参考「开发功能」。
如果是相当严重的问题,可能就得回滚到上一个 tag 的版本了。
五、额外说明
这里所提到的事情,虽非必需,但知道之后却会如虎添翼。
分支命名
除了主要分支的名字是固定的之外,派生分支是需要自己命名的,这里就要有个命名规范了。强烈推荐用如下形式:
- feature——按照功能点(而不是需求)命名;
- release——用发布时间命名,可以加上适当的前缀;
- hotfix——GitLab 的 issue 编号或 bug 性质等。
另外还有 tag,用语义化的版本号命名。
发布日期
发布频率是影响开发人员与测试人员的新陈代谢和心情的重要因素之一,频繁无规律的发布会导致内分泌失调、情绪暴躁,致使爆粗口、砸电脑等状况出现。所以,确保一个固定的发布周期至关重要!
在有一波或几波需求来临之时,想挡掉是不太可能的,但可以在评审时将它(们)分期,在某个发布日之前只做一部分。这是必须要控制住的!不然任由着需求方说「这个今天一定要上」「那个明天急着用」的话,技术人员就等着进医院吧!
六、实战
项目实战中的分支一般是这样的:
feater 分支表示某个功能点正在开发阶段,一般会加 origin/feater/corwien_需求ID
,即origin/feater/corwien_1002976
简单概括
master 生产主分支,发布到生产环境使用这个分支,由hotfix或者release分支合并过来,不直接提交代码。
develop 主开发分支 , 基于master分支克隆,由feature分支合并过来,一般不直接提交代码。
feature 功能开发分支 , 基于develop分支克隆 , 主要用于新需求新功能的开发,同时存在多个。
release 预发布分支 , 基于feature分支合并到develop之后 , 从develop分支克隆,测试完成后合并到master并tag打上版本号,同时也合并到develop。
hotfix 补丁分支 , 基于master分支克隆 , 主要用于对线上的版本进行BUG修复,完成后合并到master分支和develop分支。
相关分支操作命令:
创建分支: $ git branch mybranch
切换分支: $ git checkout mybranch
创建并切换分支: $ git checkout -b mybranch
更新master主线上的东西到该分支上:$git rebase master
切换到master分支:$git checkout master
更新mybranch分支上的东西到master上:$git rebase mybranch
实际项目
Git flow 实战:
创建分支/切换分支
➜ springsecurity-plus git:(master) git branch develop
➜ springsecurity-plus git:(master) git checkout develop
Switched to branch 'develop'
➜ springsecurity-plus git:(develop) git branch feature/kaiyi_1000011
➜ springsecurity-plus git:(develop)
➜ springsecurity-plus git:(develop) git checkout feature/kaiyi_1000011
Switched to branch 'feature/kaiyi_1000011'
➜ springsecurity-plus git:(feature/kaiyi_1000011)
➜ springsecurity-plus git:(develop) git branch release/2020103013
➜ springsecurity-plus git:(develop) git checkout release/2020103013
Switched to branch 'release/2020103013'
➜ springsecurity-plus git:(release/2020103013) git add -A
➜ springsecurity-plus git:(release/2020103013) ✗ git commit -m "release test"
[release/2020103013 ffa8682] release test
1 file changed, 2 insertions(+)
合并分支
➜ springsecurity-plus git:(feature/kaiyi_1000011) git checkout develop
Switched to branch 'develop'
➜ springsecurity-plus git:(develop) git merge feature/kaiyi_1000011
Updating bebdae6..0361fab
➜ springsecurity-plus git:(release/2020103013) git checkout develop
Switched to branch 'develop'
➜ springsecurity-plus git:(develop) git merge release/2020103013
Updating 0361fab..ffa8682
Fast-forward
➜ springsecurity-plus git:(develop) git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
➜ springsecurity-plus git:(master) git merge release/2020103013
推送到远程分支:
springsecurity-plus git:(develop) git push --set-upstream origin develop
springsecurity-plus git:(feature/kaiyi_1000011) git push --set-upstream origin feature/kaiyi_1000011
springsecurity-plus git:(release/2020103013) git push --set-upstream origin release/2020103013
master分支打标签:
# 本地打标签
➜ springsecurity-plus git:(master) git tag -a v0.1.0 -m "release 0.1.0 version"
# 将标签推送到远程分支
➜ springsecurity-plus git:(master) git push origin v0.1.0
项目代码:https://gitee.com/kaiyi/springsecurity-plus
分支合并重要说明:
功能开发完并自测之后,先切换到 develop 分支将最新的代码拉取下来,再切换回自己负责的 feature 分支把 develop 分支的代码合并进来。合并方式参照上文中的「合并」,如果有冲突则自己和配合的人一起解决。
然后,到 GitLab 上的项目首页创建合并请求(merge request)。
一个成功的git开发流程模式
A successful Git branching model
版本号命名规范和更新规则
命名规范
版本号是用来识别当前版本唯一性的命名方式。
标准的版本号采用 VX.Y.Z 的格式。例如V5.3.2
其中 V表示版本号 :version number ,简写为 V;
X、Y、Z 为非负的整数。X 是主版本号、Y 是次版本号、 Z 为修订号。
首次发布的版本命名为 v1.0.0
。后期每更新一个版本,就在版本号上+1。
更新规则
-
1.当修改Bug和优化功能时,修改叠加第三位数字,即Z+1.其他不变。例如:v1.0.0——V1.0.1。
-
2.当新增小功能时,修改叠加第二位数次,即Y+1.其他不变。例如:v1.0.0——V1.1.0。
-
3.当新增大功能或者对原有功能有大改动时,修改叠加第一位数次,即X+1.其他不变。例如:v1.0.0——V2.0.0。
-
4.前面的位数叠加后,后面的位数从零开始计算。
开发阶段命名说明
开发阶段时
此时系统尚不稳定,随时可能增减或者修正API。
版本格式:0.次版本号.修订号,版本号递增规则如下:
主版本号:0表示正在开发阶段;
次版本号:增加新的功能时增加;
修订号:只要有改动就增加。
开发完成后,发布API,或进入二方库时
此时系统已经基本稳定,可以对外公布使用,意味着API不再会被随意修改。
版本格式:1.0.0
后续的维护升级时
没有特殊需求不会修改API,尤其是对API进行不兼容的升级,或弃用时要特别谨慎。如果需要弃用API,要提前在一个或几个版本中加入弃用标示或注解,并在文档中,建议用户更换为其他可替换的API,然后在下个主版本号升级时,再真正丢掉弃用的API。
版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
主版本号:全盘重构时增加;重大功能或方向改变时增加;大范围不兼容之前的接口时增加;
次版本号:增加新的业务功能时增加;
修订号:增加新的接口时增加;在接口不变的情况下,增加接口的非必填属性时增加;增强和扩展接口功能时增加。
新增接口:如果该新增的接口只是对现有的业务线进行扩展则增加修订号;如果是为了增加新的业务线则增加次版本号。
git cherry-pick
git cherry-pick
是一条 Git 命令,用于将指定的提交(commit)应用于当前分支。通过 cherry-pick
命令,可以将其他分支、其他仓库或者本仓库的另一个分支中特定的一个或多个提交加入到当前分支上。
具体操作步骤如下:
- 首先进入要接受变更的分支,运行
git checkout <branch-name>
。 - 然后执行
git cherry-pick <commit-hash>
,其中<commit-hash>
是要应用的提交的哈希值。 - 如果出现冲突,需要手动解决冲突并提交变更。
需要注意的是,在使用 cherry-pick
命令时,我们需要在当前分支下进行操作,否则会出现各种异常情况。另外,如果要应用多个提交,可以同时指定多个哈希值。
gitlab pick:
如 在master提交的commit,然后将 commit cherry-pick 到 某一个dev分支:
特此注明:
本文部分内容为转载 芋道源码公众号文章,原文地址:
图文讲解,团队开发中的 Git 最佳实践
Git Flow工作流总结
A successful Git branching model
为者常成,行者常至
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)