个人心得:Git使用与开发规范

长时间开发,已经慢慢积累出了自己的一套开发规范,在此做下记录,同时也为读者提供一个参考。此文档可能会持续更新。

前言

强烈建议先通过交互式教程 learn git branching 了解Git对于操纵修改提交历史的各种进阶操作。

提交规范

参考 约定式提交

基本工作流

大型项目

请参照 Gitflow

小型项目

分支命名同 大型项目 。和大型项目唯一的区别是去掉了develop分支,即feature等分支可直接合入到main分支。

代码审核

多人合作,无论项目大小,禁止直接将其他分支合入到main分支。必须在仓库对应平台(如Github)上发起分支合并的PR,并指定小组其他剩余成员review,code review通过才可合入。

保持提交历史干净

借助git stash拉取最新更改

你正在a分支上进行工作,做了一些更改并还没有提交。此时你发现a分支上有一个别人上传的新提交,你想把新提交拉下来,基于此继续开发,但同时你本地的工作又还没有做完,你不想直接丢掉本地的更改。

参考以下命令:

git stash
git pull
git stash pop

git stash可以把你当前的工作区暂存,然后git pull拉取远程提交,git stash pop再恢复工作区。通俗点就是用草稿纸记一下你的当前状态,然后再从草稿纸恢复过来。

另一种应用场景是你正在你的分支上做开发,突然你的队友让你去其他分支帮忙看下bug,此时也可以使用git stash暂存一下,看完bug,回到自己的分支,再git stash pop

merge还是rebase

分支合入的方式选择merge还是rebase是一个有争议的话题。一般来说,rebase能尽量保持提交历史的干净,而merge能不破坏原有的提交记录。本人更偏向于rebase,因为干净的提交历史非常易于整理和阅读,而rebase能实现这一点。

基于此,以下讨论开发中rebase的使用。

使用rebase获取main分支的最新更改

你在main分支的M1提交上创建了一个feature分支,并在feature分支上新增了一个提交F1。此时你发现main分支上有了别人上传的一个新提交M2,于是你想把M2的内容拉到你的feature分支上,使你可以在最新的main分支修改上继续做你feature分支的开发。

习惯使用merge而不是rebase的人可能会选择直接把main分支merge到你的feature分支上,但个人不建议这样,因为这可能会让提交历史看起来很怪异(可能导致历史提交记录各种相互交织,看起来异常复杂)。笔者看来,良好的提交历史里面应该只有其他分支向main分支方向的merge,而不存在main分支向其他分支方向的merge。也就是,merge应该是单向的。

一种方式是可以使用cherry-pick,读者可以自行搜索。但这里想要介绍的是rebase的方式。在以上场景下,请把你的feature分支在本地rebasemain分支上(可能需要处理冲突),然后在本地,你的feature分支就从基于M1变成了基于M2,也就是M1<-M2(main)<-F1(feature)这种样子,整个提交历史变成了一条线性,看起来美观许多。

本地rebase以后,需要将你的本地feature分支force push(--force参数)到远程仓库,这是因为你本地的feature分支已经和你远程的feature分支所在位置不一样了,需要将本地的rebase也强行推送到远程,这点很重要

在提交PR/合并前先rebase

请在你提交PR前(也就是想合并分支前)先把你的个人分支rebasemain分支之上(记得force push),然后再发起PR。这样做的好处有二:

  • 干净的历史记录。这样做以后,你的第一个修改是接在main分支的最后一个修改后面的,整个提交历史是线性的,没有分叉和交汇,非常干净,易于阅读(特别是对于reviewer)与日后维护。
  • 无需要再处理的冲突。在你rebase的过程当中,你已经完成了你与main分支的实际代码合并(也就是上一节提到的,你已经通过rebase获取到了main分支的所有最新修改),同时完成了对于代码冲突的解决。你的reviewer也会知道,你发起的这个PR已经考虑到了所有main分支最新的修改。之后在合并时,由于提交历史已经是线性的,main分支会直接fast-forward到你的feature分支的最新提交上,非常优雅。

潜在的风险

请确保你要force push的这条分支只有你一个人在开发,没有其他人在上面,否则你的行为可能会让其他人在下次拉取提交时很困扰(直接pull的话会出现很混乱的提交合并,后果很严重!)。因此如果这条分支还有其他人,请务必先做好协商。

其他习惯/技巧

开始工作/结束工作 的第一件事:git fetch

请不要直接无脑git pull,否则可能会产生一些比较严重的后果,例如当别人可能已经通过rebase+force push或者其他方式更改了远程的提交历史/分支结构。

良好的习惯是先git fetch,获取远程的最新更改,检查一下远程状态,检阅一下别人的最新工作,再决定要做什么操作。

例如,当你发现你所在的某条分支竟然已经被某个不守规矩的人rebase并force push了,此时你要做的应该是先把你本地的这条分支删除,然后重新去把远程的这条分支拉下来。

结束工作(commit)前也是如此。请先确认好该分支没有别人上传的新提交再commit。如果有新提交,请参考前面介绍的git stash,把远程的更新拉过来再进行自己的提交,保证自己的commit在远程的这个新提交之后,而不是出现奇怪的分叉。

如果你已经commit了,问题也不大,你可以通过git pull --rebase,把你的commit置于拉取到的远程提交之后。

工具推荐

  • 强烈建议使用可视化工具查看本地/远程提交历史以及具体更改,例如vscode插件 git graph