全量格式化还是渐进格式化?
对于一个历史悠久又没有执行强制代码规范的代码库,全量格式化看起来是一件风险不可控的事情:它产生了大量的难以评估的格式改动,也使得格式化以后的代码库运行git blame几乎都会定位到进行格式化的人(而非是这段逻辑的上一次有意义的修改者)。相比之下,理想的做法似乎应该是渐进格式化:对于新代码,使用强制的代码规范;对于老代码,除非它被修改,不然不进行强制代码规范。
真的是这样子吗?
全量格式化风险并不随代码量变大而显著变大。
全量格式化往往会产生改变巨大的提交,代码改变得越多,看起来引入问题的风险就会越大。然而,对于代码格式器而言,代码格式器需要处理的是语言规范里面定义的有限几种语法结构的组合,绝大部分的业务逻辑代码都不会有像编译器的测试用例一般复杂的语法结构,对于代码格式器而言,只要代码库中的语法结构都被测试覆盖,改一百行代码和改十万行代码并没有本质区别。换言之,如果代码格式器出错,那么导致的代码错误一定很容易被查出来(因为同样的语法结构在系统各个角落都会用到)。只要十万行代码用到的语法结构组合不是一百行代码的语法结构组合的一千倍,那么全量格式化十万行代码引入的问题就不会是只格式化一百行代码的一百倍,因此全量格式化代码引入的风险关于代码量完全不成线性关系。
一致的代码风格降低开发成本。
全量格式化立刻使得代码库形成一致的代码风格,项目的每一个开发者都会备受鼓舞,一致的风格让开发者的阅读流不必被异常的代码格式阻断,对代码块的视觉识别也更加迅速(你的大脑很快习得了function后面一个空格的位置一定是一组参数,你不必为多一个或者少一个空格而额外消耗认知能力),配合上代码提交阶段的自动格式化,开发者甚至可以在开发过程中完全不需理会代码格式——反正最后提交时都会被计算机誊抄一遍进入到代码库中。
增量格式化容易扩大化合并时的代码冲突。
可是渐进代码格式化给我更多的安全感?在 GitFlow 的分支管理模式中,假设一个未被格式化的文件分别在特性分支和补丁分支有了改动,那么改动将会触发该文件的格式化,我们会在后面指出——当两个分支的同一个文件分别进行化后,合并时的冲突块大小将会被格式化放大(因为 git 并不理解哪些改动是格式化,哪些改动是修改业务逻辑),很快你将会淹没在无处不在的合并冲突之中,更糟糕的是,因为 git 不理解格式化和业务逻辑修改的区别,你需要仔细核对才能知道哪些改动压根没有修改业务逻辑,这无疑增大了合并的时候错漏的风险。
那么,不如毕其功于一役吧。
如何全量格式化GitFlow分支模型代码库?
下图一个典型的采用 GitFlow 分支模型的代码库历史图:
在 GitFlow 模型中,考察分支的生命周期,假设所有分支都是根据需求合理存在的,可以发现:
所有的 release 分支都是历史分支,它们不会再汇入到 master 分支
所有的 hotfix 分支都将汇入 master 分支,hotfix 分支生命周期很短