Git和SVN到底选哪个?我用了十年版本控制后的一些真实想法

🔑 关键词:Git, SVN, 版本控制对比, Git迁移, 分支管理

📖 摘要:基于十年使用经验,从命令行为、分支模型、二进制文件处理、团队协作等维度深度对比Git和SVN,附带实际迁移案例和踩坑记录。

2012年我从一个用了快五年的SVN项目切到Git,当时心里是一万个不乐意。原因很简单:SVN的版本号是连续递增的,r1234、r2345,看着就舒服,而且老员工都习惯用锁文件的方式避免冲突。但后来被逼着用了三个月Git,就再也回不去了。这篇文章不是什么劝退SVN或者无脑吹Git,而是想聊聊两者在真实开发中的那些细碎差异。

先拿最常见的分支操作来说。SVN的分支本质上是目录拷贝,你在服务器上执行svn copy的时候,其实是把trunk复制到了branches/feature-xxx。这个操作在仓库大的时候简直要命,我曾经在一个代码量大概800MB的项目上,光创建分支就花了半分钟以上,而且本地svn switch切换分支时,如果文件多,它会把每个文件的版本号都重新比对一遍,卡到怀疑人生。Git的分支只是一个指针,创建分支就是git branch feature-xxx,瞬间完成,切换分支用git checkout,本地记录差异,速度完全不在一个量级。但Git的缺点是,如果文件量极大(比如超过几十万个文件),首次clone会非常慢,我遇到过1.4GB的仓库,git clone在普通千兆内网都要跑两分多钟,而SVN可以先按目录稀疏检出,这一点在巨型仓库场景下SVN仍然有优势。

再说说日常提交这个动作。SVN的提交是全局的,你提交之后直接生成一个全仓库递增的版本号,比如r2010,然后同事通过svn update就能拿到。但只要你忘记update就直接commit,大概率会报out of date,然后你得先更新,再解决冲突,最后再重新提交,整个流程下来,你原本写好的提交信息可能会被合并成好几条,历史看起来很乱。Git的话,提交是本地操作,git commit -m "fix: 修复登录超时",随时随地提交,然后推上去就是一条干净记录。但是Git的坑在于,你需要自己管理暂存区,很多刚上手的人会搞混git add和git commit的区别,我见过有人疯狂tab补全结果把不该提交的配置文件给add进去,然后提交了,最后还得靠git reset --soft HEAD~1来撤销,不熟悉命令的话很容易慌。

讲到冲突处理,这里必须得提SVN的锁机制和Git的合并机制。SVN项目里,如果几个前端在改同一个文件,管理员通常会给文件加锁,就是svn lock,别人只能只读,改完提交之后解锁。这种模式对二进制文件特别友好,比如设计稿PSD、Unity场景文件,绝对不能并行修改。Git在二进制文件上就很吃力,它默认不支持二进制合并,一旦两个人都改了同一个图片或模型,git merge会直接提示conflict,你没法手工合并,只能选一个版本覆盖另一个,要么就得重新导出一遍。所以如果你的团队经常要处理大量二进制资源,Git其实不如SVN加锁来得干脆。我之前的项目有大量Blender模型,后来我们甚至搞了个钩子脚本,在Git push之前检查二进制扩展名,提醒大家提前沟通,但依然不如SVN的lock直观。

还有一个很多人忽略的点:离线工作能力。SVN如果你在高铁上或者飞机上,没有网络就啥也干不了,别说提交,连查看历史都不行。Git所有的提交都是本地的,你在飞机上改了代码,git commit照样可以写,等落地再push。这种体验真的会改变一个人的工作习惯——我现在哪怕是一个人的小项目,也习惯先git init,然后就敢大胆乱改代码,因为随时可以git diff看看改了什么,甚至可以git stash临时保存当前进度再去改另一个热修分支。不过Git的这个灵活性也带来一个问题:本地历史太容易乱。我见过有人忘了切换分支,在master上直接改代码然后提交,发现搞错了又用git cherry-pick救场,最后历史变得很抽象,反而不如SVN那种一条直线历史看起来直观。

如果你正在考虑从SVN迁移到Git,我建议你先把团队里所有人的Git水平拉齐。我们当时迁移时,最大的阻力不是技术,而是老同事习惯了SVN的版本号,遇到问题总说“r1500没问题啊”,Git的commit hash一串乱码根本没法记。后来我们写了个小脚本,在git log时自动简化短hash,同时强制大家写规范的commit message,包括type和scope,坚持了两个月才好点。还有一点,Git的权限控制比SVN弱很多。SVN可以通过path-based authorization精确到目录级别,比如dev只能改src/app,不能碰src/core。Git服务端如果要做到同样的效果,得靠Gitolite或者Gitea的类似插件配置,裸Git仓库本身根本不支持。所以如果你的公司有严格的安全合规要求,或者有外包人员只允许接触某几个模块,SVN在权限管理上反而更省事。

最后聊聊实际操作中的一个冷门技巧。Git有个命令叫git worktree,可以让你在同一个仓库的不同分支上同时干活,而不用反复stash或clone。比如我在主分支改bug,同时又想在新功能分支写代码,就用git worktree add ../feat feat-branch,两个文件夹各干各的,互不干扰。而SVN没有这种机制,只能在同一个工作副本里切换或者搞两份完整代码拷贝,占双倍磁盘空间,切来切去还慢。还有git bisect,找引入bug的提交非常好用,我用它把一次线上事故定位到大约三十分钟内提交的8个commit里,成功率比纯看日志高太多。SVN虽然也有svn blame,但遇到一个文件被楼上无意中格式化过,每一行都显示那个人的名字,你会非常想骂人。

不管怎么说,版本控制这种东西,只有真正踩过坑才会明白自己的需求。不是所有人都需要Git,也不是所有项目都适合SVN。如果你做的是纯文本代码为主、团队协作频繁、需要快速分支迭代,Git几乎是最优解。如果是美术资产多、权限要求严格、团队技术基础偏弱、或者服务器带宽有限,SVN依然有它的生存空间。我的个人建议是:可以按模块混用,比如代码用Git,美术资源用SVN,然后通过Jenkins脚本自动同步,听起来有点土,但确实有效。总之,别被“Git天下第一”的论调绑架,工具是为人服务的。

🏷️ 标签: