我曾经在一个用SVN的团队待过两年。每个周一早上最怕听到的就是:“你锁了config文件吗?”因为没人锁,意味着别人可能正在改,而你在提交时会收到一条莫名其妙的conflict。那时候我们有个不成文的规矩:改任何公共文件前,要在群里吼一声。这种基于语言的协调,本质上比任何协议都古老。后来我切换到Git,发现没人再提“锁”这个字,但冲突照样发生,只是变成了“你什么时候push的?我pull之前还好好的”。说实话,版本控制从来不是技术工具,它是一套关于“谁有权动别人的东西”的社会契约。SVN把契约写进了文件锁,Git把契约变成了提交历史的道德约束——但很多人没意识到这一点,所以他们才会把Git用成SVN,用糟糕的merge把历史搅成一锅粥。
如果你深挖两者的行为差异,会发现真正的分水岭是“信任模型”。SVN是中央集权式的,服务器是唯一真相,客户端只是提款机。你每次更新都是把别人的垃圾拉到本地,而提交时还要校验版本号,整个流程像极了去政府办事大厅:排队、取号、窗口说材料不对。Git则是完全的联邦制,每个克隆都是完整的历史仓库,你可以离线提交,甚至可以自己发branch强制覆盖远程——只要你有权限。Git最疯狂的设计是内容寻址:每个提交的SHA-1哈希不仅包含自己的快照,还包含父提交的哈希,这意味着一旦历史被改写,整个链条的指纹都会变。这不是为了酷,而是为了让你每一次操作都能被审计。你rebase时改了一个commit,等于篡改了过去,而reflog会像前女友的日记一样,把所有挣扎都记着。
说到提交信息,我见过太多人把这东西当成给上司看的流水账。写“fix”或者“update”这种话,简直是在侮辱未来的自己。我用git bisect找回归时,最怕看到的就是那一串“wip”和“oops”。git bisect的原理很粗暴:在你的历史中二分查找,逐一checkout并运行测试脚本,直到找到第一个坏的commit。如果有用的信息只有“改了点东西”,那跟看天书有什么区别?我现在的习惯是:每次提交只做一件事,信息里写清楚“为什么”而不是“是什么”。比如“不要用float存金额,精度丢失导致结算错误”,而不是“fix bug”。有人说commit message是给协作的人看的,但我的经验是,三个月后你会发现自己成了最陌生的协作者。
另一个被过度神化的功能是branch和merge。Git鼓励你频繁开分支,因为合并代价低——如果你的分支活得足够短。可现实是,很多人开了feature分支就忘了关,两周后rebase主干,冲突能让你怀疑人生。我见过一个团队为了“永远保持线性历史”,强制要求所有合并前先rebase,结果有人rebase时把别人的commit弄丢了,最终靠reflog捡回来。你问我支持merge还是rebase?我的答案是:如果你能解释清楚这两种操作在DAG上分别做了什么,你就配用人家的高级功能。否则,老老实实用merge --no-ff,至少历史是诚实的。版本控制工具不会拯救混乱的团队,它只会把团队的真实状态放成一幅X光片——有些团队看起来忙碌,但每一次merge都像是在演宫斗剧。所以,别再问“哪个版本控制工具最好”了,先问问你的团队到底怕什么:怕的是丢失代码,还是怕丢失信任?
最后说点反直觉的:分布式并没有让协作变得更民主,反而让权力更隐秘。在SVN时代,服务器管理员可以轻松查看谁改了哪一行;在Git时代,force push等于直接撕毁他人的工作成果,而且很难追溯——除非你配置了protected branch和PR审核。但这些都是流程上的补丁。真正的元技能是:知道什么时候该把分支合进主干,什么时候该退回去重写历史。这种判断力无法从工具文档里获得,只能从一次次被迫重做、被同事嫌弃、以及深夜修复冲突的糟糕体验里长出来。版本控制是给未来的自己留的一张字条,上面写着:我当时为什么这么干?至于工具本身,它们只是不同毒性的药,而病情,从来都在团队心里。