Git 使用规范
分支规范
分支的命名习惯上使用小写字母、数字、连字符 - 以及斜杠 / 组成。
这里建议使用斜杠 / 因为它会被Git自动识别,并且在 .git/ 中为其创建目录结构,例如 .git/refs/heads/feature/xxx。
复杂项目
对于使用多个平行分支进行版本管理的复杂项目,分支的命名和使用通常遵循如下规范:
- 两个长期分支:
- 主分支:包括项目的所有正式版本,名称为
main或master; - 开发分支:包括项目的最新版本,名称为
develop、dev或next;
- 主分支:包括项目的所有正式版本,名称为
- 几种短期分支:(向长期分支合并完成后即可删除)
- 功能分支:拆分为多个独立功能进行同步开发,名称例如
feature/login-page,feature/user_module; - 预发布分支:在正式版本发布之前的准备,名称例如
release/xxx,xxx代表日期;(改为pre-release可能更合适) - 快速修复分支:名称例如
hotfix/xxx,xxx代表修复的问题编号或描述。
- 功能分支:拆分为多个独立功能进行同步开发,名称例如
关于长期分支的使用:
- 主分支和开发分支作为一组平行线长期存在;
- 主分支上的结点主要是发布的稳定版本,在结点上加上对应的版本标签(例如
v1.0); - 开发分支记录了项目的最新版本代码,任何短期分支最终都需要合并到开发分支;
- 我们需要保证主分支的简洁性和稳定性,避免直接提交到主分支,主要从其它分支(预发布分支,快速修复分支)向主分支合并。
关于短期分支的使用:
- 为了创建新功能,我们在开发分支上创建若干功能分支(例如
feature/A,feature/B),我们主要工作在功能分支上,在其中不断修改代码,提交并创建新结点; - 对于完成的功能分支,重新将其合并到开发分支;
- 对于一些简单的修改,也可以直接在开发分支上修改代码,提交并创建新结点;
- 在开发分支上积累了足够多的新功能之后,可以从开发分支上创建预发布分支(例如
release/xxx),对于预发布分支上的代码,将不再向其引入任何新功能,只进行 bug 的修复; - 对于预发布分支,在经过了足够的测试之后,将进行如下操作:
- 合并到主分支,合并后的结点即为对应的正式发布版本,对其打上大版本标签(例如
v1.0); - 合并到开发分支,否则预发布分支上的 bug 修改在开发分支上没有体现;
- 合并到主分支,合并后的结点即为对应的正式发布版本,对其打上大版本标签(例如
- 在主分支上的正式发布版本可能仍然存在紧急 bug,此时可以直接从主分支上创建快速修复分支(例如
hotfix/xxx); - 对于快速修复分支,在解决了相应问题之后,将进行如下操作:
- 合并到主分支,对其打上小版本标签(例如
v1.0.1); - 合并到开发分支,否则快速修复分支上的 bug 修改在开发分支上没有体现;
- 合并到主分支,对其打上小版本标签(例如
大致如下图所示(图中有错误,hotfix 也需要向开发分支合并,确保对 bug 的修复也在开发分支中存在)
小结:
- 主分支:唯一并且长期存在;
- 开发分支:唯一并且长期存在;
- 功能分支:从开发分支创建,在功能开发完成后被合并到开发分支;
- 预发布分支:从开发分支创建,在测试完成后被合并到开发分支和主分支;(注意要向两个长期分支分别合并)
- 快速修复分支:从主分支创建,在修复对应问题后被合并到开发分支和主分支。(注意要向两个长期分支分别合并)
除了上面的经典的Git Flow,还有更复杂的分支操作流程,做了如下改动:
- 主分支,开发分支和所有的预发布分支均为受保护的只读分支,分支由管理员所有;
- 不能向保护的只读分支直接提交代码,只能向管理员申请合并;
- 对预发布分支的bug修复,需要专门从预发布分支创建
bugfix/xxx分支,修复完成后向预发布分支合并。
简单项目
对于简单的项目,没有必要使用前文那么复杂的分支流程,可以将分支系统作为一个简单的链表维护,
不同的分支处于链表的不同位置,例如分别为 main 和 next(或者称为 draft,dev),使用方式如下:
next分支处于链表的开头,我们所有的代码修改都向next分支提交;main分支会落后于next分支;- 在经过若干次提交并确保功能稳定之后,将
next分支与main分支合并(快进合并),使得后者跟进到最新的稳定版本,此时也可以顺便打上版本标签。
提交信息规范
感觉自己的 commit 写的还是太随意了,没有保留任何有效信息,学习一下广泛采用的 AngularJS 规范。
下面的 commit 示例都是针对科学计算项目的。
直接用大语言模型基于暂存区信息生成的提交信息已经非常 nice 了,稍微改一下即可。
基本格式
Commit message 由三个部分组成:
Header(标题)Body(正文,可选)Footer(页脚,可选)
模板为
1 | <type>(<scope>): <subject> |
对应的中文模板为
1 | <类型>(<作用域>): <主题> |
在语句表达中不需要考究语法细节:标题部分直接使用祈使句,命令式的语气,以动词原形开头即可;正文部分使用祈使句或陈述句即可。
下面是一个完整的提交消息示例
1 | feat(optimization): implement genetic algorithm for parameter tuning |
标题 Header
标题是必需的部分,它包含了提交类型、可选的作用域和简短的描述:
1 | <type>(<scope>): <subject> |
其中:
- type(类型):表示提交的类别。
- scope(作用域):表示改动对应的影响范围,通常是一个模块名称(可选)。
- subject(主题):简短描述本次提交的内容。
常见的类型(type)如下:
- feat:新功能(feature)
- fix:修复 bug
- docs:仅文档更改,例如更新 README
- style:不影响代码含义的细节更改(空白、格式化、缺少分号等)
- refactor:既不修复 bug 也不添加新功能的代码更改,通常是很安全的代码重构
- perf:改进性能的代码更改
- test:添加缺失的测试或更正现有的测试
- build:影响构建系统或外部依赖项的更改
- ci:对 CI 配置文件和脚本的更改
- chore:其他不涉及源文件或测试文件的更改,通常是构建过程或辅助工具的修改
- revert:恢复先前的某次提交
下面是一些 Header 示例
1 | feat(simulation): add Monte Carlo simulation for risk analysis |
Header 比其它两个部分重要地多,在提交历史中主要查看的信息就是 Header,在文末针对科学计算项目的情景提供了丰富的 Header 示例。
正文 Body
正文是可选的部分,但是对于某些复杂的改动,应详细描述改动的原因、方式及其影响。
- 使用动词描述,比如 “Fixes”, “Adds”, “Changes”.
- 每行控制在 72 个字符以内便于阅读,对过长的句子手动换行。
下面是 Body 示例
1 | feat(simulation): add Monte Carlo simulation for risk analysis |
页脚 Footer
页脚是可选的部分,用于引用相关问题、关闭问题或提供额外的补充信息。
- 如果有破坏性变更,应在页脚注明。
- 如果提交关联某个问题(如 GitHub issue),应在页脚注明。
下面是 Footer 示例
1 | BREAKING CHANGE: The simulation module interface has changed. |
示例 Header
以下是一些针对科学计算项目的提交消息的 Header 示例。
新功能 (feat)
1 | feat(data_processing): add data normalization function |
修复 Bug (fix)
1 | fix(interpolation): handle edge cases in cubic spline interpolation |
文档更新 (docs)
1 | docs(api): add documentation for new optimization functions |
代码风格 (style)
1 | style(simulation): apply consistent naming conventions to simulation module |
代码重构 (refactor)
1 | refactor(io): streamline data import/export functions |
性能优化 (perf)
1 | perf(ml): enhance training speed by optimizing data loading |
测试相关 (test)
1 | test(data_processing): add tests for data cleaning functions |
构建系统相关 (build)
1 | build(dependencies): upgrade numpy to latest stable version |
持续集成 (ci)
1 | ci(gitlab-ci): add GitLab CI pipeline configuration |
其他杂项 (chore)
1 | chore(deps): remove unused dependencies |
回退 (revert)
1 | revert: revert "perf(ml): enhance training speed by optimizing data loading" |
个人使用
前面讨论的规范都太复杂了,个人使用的笔记/代码仓库完全不需要那么复杂,因此自行归纳了一套目前的 Git 分支使用方式:
- 简单的情况下可以只使用
main分支, - 稍微复杂的情况,可以使用
main分支和next分支
为了避免commit历史太混乱,不使用过于复杂的分支操作:
- 至多维护两个长期分支
- 临时操作可以创建分支,但是需要及时合并,不要推送到远程
- next 分支可以创建
WIP类型的临时 commit,然后在合适的时机使用 rebase 对这些 commit 合并整理
对于需要提供稳定支持的仓库,可以提供对应的稳定版本,有两种做法:
- 对固定 commit 使用 tag 标记,例如
v1.0 - 创建单独的
release分支,例如release-v1.0.0,这个分支将不再合并到 main 分支,而是保持稳定,只进行必须的 bug 修复,不引入新功能。