在日常的协作开发中,Git作为我们最重要的代码版本控制工具,扮演着核心角色。然而,我们发现团队在使用Git时,出现了一些不规范的操作,例如提交信息不清晰、提交粒度过大、主线分支存在无效提交等。这些问题不仅影响了代码的可追溯性和可维护性,也增加了代码审查的难度,降低了团队的协作效率。
本次分享旨在为大家提供一份详细的Git提交规范与Git Flow最佳实践指南。Git Flow 每个团队可能都有自己的规范,这次分享只讲如何通过规范我们的操作,让每一次提交都有迹可循,让每一次代码合并都清晰明了,从而提升开发效率,保障代码质量。
一、Git Commit 消息规范
良好的Commit消息是项目文档的一部分,它能帮助我们快速理解每次提交的目的、内容和影响。
1.1 常见问题与危害
- 问题1: 提交消息简单,如 "update", "fix", "no update" 等。
- 危害: 无法快速了解本次提交的具体内容和目的;导致代码回溯困难;影响问题排查效率。
1.2 规范准则
我们建议采用 Angular 团队的 Commit Message 约定,它结构化且清晰明了。
提交消息格式:
xml
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
type
(必需): 本次提交的类型,用于说明提交的性质。feat
: 新增功能 (feature)fix
: 修复 bugdocs
: 文档变更 (documentation)style
: 代码格式或风格调整 (不影响代码运行的变动,如空格、分号等)refactor
: 代码重构 (既不是新增功能也不是修复 bug 的代码变动)perf
: 性能优化 (performance)test
: 增加或修改测试build
: 构建过程或辅助工具的变动 (如 CI/CD 配置、依赖更新)ci
: CI/CD 相关的改动chore
: 其他不属于以上类型的提交 (如构建过程、辅助工具的变动)revert
: 撤销之前的提交
scope
(可选): 本次提交影响的范围,例如users
,auth
,component-name
,api
等。这有助于更精细地定位改动。subject
(必需): 提交的简短描述,不超过 50 个字符。- 使用现在时、祈使句,例如 "fix: add user validation" 而不是 "fix: added user validation"。
- 首字母小写,句末不加句号。
body
(可选): 详细描述本次提交,解释为什么进行这项改动,以及解决了什么问题,或实现了什么功能。可以分点说明。footer
(可选): 放置一些元数据,例如关联的 Issue ID,或 Breaking Changes 信息。- 例如:
Closes #123
,Refs #456
,BREAKING CHANGE: ...
- 例如:
1.3 示例
sql
feat(user): add new user registration API
This commit introduces a new API endpoint for user registration.
It includes:
- Validation for email and password
- Hashing of passwords
- User data persistence to database
Closes #789
less
fix(auth): correct token refresh logic
Previously, the token refresh mechanism was failing under high load
due to a race condition. This fix ensures that only one refresh
request is active at a time for a given user session.
1.4 这样做的好处
- 可读性: 快速理解每次提交的目的,无需深入代码。
- 可追溯性: 方便回溯代码历史,定位问题和查找功能实现。
- 自动化: 可以基于 Commit Type 自动生成 CHANGELOG,或者触发 CI/CD 流程。
- 团队协作: 统一的规范减少沟通成本,提高团队协作效率。
二、Git Flow 工作流管理
Git Flow是一种成熟且广泛使用的分支管理模型,它定义了明确的分支角色和合并策略,有助于大型团队和复杂项目的协作。
2.1 常见问题与危害
- 问题2: 提交的代码一次性很多,让 review 的人很麻烦。
- 危害: 代码审查效率低下;隐藏潜在Bug;难以理解改动的上下文;回滚风险高。
- 问题3: 在 main/develop 的主线分支上,有很多无效提交信息节点, 比如 merge xx into develop。
- 危害: 污染主线分支历史;难以通过主线历史了解项目演进;难以进行有效的代码回溯。
2.2 Git Flow 核心分支
Git Flow主要包含以下几类分支:
main
(或master
) 分支: 生产环境代码,保持永远可发布状态。只接受release
分支或hotfix
分支的合并。develop
分支: 预发布环境代码,包含所有已开发完成并通过测试的功能,是集成各功能的主分支。feature
分支: 功能开发分支,从develop
分支创建,用于开发新功能。完成后合并回develop
。release
分支: 发布分支,从develop
分支创建,用于准备发布新版本。在此分支上进行 Bug 修复、版本号升级等操作。完成后合并到main
和develop
。hotfix
分支: 热修复分支,从main
分支创建,用于紧急修复生产环境的Bug。完成后合并到main
和develop
。
2.3 最佳实践规范
-
提交粒度小而精 - 如何界定提交,确保每次 commit 是独立或有效的:
- 核心原则: 每次提交只做一件事,并且这件事是完整的、可测试的、有意义的。 一个提交应该是一个原子操作,它引入的改动不应该依赖于后续的提交才能正常工作。
- 操作指南:
- 逻辑拆分: 将一个大的功能点拆解成多个小的、独立的逻辑单元进行提交。例如:
- 数据模型修改 (
feat(model): add user profile fields
) - API 接口定义 (
feat(api): define user profile endpoints
) - 业务逻辑实现 (
feat(service): implement user profile update logic
) - UI 界面展示 (
feat(ui): display user profile screen
) - Bug 修复 (
fix(login): correct password validation
) - 优化 (
perf(image): optimize image loading
)
- 数据模型修改 (
- 避免半成品提交: 不提交那些无法编译、无法运行、功能不完整的代码。即使是小的修改,也应确保提交后代码库处于可用状态。
- 上下文一致性: 一个提交中的所有改动都应围绕一个主题。例如,不要在一个提交中同时修改用户模块和订单模块。
- Reviewer 视角: 在提交前,想象一下你的 Reviewer 看到这个提交时,是否能轻松理解你做了什么、为什么这么做,以及这些改动的影响。如果一个提交需要 Reviewer 花大量时间去梳理,那它可能太大了。
- 逻辑拆分: 将一个大的功能点拆解成多个小的、独立的逻辑单元进行提交。例如:
- 解决问题2: 确保每次提交都是一个经过深思熟虑、逻辑独立的单元,极大地提高了代码审查的效率和质量,降低了 Bug 引入的风险。
- 好处:
- 高效 Review: Reviewer 可以专注于一个特定改动点,快速理解并给出反馈。
- 精确定位问题: 当出现问题时,可以通过
git blame
或git bisect
精确到引入问题的具体提交,而不是一大坨改动。 - 轻松回滚/Cherry-Pick: 如果某个功能有问题,可以轻松地回滚某个提交,或者将某个提交 Cherry-Pick 到其他分支。
- 方便单元测试: 小粒度提交意味着每个提交都可能对应一个功能点或修复,更容易编写和运行单元测试来验证其正确性。
- 个人测试保障 (重点): 在提交代码给别人之前,小粒度的提交能极大地增加你进行充分自测的可能性。 当你只改动一小部分代码并提交时,你更有能力全面地验证这部分改动的功能是否正常,是否引入了新的问题,甚至可以为这部分改动编写简单的单元测试。这确保了你在将代码提交给 Reviewer 时,已经进行了初步的验证,极大地提升了你作为开发者的"靠谱性",减少了因提交未验证代码而引发的返工和延误。
-
主线分支的提交信息原则:
- 原则:
main
和develop
分支上的提交历史应该是清晰、有意义的,能够反映项目的重要里程碑和功能演进。我们应避免将零碎的、中间过程的提交直接暴露在主线分支上。 - 解决问题3: 避免主线分支出现
merge xx into develop
这类冗余信息。 - 操作:
- 使用
git merge --no-ff
: 在将feature
、hotfix
、release
分支合并到develop
或main
时,始终使用--no-ff
(no-fast-forward) 选项。这会强制创建一个合并提交,保留分支合并的历史记录,而不是简单地将分支指针向前移动。 - 使用
git rebase
(谨慎): 在将feature
分支合并回develop
之前,可以使用git rebase develop
来将feature
分支的提交"整理"到develop
分支的最新提交之后,保持提交历史的线性。这会改写历史,因此只在本地未推送的私有分支上使用 。一旦推送到远程,切勿rebase
已共享的分支。 Squash
提交: 当一个feature
分支上有多个零碎的提交,但这些提交共同实现一个完整功能时,可以在合并到develop
之前,使用git rebase -i
将这些提交squash
(合并) 成一个或几个逻辑清晰的提交。- 合并提交消息: 合并回主线分支时,确保合并提交的消息清晰地描述了合并的内容,例如 "feat(login): complete user login module"。
- 使用
- 好处: 保持主线分支历史的整洁和可读性;方便通过主线提交历史快速了解项目功能演进;易于版本管理和发布。
- 原则:
2.4 这样做的好处
- 结构清晰: 明确的分支职责,使得项目结构一目了然。
- 并行开发: 允许多个功能并行开发,互不干扰。
- 版本控制: 有序的版本发布流程,降低发布风险。
- 故障隔离: 热修复流程能快速响应生产环境问题,且不影响正在进行的开发。
- 历史整洁: 规范合并策略,保持主线分支历史的干净和有意义。
三、实际案例:SPM 组件化开发中的多仓库改动
在 SPM (Swift Package Manager) 组件化开发中,一个业务功能的实现往往涉及多个独立的 SPM 包 (即多个 Git 仓库)。例如,实现一个"用户注册"功能,可能需要改动以下几个仓库:
NetworkingKit
(网络层): 增加注册 API 请求和响应模型。AuthCore
(认证核心): 增加用户注册的业务逻辑处理,如密码加密、Token 生成等。UserUI
(用户界面): 增加注册界面和交互逻辑。AppMain
(主应用): 集成UserUI
,并调用AuthCore
进行注册。
假设我们要实现"用户注册"功能,这是一个相对复杂的功能,我们可以将其拆解为多个独立的、有意义的提交。
3.1 提交粒度拆解与规范化提交示例
目标功能: 实现用户注册,包括邮箱和密码。
开发分支: 我们在 AppMain
仓库中从 develop
拉取 feature/user-registration
分支。同时,在 NetworkingKit
, AuthCore
, UserUI
仓库中也分别创建对应的 feature/user-registration
分支。
提交流程 (以 AppMain
中的 feature/user-registration
为主线,其他组件库同步进行):
Step 1: NetworkingKit
仓库修改 - 定义注册 API 请求/响应
- 修改内容: 在
NetworkingKit
中增加RegisterRequest
和RegisterResponse
结构体,并添加相应的Endpoint
定义。 - 在
NetworkingKit
仓库提交:-
命令行:
bash# 确保你在 NetworkingKit 仓库的 feature/user-registration 分支 git add Sources/NetworkingKit/API/Auth/RegisterEndpoint.swift git add Sources/NetworkingKit/Models/Auth/RegisterModels.swift git commit -m "feat(networking): add user registration API models and endpoint" -m "This commit defines the request and response models for user registration, along with its API endpoint definition." # 推送到远程分支 git push origin feature/user-registration
-
SourceTree:
-
在 SourceTree 中,切换到
NetworkingKit
仓库的feature/user-registration
分支。 -
在"文件状态"区域,选择
Sources/NetworkingKit/API/Auth/RegisterEndpoint.swift
和Sources/NetworkingKit/Models/Auth/RegisterModels.swift
。 -
点击"暂存选中项"或"暂存全部"。
-
在"提交信息"文本框中输入:
sqlfeat(networking): add user registration API models and endpoint This commit defines the request and response models for user registration, along with its API endpoint definition.
(通常 SourceTree 会有一个较小的文本框用于 subject,下方有较大的文本框用于 body,请分开填写)
-
点击"提交"按钮。
-
点击"推送"按钮,将分支推送到远程。 `.
注意: SourceTree 通常不会有
pre-commit
钩子直接的视觉反馈,但如果你的项目配置了 Git Hook 脚本,它会在你点击"提交"时自动运行检查,如果失败会弹窗提示。
-
-
Step 2: AuthCore
仓库修改 - 实现注册业务逻辑
- 修改内容: 在
AuthCore
中实现调用NetworkingKit
的注册 API,并处理响应,包括密码加密、Token 存储等。 - 在
AuthCore
仓库提交:-
命令行:
bash# 确保你在 AuthCore 仓库的 feature/user-registration 分支 # (可能需要先更新 NetworkingKit 依赖到最新,以便使用新定义的API) git add Sources/AuthCore/Services/AuthService.swift git add Sources/AuthCore/Models/AuthToken.swift git commit -m "feat(auth): implement user registration business logic" -m "This commit adds the core business logic for user registration, including password hashing, API call integration with NetworkingKit, and JWT token handling." git push origin feature/user-registration
-
SourceTree: 类似上述步骤,在
AuthCore
仓库中进行操作。
-
Step 3: UserUI
仓库修改 - 实现注册界面
- 修改内容: 在
UserUI
中创建注册视图控制器,包含邮箱和密码输入框,以及注册按钮,并连接AuthCore
进行注册。 - 在
UserUI
仓库提交:-
命令行:
bash# 确保你在 UserUI 仓库的 feature/user-registration 分支 # (可能需要先更新 AuthCore 依赖到最新) git add Sources/UserUI/Registration/RegistrationViewController.swift git add Sources/UserUI/Registration/RegistrationViewModel.swift git commit -m "feat(user-ui): create user registration screen" -m "This commit adds the UI components and view model for the user registration screen, integrating with AuthCore for submission." git push origin feature/user-registration
-
SourceTree: 类似上述步骤,在
UserUI
仓库中进行操作。
-
Step 4: AppMain
仓库修改 - 集成注册功能
- 修改内容: 在
AppMain
中将UserUI
的注册界面集成到应用导航流程中。 - 在
AppMain
仓库提交:-
命令行:
bash# 确保你在 AppMain 仓库的 feature/user-registration 分支 # (此时,你需要确保所有 SPM 依赖都已更新到各自 feature/user-registration 分支的最新版本) git add Sources/AppMain/AppCoordinator.swift git commit -m "feat(app): integrate user registration flow" -m "This commit integrates the new user registration UI from UserUI into the main application navigation flow." git push origin feature/user-registration
-
SourceTree: 类似上述步骤,在
AppMain
仓库中进行操作。
-
3.2 案例总结与思考
这个案例演示了在一个跨多个 SPM 包的业务功能开发中,如何将一个大的功能拆分成多个小而独立的提交。
- 独立的 Commit: 每个 Commit 都完成了各自仓库中一个独立的、可验证的逻辑单元。例如,在
NetworkingKit
中提交后,NetworkingKit
本身仍然是一个功能完整且可用的包。 - 清晰的 Commit Message: 每个 Commit 都遵循了规范,清晰地说明了
type
,scope
,subject
,并且在body
中提供了详细的描述。 - 个人测试: 在
NetworkingKit
提交后,开发者可以立即运行NetworkingKit
的单元测试或进行简单的 API 调用测试,确保新增的 API 模型和 Endpoint 是正确的。同样,在AuthCore
提交后,可以运行AuthCore
的单元测试来验证业务逻辑。这种"提交即验证"的模式,大大提升了提交代码的可靠性。 - 跨仓库协作: 虽然涉及到多个仓库,但清晰的提交粒度和规范,使得每个仓库的改动都能独立审查和验证,最终在
AppMain
仓库中完成集成。
四、线下规范操作验证方法与工具
为了帮助大家更好地实践这些规范,我提出以下线下验证方法和工具:
4.1 定义 Commit 消息检查工具 (Swift 项目适用)
虽然像 commitlint
这样的工具通常基于 JavaScript 生态,但我们可以在 Swift 项目中实现类似的 Commit 消息规范检查。
-
Git Hooks (自定义脚本): Git 提供了
pre-commit
和commit-msg
等钩子。你可以编写 Swift 脚本或 Shell 脚本,将其放置在.git/hooks/
目录下(并移除.sample
后缀,确保可执行权限),以在提交前自动检查 Commit 消息是否符合规范。-
commit-msg
Hook: 这个钩子最适合用于检查 Commit 消息本身。当执行git commit
时,Git 会将 Commit 消息的临时文件路径作为参数传递给commit-msg
脚本。你可以在脚本中读取这个文件,然后使用正则表达式或其他逻辑来验证 Commit 消息的格式。如果验证失败,脚本以非零退出码退出,Commit 就会被阻止。 -
示例
commit-msg
脚本:bash#!/bin/sh # .git/hooks/commit-msg COMMIT_MSG_FILE=$1 COMMIT_MSG=$(cat "$COMMIT_MSG_FILE") # 简单的正则检查,确保以 type(scope): subject 格式开头 if ! echo "$COMMIT_MSG" | grep -Eq "^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .{1,50}"; then echo "Error: Commit message does not follow the conventional commit format." echo "Please use format like: <type>(<scope>): <subject>" echo "Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert" exit 1 fi # 可以添加更多复杂的 Swift 脚本逻辑 # 例如: # swift run MyCommitChecker "$COMMIT_MSG_FILE"
-
-
CI/CD 阶段检查: 在你的 CI/CD 流程中增加一个步骤,专门用于检查提交的 Commit 消息。即使本地没有强制检查,CI 也能捕获不规范的提交,从而避免其合并到主线分支。这是一种更强力的保障机制。
4.2 实践演练
- 创建一个沙盒项目: 建立一个独立的 Git 仓库,作为大家的练习场。
- 模拟开发流程: 在沙盒项目中,大家可以尝试:
- 创建
feature
分支,开发并进行多次小粒度提交。 - 按照规范撰写 Commit 消息,体验 Git Hooks 或 CI/CD 检查的校验。
- 尝试合并
feature
分支到develop
分支,使用git merge --no-ff
。 - 模拟 Bug 修复,创建
hotfix
分支,合并到main
和develop
。 - 体验
git rebase -i
来squash
提交 (注意只在私有分支使用)。 - 刻意制造冲突并解决:在沙盒项目中,让两个人修改同一个文件的同一行代码,制造冲突。然后尝试解决冲突,体验命令行或 SourceTree 的冲突解决流程,并确保所有冲突标记都被正确移除。
- 创建
4.3 团队 Code Review
- 将 Commit 消息规范和提交粒度作为 Code Review 的重要内容。
- Reviewer 在审查代码的同时,也要审查 Commit 消息和提交历史。
- 通过相互监督和帮助,共同提升团队的 Git 使用水平。
五、总结与展望
规范的 Git 提交与工作流管理,并非为了增加我们的工作负担,而是为了让开发过程更加高效、透明和可控。它能帮助我们:
- 提升代码质量: 清晰的提交历史和精细的提交粒度,有助于发现和修复问题。
- 降低维护成本: 快速理解代码变更,简化代码回溯和问题排查。
- 增强团队协作: 统一的规范减少沟通障碍,提高团队整体效率。
- 促进个人成长: 养成良好的提交习惯,提升专业素养。
我鼓励大家从现在开始,在日常工作中积极实践这些规范。初期可能会有一些不适应,但随着时间的推移,你会发现这些努力是值得的。