为啥我的代码库那么大?聊聊Git使用坏习惯

简介: 阿里代码平台开发同学在阿里内网吐槽贴本文作者用幽默又真实的文字总结了开发者日常工作中遇到的那些事儿。

求求你们了!好好写你的Git commit message! squash你的 fixfixfix!删删你已经没用的分支!

说点真实的

众所周知啊🤔,部分老板会关注你提交了多少次commit,以及每一次提交包含多少行代码。

搞得部分兄弟一天可劲 add commit push。 他那一个变更发上线,你打开Git Log,好家伙那一大串都是他。加加减减缝缝补补,最后一共改了20行代码,硬是刷出了200行的功德

一串Git commit message如下

  • Merge feautre/777_平台赋能牛逼新特性 to master
  • Fix老板CR中建议
  • fix again
  • fix2
  • fix
  • save
  • update
  • new feature

典!典中典!

仿佛能看到他信心满满,直接发预发!一刷新, 哦豁,白屏!
哦忘了传这个了,fix,push,部署,玩手机,抬头一刷新,哦豁,白屏!


好活!

下次再搞扫描技术人脱口秀我提个节目!

随机找几个这种这种 git log,让演员上去 现场即兴模仿表演。

他们欢快而自信的时候,以及受痛苦和绝望所折磨的时候,生活中的痛苦和压迫会像血汗一样,一行行地全写在了他们的Git Log里。

你们好不好奇, 一个代码库几百个文件,为啥能用一两个G?小小一个系统,下个代码都十几分钟

部署和CI场景可以 depth=1 浅克隆加速

完全没有意义的Git 提交历史,极速膨胀的代码库大小,混乱的分支关系。正在压死你的代码库和项目!

每一个程序员,在遇到一个可爱而热心的代码仓看门大爷前,都会随意的对待自己的commit,因为大家都这么做。没见过怎么best practice,所以就doesn't matter。

求求你们了!好好写你的Git commit message! squash你的 fixfixfix!删删你已经没用的分支!重构拆掉哪些超大文件和几万行的类!

让Git Log能做到写清楚你到底改了啥! 而不是你的草纸!

很多资深Coder都是野生哆啦A梦,技能树包括但不限于

  1. 手里维护的代码库,托前人的福,一堆方法 JsonObject 出入参,他能如二战特工,手持密码本相互通信。
  2. 系统日志print全是不带id的sout,也能靠口袋里的见都没见过的神奇道具和监控线条里波纹感应定位故障源头。
  3. 一个代码文件3万行,硬是一眼扫找出在哪儿再加个if else能5分钟hotfix一下线上Bug。

"线上bug!很急,来不及加单测了,帮忙过一下!"

  1. 对代码系统机魂, 了如指掌,深谙取悦机魂之道。知道一些外人看来十分玄妙的独特逻辑。掌握很多,“又不是不能用,只是你不会用,要这么这么就能用”的高级黑盒功能。

虽然兄弟们这么用Git “又不是不能用”,但是要我说 “那是你不会用”“要这么这么用”

到这里,聪明的小朋友就要问了。为啥几百个文件,为啥能用一两个G呢?

Git的结构,图文实例解说

众所周知,Git 主要数据结构是一颗树,在你的.git文件夹里,结构是这样的。

其中的 refs中保存有分支的信息。

其中的  heads,本地分支remotes,远程分支(用git fetch更新) tags 不可变的版本指针

他们都是指向一个commit的指针

比如 这里, master,就是一个commit id。

你可能注意到了,分支似乎不是一个分支一个文件的。而是按/分割,当成文件夹存储的。

比如feature是不是个分支? 其实不是,假设 我新建一个名为 feature/newaCTO的分支,会在feature文件夹里建一个newaCTO, 而不是直接新建一个feature/newaCTO

小zips, linux文件系统中,文件与文件夹是不能重名的。

