如今,Git已经成为程序员必备技能之一,也是众多公司的代码管理工具。然而,据我本人观察,这么多年来,特别是有了GitLab这类中心仓库工具之后,发现很多团队都把Git当成一个容易开分支的SVN来使用,只会pull和push。而且即使会使用更多Git功能的团队,很多地方也没有使用对。因此,谈谈自己的看法。

善用分支

Git分支的本质其实就是记录在文件中的一个指向某个工作版本一条HEAD记录的指针或引用,因此Git开分支才如此轻便。

所以何时用分支?创建分支那么轻便,应该随时用分支。

比如,你需要开发一个功能,这个功能就是一个分支。但是这个功能很大,你就需要拆分成各种小功能,一个小功能也是一个分支。当小功能的分支都开发完毕之后,合并或者衍合回大功能这个分支。大功能这个分支开发测试完了,再合并回你的主干。

同理,开发补丁版本,针对性的特殊版本,这些都是分支。

其实套路真的很简单,定义分支的关键是理清楚,你要做的某件事。这件事就是个分支,这件事中的小事也是分支。

提交什么

曾经见过有人,从头到尾开发了一大堆功能,各种复制粘贴代码,最后囫囵吞枣一股脑作为一个提交推送。此时产品经理要是跑过来说,后面的几个需求评审下来,不再需要了,让删掉,此时就该要抓狂了吧?

所以一个提交应该包含多少内容?上面提到了分支是做某件事,那么提交就应该是做这件事的步骤。

比如,你现在在开发一个功能,这个功能分前端和后端,后端要提供两个接口,前端要提供四个页面,那包含测试用例,至少应该会有十二个提交,而且每个提交足够“原子”,相当于一个非常微小的不可再分的功能。

提交的内容区分界限的关键,是要给自己在后悔时,很容易回退到某个时间点,并且这个时间点损失小,需要再开发的内容少。

压缩提交

既然提到了提交的内容区分,像git merge --squash在什么情况下使用呢?

比如此刻你在修复某个BUG,你在一个提交之后满以为解决了问题,把修复分支推到了中心仓库,让测试同学测试。然而测试同学测试下来,发现问题是解决了,但是这个解决方案引出了另外的问题。如此,接下来的提交是为了修复这个新出现的问题。可能最终几轮提交下来,最初的这个问题和引申的问题才彻底解决了。但其实你做这件事是为了解决最初的这个问题,此时你就可以用squash的功能。

何时压缩提交的关键在于判断提交是否被拆分得过散。

单分支协作

像上面的例子,一个分支现在要完成一个功能,这个功能包含前端和后端,开发人员有两名分别是一名前端工程师和一名后端工程师。那是否还要拆分一个前端分支和一个后端分支呢?

不用,git pull --rebase很好的解决了这个问题,并且前端和后端的冲突概率其实真的非常小。

合并还是衍合

这个问题网上真的各种站队的都有,我还是倾向衍合的。

首先,衍合后的提交更加线性化,容易回溯。

其次,公司内部交互中,衍合后的分支在合并回主干后会被删除掉,并不会出现某一时刻还有开发者还依赖衍合前的分支但没合并的情况。

最后,衍合的方式让解决冲突的操作落在了功能分支的提交者上,而不是主干分支的管理者上。功能分支的提交者更加清楚自己写的代码,减少合并错误的风险,而主干分支的管理者也可以从大量分支合并请求中解脱出来。实质上,分支合并请求很多的时候,少量的主干分支管理者是否有足够的精力合并所有的代码都是个问题。

规范化

提交注释规范化

推荐使用约定式提交,不但让历史更加清晰,还能通过工具自动化生成CHANGELOG。

分支名规范化

参考gitflow的分支命名,可以只参考命名而不使用它的流程。

版本标签规范化

参考语义化版本,通常社区的方式是在版本号之前加个v作为Git的Tag。

安全性问题

分支保护

常用的master、develop分支,还有tag应该用钩子保护起来。以阻断git push -f重写历史,对于CI来说,历史不应该被修改。

GPG签署提交

你看到一个提交历史中,出现了某个人,那这个提交就一定是这个人提交的吗?当你初学Git的时候,肯定首先学会了配置你自己的名称和邮箱,如果别人用你的名称和邮箱配置他自己的Git客户端呢?

普通的提交信息并不是可信的。用GPG签署提交应该作为团队协作一个默认的方式。