TL;DR
持续集成就是“持续地集成”,用好 Git 会让你把“持续地集成” 做的更加有效。
引子
这些年,随着持续交付和DevOps的不断升温,似乎让大家忘记了持续集成(以下简称 CI)这个基础实践。更不科学的是,很多团队以为做了持续交付和 DevOps(其实也没做好)就不需要 CI 了。鉴于此,我才有了写本文的想法。
如果你看到这里,还是不理解为什么 CI 会和 Git 有关,那么我强烈建议你看下去,因为你对 CI 的认知中很可能少了核心的东西。如果你明白 CI 和 Git 的关系,你可以跳过下一节,直接从 Git 对 CI 的帮助那节看起。
持续集成到底是什么鬼?为啥和Git有关系?
CI 就是“持续地集成”,就是频繁的提交代码并与主干上的代码合并。CI 的出发点很简单,当团队工作在同一个代码仓库(以下简称 repo)时,如果每个人都等到最后时刻提交合并代码,会带来很多冲突,导致开发延期等诸多风险。要解决这个问题,团队中的每个人应该更早更加频繁地提交合并代码,这样冲突就会减少,风险也会更早地被发现。除了频繁提交之外,整个团队工作在单一主干上,以及频繁的运行各种自动化测试也很重要。不过,本文关注在频繁提交这一点上面,其他的就先不展开讨论了。
从 CI 的定义来看,是否有一个 Jenkins 服务器(或者其他类似工具),其实并不重要。Jenkins 只是一个“持续集成系统”而已,没它照样可以 CI,有它团队也不见得在“持续地集成”。CI 系统并不是没用,但是在团队真正做到“持续地集成”之前,CI系统的作用非常有限。James Shore 曾经写过文章专门说这个事情,大家可以参考一下 http://www.jamesshore.com/Blog/Continuous-Integration-on-a-Dollar-a-Day.html。
Git 作为一个几乎要一统天下的分布式版本管理系统,对提交合并代码提供了诸多功能支持,使得程序员可以“随心所欲”地 CI。当然,Git 并非是唯一的也不一定是最早的有这些功能的分布式版本管理系统,如果你用的是 mercurial 或 Bazaar,应该也会有这些功能。
Git 对持续集成的帮助有哪些?
Commit 和 Push 使何时将代码提交入主干变得更加灵活可控
频繁提交代码意味着,每次有可工作的代码变更就应该提交到主干,比如小到修改一个资源文件中的文本。在 Git 中提交代码到主干,我们需要 git add; git commit; git push(不考虑命令简写的情况),实际上这要比 svn 这样的非分布式版本管理系统来的复杂(svn add; svn commit)。然而,毕竟不是每次提交都是像修改资源文件那样简单,Git 的分布式设计使得提交代码变得更加灵活可控。
比如复杂一点的场景是,你实现了一个功能的部分需求,剩下的需求仍未实现。这时候,通过提交来保存一个阶段性成果是非常合理的做法,但你可能不希望完成一半的功能进入主干,那么 Git 就帮上忙了。你可以 commit 这部分代码变更但是不 push,然后等剩余的需求实现之后再 commit 并把两个提交一起 push 上主干。如果是 svn,就做不到了。
Commit --amend 让一次提交可包含的代码变得更加灵活可控
换一个场景,你实现了一个功能,代码和单元测试都写好了,但有些代码需要重构。你刚想要开始重构,无奈马上要去开个一小时的会。这时,作为一个专业的程序员,你肯定不希望把没重构的代码 push 到主干,但你可能又担心回来之后重构时可能犯晕。如果用 Git,这时你可以果断 commit 但不 push,之后如果重构犯晕了,就 git checkout 或 reset;如果不犯晕,就 git commit —amend 把重构之后的代码合并到上一个提交,然后再 push。
Stash 让被打断的工作轻松暂存和恢复
程序员常被一些意外的事情打断,比如代码写到一半,被告知(或者自己意识到)需要先完成另一个更高优先级的代码提交。这时,不论代码写到什么程度,都可以用 git add; git stash 把这些代码暂存。等到那个代码提交完成了,再用 git stash pop 恢复之前的代码就好了。
Git 让合并代码变得更加简单和便捷
开源项目中 Git 的玩法会有所不同
上面介绍的 Git 用法,是针对工作在同一个产品或项目上的一个或多个团队的建议。然而,开源项目中 Git 的玩法可能是不同的。比如,给开源项目发 pull request 的时候,通常要求你先把所有的提交 squash 成一个提交记录,以避免太多提交记录污染主干。我除了搞过一个 react-native 的 npm package 之外,并没有其他开源项目的经验,所以之前的建议不适用开源项目的可能性很大。