所以, 一旦有个傻逼建了一个叫做feature的分支。 那么,在第一个小聪明删掉他之前。 就没人能在aone拉出任何feature/*的分支了。

分支是指向commit的指针,那让我们更近一步, commit是啥呢?

这是一个merge 节点。 和普通的commit节点不同,merge节点有两个parent。

除此之外,有author和committer, author是作者,committer是提交人。在本地开发中,这两者基本上是一样的。 注意,这里使用的,是gitConfig中的username 和 email。

这里parent指向的是另一个commit, 典型的有向无环图结构。

那么Tree中有什么呢。

可以看到展示了一个列表,与GIt Log中展示不同, Tree里其实包含了完整的文件树。 树的末端,指向一个文件的oid。

列表中四个字段分别是

  1. mode: 权限(chmod加的东西,但也不完全是,是unix mode的选择性延用,还包含符合链接类型和gitlink类型用于表示git submodle)
  2. type:  类型tree说明是文件夹blob则是文件,大部分情况下就是代码,也可能是图片或其他文件。

游戏团队的代码库中就会包含很多美术素材图片。

但是,Git不是文件存储系统,大文件建议使用Git LFS技术,上传OSS,Git仅管理文件链接

  1. revision(oid): 每当你提交commit时,你在这个commit中改动的文件,git会把文件的二进制数据加个信息头,然后算个hash,从而产生一个新的oid。然后这个commit会指向他。

所以,每当你commit,就算提交一行的改动。就会存整个文件!(把代码文件,把类拆小点吧!不小心摸一下触摸板,代码就不知道去哪儿找了!)


然后你在每一改个两行,就来个commit,存储膨胀能不快么!

当然这个要看你服务端的实现,github的实现,server接收到的就是pack。

就不谈没维护好 gitIgonre,胡乱git add .把编译文件,摸鱼刷的leetcode代码或者其他奇奇怪怪的东西提交到Git中的情况了。

你可能会问,我这都文件已经都删掉了,为啥.git还有这么大? 你们是不是有Bug?


因为Git会保存你历史的所有版本!除非没有一个任何一个commit指向这个blob,才会成为游离节点,在Git Gc的时候被处理掉。

当然Git也没那么呆,是可以存增量的。

你在Git pc的时候会执行repack,repack会压缩一部分到增量,不过一般没人这么做,所以一般都是存了完整的离散文件。

git gc是个好课题,存储未来可能可以智能化的全自动gc,但是现在还不行。

因此!没事删删分支!不小心提上来的大东西,得把对应的commit squash掉!

本地也可以允许git gc清理掉游离节点释放空间。

我们并不是说要squash到一个变更一个commit,或者一个feature分支就一个commit。 就像游戏存档一样。你可以在关中频繁存档,但是一大关打完了,你可以存一个然后把关中的存档都删了。 只留关键节点,这样万一你后悔选了这条命运线,可以读档重来。

这也是为什么那些大库需要严格执行主干开发的原因。要是我们公司几万研发都用一个大库,还都是用Git记日记的憨批, 我觉得我们会被迫在存储技术上卷死OSS。坏了,顺手把多版本云盘做成主营业务了。以后新人来了先发一块移动硬盘好了, 微服务那么多系统,下代码不得先下个半个月?

总结

一是git log和commit message是很重要的信息来源,要保持整洁,用的正确,这个比发布文档还清晰。

二是代码存储膨胀问题是很现实的, 随着系统发展,代码库一个G你还能下下,再大点咋办呢。

虽然我们存代码不收费,说到这儿dataworks的兄弟们可能有同感, 不要钱就使劲造,狠角色拿git当oss用的都有几个。。

主要影响的,还是可见的未来,不治理的话,clone代码越来越慢。

Best practice:

  1. Code Owner要建立 代码库统一的commit messgae 格式规范,例如 Feature(commit): write an article to introduce git
  2. 打完Boss,单测通过,squash掉你之前上厕所或者测试时候的commit!化零为整但也不要矫枉过正!团队应该根据自己业务情况探索合适commit的大小和规范
  3. 重构掉“巨石类”!他们早就不够内聚了!把能拆掉功能拆出来吧!我先替管存代码的那个亘古和其他用这个类的开发先感谢你!
  4. 大文件用Git LFS
  5. 及时维护GitIgonre!误提交的文件一定要清理掉!
  6. 用心维护主干分支的Git Log!让改动清晰可见!
  7. 删掉没用的分支!Later Equals Never!当机立断舍离!
  8. Readme要持续更新!怎么启动,格式规范都可以写在readme里!

Bad Practice:

  1. 不要建 feature,release这两个分支,虽然git没有保留关键字,但是后人发现为啥建不出来分支的时候肯定会问候你的
  2. 不要多分支并行开发的时候merge 来 merge去, git会脏合并! 保持提交历史干净简单!业务太复杂不行咱就上主干开发不要每次代码合并不符合预期就跑过来说Git丢代码,这种侦探服务的难度,在我看是,以后是要按次收费的!      


后记

本文初发布于阿里内网,在热贴呆了几天,运营大佬问能不能翻新一下投放公众号。

发本文的初衷,是因为有几个答疑,发现合并不符合他的预期,来质问我们是不是丢代码了。让我突然意识到,其实有很多写了很多年代码的程序员,git对他们来说只需要会pull,push和checkout。

这是知识的诅咒,很多人自己是知道的。所以他们觉得别人都应该已经知道了。

而且阿里这种大厂是很多新人的第一站,老人觉得新人肯定会,其实没人教过他们。

我个人感觉,阿里是企业文化四象限中比较偏向,追求培养和传承文化的企业。

师兄和TL的水平和耐心(以及爱心)比较直接的影响新人的成长速度和体感。阿里有很多大团队的研发流程做的是极好极规范的,比如OceanBase团队,对DevOps很有自己的理解。

但是,是不是应该有一些更规范化的,sop化的,pipeline化的 (甚至pipeline as code)的CI流程,和培训流程。用流程替代管理呢?

埋下种子最好的时机是春天,第二好的时机是现在 。

相关文章
|
7月前
|
存储 Java 开发工具
WinServer服务器上搭建Git代码库
本文介绍如何在WinServer服务器上搭建Git代码库。
195 0
|
4月前
|
开发工具 git
【Azure App Service】App Service设置访问限制后,使用git clone代码库出现403报错
【Azure App Service】App Service设置访问限制后,使用git clone代码库出现403报错
|
Devops 开发工具 git
【下一代核心技术DevOps】:(三)私有代码库阿里云Git使用
  1. 引言    使用DevOps肯定离不开和代码的集成。所以要想跑通整套流程,代码库的选型也是非常重要的。否则无法实现持续集成。目前比较常用的代码管理有SVN和GIt    如果还使用SVN的,建议尽早迁移到Git上面,不然很费劲的。
1452 0
|
Shell 网络安全 开发工具
crontab定时运行git命令 更新代码库
Q:  http://stackoverflow.com/questions/7994663/git-push-via-cron    I'm trying to run a git push from cron.
1003 0
|
25天前
|
缓存 Java Shell
[Git]入门及其常用命令
本文介绍了 Git 的基本概念和常用命令,包括配置、分支管理、日志查看、版本回退等。特别讲解了如何部分拉取代码、暂存代码、删除日志等特殊需求的操作。通过实例和图解,帮助读者更好地理解和使用 Git。文章强调了 Git 的细节和注意事项,适合初学者和有一定基础的开发者参考。
42 1
[Git]入门及其常用命令
|
4月前
|
开发工具 git
【GIT 第二篇章】GIT常用命令
Git常用命令涵盖初始化、状态管理、提交、分支处理、远程操作等关键流程。`git init`启动本地仓库,`git clone`下载远程仓库。通过`git status`和`git diff`检查工作状态与差异。利用`git add`暂存文件,`git commit`保存更改。借助`git branch`、`git checkout`、`git merge`和`git rebase`管理分支。使用`git fetch`、`git pull`和`git push`同步远程仓库。通过`git reset`、`git revert`和`git checkout`实现版本回退。
71 0
|
2月前
|
开发工具 git
git学习四:常用命令总结,包括创建基本命令,分支操作,合并命令,压缩命令,回溯历史命令,拉取命令
这篇文章是关于Git常用命令的总结,包括初始化配置、基本提交、分支操作、合并、压缩历史、推送和拉取远程仓库等操作的详细说明。
117 1
git学习四:常用命令总结,包括创建基本命令,分支操作,合并命令,压缩命令,回溯历史命令,拉取命令