Files
leaudit-platform-backend/docs/项目总览/团队Git协作完整规范-2026-05-07.md
T
2026-05-09 20:04:08 +08:00

853 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 团队 Git 协作完整规范
> 适用对象:个人项目、小项目、大项目
> 版本:v1.0 · 2026-05-07
---
## 一、核心原则
不论团队规模大小,所有 Git 协作都遵守以下 5 条铁律:
1. **主干(main)永远可发布**
2. **凡是会影响他人的操作,事前通知、事中可见、事后可恢复**
3. **分支属于"任务",不属于"人"**(详见第六章)
4. **推送前先同步**`git pull --rebase` 后再 `git push`
5. **重要分支禁止直接 push,必须走 PR**
---
## 二、如何选择工作流
### 决策树
```
你一个人做这个项目吗?
├─ 是 → 【方案 A】个人极简流
└─ 否 → 团队 ≥ 6 人 或 同时维护多版本?
├─ 是 → 【方案 C】Git Flow Lite(带 develop
└─ 否 → 【方案 B】GitHub Flow90% 小项目适用)
```
### 三种方案对比
| 维度 | A·个人 | B·小项目 | C·大项目 |
|------|--------|---------|---------|
| 团队规模 | 1 人 | 2-5 人 | 6+ 人 |
| 主分支 | main | main | main + develop |
| 是否需 develop | ❌ | ❌ | ✅ |
| 是否需 release 分支 | ❌ | ❌ | ✅(可选) |
| 是否需 hotfix 分支 | ❌ | ✅ | ✅ |
| 部署策略 | 手动 / tag | 持续部署 | 阶段部署 |
| 发布周期 | 不固定 | 随时 | 双周 / 月度 |
| PR Review | 自看 | ≥1 人 | ≥2 人 |
| 复杂度 | ★ | ★★ | ★★★★ |
### 升级信号
**A → B**:第二个人加入,立刻配 main 保护 + PR 流程。
**B → C**:满足以下任一即升级
- 同时维护多个版本(v1 还在用,v2 在开发)
- 有专门 QA 阶段,main 不能合并即上线
- 发布周期 ≥ 2 周
- 团队 ≥ 6 人
> ⚠️ **宁可滞后升级,也不要预防性复杂化**。多数团队过度设计,把简单事搞复杂。
---
## 三、方案 A:个人项目工作流
### 分支模型
```
main (主线 + 发布锚点)
└─ feature/* (按需,仅用于实验性大改动)
```
### 日常工作流
**小改动 → 直接上 main**
```bash
git add .
git commit -m "fix: 修复登录按钮样式"
git push
```
**大改动 / 不确定的实验 → 开分支保命**
```bash
git checkout -b feature/重构数据层
# ...开发...
# 没问题 → 合并
git checkout main
git merge --no-ff feature/重构数据层
git push
git branch -d feature/重构数据层
# 实验失败 → 直接丢弃
git checkout main
git branch -D feature/重构数据层
```
### 发布流程
```bash
git tag -a v1.2.0 -m "release: v1.2.0 - 评查详情页上线"
git push --tags
```
**版本号遵循 [SemVer](https://semver.org/)**
- `v主版本.次版本.补丁`
- 主版本:不兼容改动
- 次版本:新功能(向下兼容)
- 补丁:bug 修复
### 个人项目最低纪律
| 习惯 | 价值 |
|------|------|
| 每天 push 到远程 | 硬盘坏了不丢工 |
| commit 信息写人话 | 半年后能看懂 |
| 重大改动前打 tag | 有回滚锚点 |
| README 记录关键决策 | 时间久了不抓瞎 |
| `.gitignore` 配齐 | 不把密钥推上去 |
### 不要做的事
-`git commit -m "update"` / `"修改"` / `"123"`
- ❌ 一周不 push
- ❌ 在 main 上做不确定的实验
- ❌ 把 `.env` 推到远程
---
## 四、方案 B:小项目工作流(GitHub Flow
**适用:2-5 人,持续部署**
### 分支模型
```
main (受保护,PR 合并,自动部署到生产)
├─ feature/评查详情页
├─ fix/登录闪退
└─ hotfix/支付失败 (紧急修生产)
```
**核心思想**
- main 永远可发布
- feature 分支**生命周期短**(1-3 天最佳,最长 1 周)
- 所有改动走 PR
### 日常工作流(每个任务都按这个跑)
```bash
# 1. 开分支前先同步 main
git checkout main
git pull --rebase
# 2. 创建任务分支(任务命名,不要带人名)
git checkout -b feature/评查详情页
# 3. 开发,频繁小步提交,频繁推送
git add .
git commit -m "feat(评查): 新增详情页骨架"
git push -u origin feature/评查详情页
# 4. 推送前同步 main,避免冲突堆积
git fetch origin
git rebase origin/main
# 解决冲突 → 本地测试通过
# 5. 推送(rebase 后需要 force-with-lease
git push --force-with-lease
# 6. 提 PR → Review → 合并 → 自动部署
# 7. 合并后清理本地
git checkout main
git pull
git branch -d feature/评查详情页
```
### PR 流程规范
**提 PR 时**
- 标题:`<type>(<scope>): <简短描述>`,例:`feat(评查): 新增详情页`
- 描述模板:
```markdown
## 改动内容
- ...
## 测试方法
- ...
## 截图(如有 UI 改动)
## 关联 Issue
Closes #123
```
- 自检:lint 通过、测试通过、不含调试代码
**Review 时**
- 24 小时内响应
- 给具体可操作的反馈,不要"建议优化一下"
- Approve 前自己心里跑一遍代码逻辑
### 紧急修复(hotfix
```bash
git checkout main && git pull
git checkout -b hotfix/支付失败
# 修 → 测 → push → PR → 紧急合并 → 立即部署
```
### 仓库一次性配置(落地关键)
**main 分支保护(GitHub/GitLab 后台)**
- ✅ 禁止直接 push
- ✅ 禁止 force push
- ✅ 必须 PR + ≥1 Approve
- ✅ 必须通过 CIlint + test
- ✅ 合并后自动删除分支
---
## 五、方案 C:大项目工作流(Git Flow Lite
**适用:6+ 人 / 多版本并存 / 有 QA 测试期**
### 分支模型
```
main (生产版本,受保护,只接受来自 release/hotfix 的合并)
└─ develop (开发主线,受保护,所有 feature 合并到这里)
├─ feature/评查详情页 (个人功能分支)
├─ feature/登录优化
├─ release/v2.3.0 (准备发布的快照分支)
└─ hotfix/支付失败 (从 main 拉,修完合并回 main + develop)
```
### 各分支角色
| 分支 | 来源 | 合并去向 | 生命周期 |
|------|------|---------|---------|
| `main` | 永久 | — | 永久 |
| `develop` | 永久 | — | 永久 |
| `feature/*` | develop | develop | 任务期 |
| `release/*` | develop | main + develop | 发布周期 |
| `hotfix/*` | main | main + develop | 紧急修复 |
### develop 的角色
- **开发主线**:所有 feature 合并到 develop
- **集成测试环境**QA 在 develop 上测试
- **不直接发布**:发布前从 develop 拉 release 分支封板
### release 分支:发布前的"封板"
```bash
# 1. 从 develop 拉 release 分支(功能冻结)
git checkout develop && git pull
git checkout -b release/v2.3.0
# 2. 在 release 上只修 bug,不加新功能
# QA 测试 → 修 bug → 改版本号 → 写 changelog
# 3. 测试通过,合并到 main 并打 tag
git checkout main && git pull
git merge --no-ff release/v2.3.0
git tag -a v2.3.0 -m "release: v2.3.0"
git push --tags
# 4. 合并回 develop(带上 release 上的 bug 修复)
git checkout develop
git merge --no-ff release/v2.3.0
git push
# 5. 删除 release 分支
git branch -d release/v2.3.0
```
### hotfix 分支:紧急修生产
```bash
# 1. 从 main 拉 hotfix 分支
git checkout main && git pull
git checkout -b hotfix/支付失败
# 2. 修 → 测 → 提 PR
# 3. 合并到 main 并打 patch tag
git checkout main
git merge --no-ff hotfix/支付失败
git tag -a v2.3.1 -m "hotfix: 修复支付失败"
git push --tags
# 4. 必须合并回 develop!否则下次发布 bug 复发
git checkout develop
git merge --no-ff hotfix/支付失败
git push
# 5. 删除 hotfix 分支
git branch -d hotfix/支付失败
```
### 日常 feature 工作流
```bash
# 从 develop(不是 main!)开分支
git checkout develop && git pull --rebase
git checkout -b feature/评查详情页
# 开发 → 推送 → PR 到 develop(不是 main!)
git push -u origin feature/评查详情页
# 提 PRfeature/评查详情页 → develop
```
### 大项目仓库配置
- **main**:禁止 push、禁止 force push、只接受 release/hotfix 合并、必须 ≥2 Approve
- **develop**:禁止 push、必须 PR + ≥1 Approve、必须通过 CI
- **CI 要求**lint + 单元测试 + 集成测试 + 静态分析
---
## 六、为什么禁止使用个人分支【专题】
### 反模式(团队中常见错误)
```
❌ feature/评查详情页-zhangsan
❌ zhangsan-dev
❌ feature/zhangsan/xxx
❌ 张三的分支
❌ wy-dev / wy-test
```
### 5 大问题
#### 1️⃣ 强化"个人所有权",破坏协作精神
分支带人名 → 同事潜意识"这是张三的地盘,我别动" → 协作变独行 → 出问题没人帮看。
**好的协作是任务驱动**:分支属于任务,谁有空谁接手。
#### 2️⃣ 离职、休假、轮岗时难交接
**真实场景**
> 张三休假一周,留下 5 条 `feature/xxx-zhangsan` 分支。李四接手时是改名(影响远程)、还是新开 `feature/xxx-lisi`(造成分裂)?怎么处理都别扭。
如果分支叫 `feature/评查详情页`,李四直接 `git checkout` 接着干,**零摩擦**。
#### 3️⃣ 分支语义模糊,看名字不知道做什么
`zhangsan-dev` 这条分支在做啥?
- 评查模块?
- 登录优化?
- 性能调优?
- 还是张三的"我啥都往这塞"草稿?
**好的分支名 = 一句话任务说明**,看到 `feature/评查详情页` 立刻明白。
#### 4️⃣ 容易堆积成"杂物分支"
人名分支没有边界,张三会把"评查详情页 + 顺手优化的登录 + 试验性能改动"都塞进 `feature/zhangsan-dev`。结果:
- PR 改动巨大,没人能 Review
- 一个 bug 拖延了三个功能
- 不能单独 revert 某项改动
**任务分支天然有边界**`feature/评查详情页` 只装评查相关改动,超出范围就该开新分支。
#### 5️⃣ 多人协作同一功能时命名混乱
**真实场景**
> 评查详情页很大,张三李四王五一起做。分支会变成:
> - `feature/评查详情页-zhangsan`
> - `feature/评查详情页-lisi`
> - `feature/评查详情页-wangwu`
>
> 三人改动如何同步?合并顺序?谁先合谁后合?混乱。
**正确做法**:开一条父分支 `feature/评查详情页`,每人开**子任务分支**
- `feature/评查详情页-头部`
- `feature/评查详情页-列表`
- `feature/评查详情页-筛选`
子任务都合并回父分支,最后父分支整体合到 develop。
---
### 真实案例:人名分支造成的事故
**案例 1:交接黑洞**
> 某团队张三离职前留下 `zhangsan/refactor-payment` 分支,里面有他重构支付的工作(约 60% 完成)。半年后团队想接着做,发现:分支与 main 已差异巨大,无法 rebase;分支内 commit 信息全是"WIP",没人知道每个 commit 在做什么;最终只能放弃,**重做**。
**案例 2:紧急修复无法落地**
> 周五晚上生产支付功能挂了。值班发现修复需要改一个文件,但该文件在 `feature/支付重构-zhangsan` 分支上张三已改了一周。要么强行修生产(与张三的分支冲突会变大),要么等张三周一来。最终用了一个肮脏的 hotfix,**技术债累计**。
**案例 3PR Review 无人能审**
> `feature/zhangsan-dev` 一个 PR 改了 2000 行,包含评查、登录、列表、性能 4 个不相关的改动。Review 同事看了一上午放弃,盖章通过。两天后线上 bug,**无法定位是哪个改动引起的**。
---
### 正确做法
#### ✅ 任务命名规范
```
feature/<任务描述> feature/评查详情页
feature/<issue号>-<描述> feature/PROJ-123-评查详情页
fix/<bug简述> fix/登录闪退
hotfix/<紧急bug> hotfix/支付失败
refactor/<范围> refactor/数据层
```
#### ✅ 任务归属体现在 PR/Issue Assignee
谁负责这个任务 → 在 **PR/Issue 的 Assignee** 字段标记,不要污染分支名。
#### ✅ 分支只与「任务/目标」关联
看到 `feature/评查详情页` 应当能立刻回答:
- 这条分支在做什么
- 合并目标是什么
- 何时该删除
---
### 例外:可以带人名的场景
**仅限以下短期、可丢弃的情况**
#### 1. 个人探索/草稿分支(明确不打算合并)
```
zhangsan/试个性能方案
zhangsan/sandbox
```
用斜线作为"个人 sandbox 命名空间"前缀,与正式 feature 分支区分。
#### 2. Fork 工作流(开源项目常见)
你 fork 仓库到自己账号后,分支名随意,提 PR 时再说。这是**远程 fork 模式**,不属于"单仓库协作"场景。
#### 3. 多人并行试方案的临时区分
```
feature/评查详情页/方案A-zhangsan
feature/评查详情页/方案B-lisi
```
验证完保留赢的方案,删除全部分支,重命名为正式分支。
> **核心:这些场景都是短期、明确不污染主流分支的。**
---
## 七、通用规范
### 分支命名
```
<type>/<description>
<type>/<issue-id>-<description>
```
**type 类型**
- `feature/` 新功能
- `fix/` bug 修复
- `hotfix/` 紧急修复(生产)
- `refactor/` 重构
- `docs/` 文档
- `test/` 测试
- `chore/` 杂项(依赖升级、配置)
- `release/` 发布分支(仅 Git Flow
**示例**
```
feature/评查详情页
feature/PROJ-123-评查详情页
fix/登录闪退
hotfix/v2.3-支付失败
```
**禁止**
- ❌ 人名前缀/后缀
- ❌ 拼音缩写如 `wy-dev`
- ❌ 没有 type 前缀
- ❌ 空格、特殊字符
### 提交信息(Conventional Commits
```
<type>(<scope>): <简短描述>
<可选详细说明>
<可选 footer>
```
**type**
- `feat:` 新功能
- `fix:` 修 bug
- `refactor:` 重构(不改外部行为)
- `perf:` 性能优化
- `test:` 测试
- `docs:` 文档
- `style:` 格式(不改代码逻辑)
- `chore:` 杂项
- `build:` 构建相关
- `ci:` CI 相关
**示例**
```
feat(评查): 新增详情页骨架屏
- 添加加载占位组件
- 实现进入动画
- 适配移动端
Closes #123
```
**禁止**
- ❌ `update`、`fix`、`修改`、`123`
- ❌ 一个 commit 改 N 个不相关的事
- ❌ `WIP`(合并前应 squash 掉)
### 标签(Tag)规范
**版本标签**遵循 [SemVer](https://semver.org/)
```
v1.2.3 正式版
v1.2.3-beta.1 beta 版
v1.2.3-rc.1 发布候选版
```
**打 tag 时机**
- 个人项目:每次发布
- 小项目:每次部署到生产
- 大项目:release 合并到 main 时
### .gitignore 通用模板
```gitignore
# 依赖
node_modules/
__pycache__/
*.pyc
venv/
.venv/
# 环境变量(永远不要提交)
.env
.env.*
!.env.example
# 编辑器
.vscode/
.idea/
*.swp
.DS_Store
# 构建产物
dist/
build/
*.log
coverage/
# 本地配置
.cache/
*.local
```
---
## 八、共享资源协作守则
### 通用原则
> **凡是会影响他人的操作,事前通知、事中可见、事后可恢复。**
### 共享资源守则表
| 共享资源 | 守则 |
|---------|------|
| 共享开发/测试机 | 默认主分支;切换通知;用完还原;优先 `git worktree` |
| 共享数据库 | 不在共用库做破坏性改动;每人独立 schema;测试用 docker 起本地 |
| 共享 API Key / Secret | 用 secret manager;禁止贴 IM/代码/截图 |
| CI/CD pipeline | 改流水线前通知;不在大家忙时触发昂贵任务 |
| 第三方账号 | SSO + 审计;操作记录可追溯 |
| 部署环境 | 部署窗口公告;锁机制(谁部署谁举手) |
### 切换共享资源的标准动作
```
1. 群里通知:"我要 [操作] [资源],预计 [时间]"
2. 操作
3. 用完恢复默认状态
4. 群里告知:"已恢复"
```
### 优先用隔离手段,而不是切换
```bash
# git worktree:在不同目录跑不同分支,主目录不动
git worktree add ../preview-xxx feature/xxx
git worktree remove ../preview-xxx
# Docker:每个分支起独立容器
docker compose -p preview-xxx up
```
### 终极方案:消除"共享"本身
- 每个 PR 自动起一个独立预览环境(Vercel / Netlify / K8s preview
- 每人独立数据库 schema(自动迁移)
- 每人独立云资源(云厂商沙盒账号)
> **根本不需要"共享调试机"。**
---
## 九、常见问题 FAQ
### Q1:合并前应该用 merge 还是 rebase
| 场景 | 推荐 |
|------|------|
| 个人 feature 分支同步 main | `git rebase`(历史干净) |
| 多人协作的分支 | `git merge`(不重写他人 commit |
| 合并 PR 到 main | **Squash and merge**main 历史干净) |
| 已推送的分支 | 谨慎使用 rebase,需 `--force-with-lease` |
### Q2feature 分支多久合并一次?
- **理想**1-3 天
- **可接受**1 周
- **超过 1 周必须警惕**:分支过大 → 拆分;同步频率提高(每天 rebase main
### Q3commit 太多太乱怎么办?
合并到主分支前 squash 整理:
```bash
git rebase -i HEAD~5
# 把第二个开始的 pick 改成 squash 或 fixup
```
或在 PR 合并时选择 **"Squash and merge"**。
### Q4:误推了敏感信息(密钥)怎么办?
1. **立即作废**这个密钥(去后台重新生成)
2. 用 `git filter-repo` 或 BFG 清除历史
3. **强制推送**所有分支
4. **通知所有人**重新 clone
预防:
- `.gitignore` 配齐 `.env`
- 用 secret manager
- 装 `git-secrets` / `gitleaks` pre-commit 钩子
### Q5rebase 时遇到冲突怎么办?
```bash
# 1. 解决冲突文件
# 2. git add <冲突文件>
# 3. git rebase --continue
# 实在搞不定就放弃
git rebase --abort
```
### Q6:误删了分支怎么恢复?
```bash
# 找到最后一次 commit
git reflog
# 恢复
git checkout -b feature/xxx <commit-sha>
```
### Q7`git push` 被拒(rejected
意味着远程分支有新 commit 你本地没有。
```bash
# 同步后再推
git pull --rebase
git push
# 如果是 rebase 后推自己的分支
git push --force-with-lease # 注意是 --force-with-lease,不是 --force
```
> ⚠️ **永远不要对 main / develop 用 `--force`**
### Q8:什么时候删分支?
- 合并到主干后**立即删除**远程和本地
- GitHub/GitLab 可设置"合并后自动删除"
- 本地:`git branch -d feature/xxx`
### Q9:长期废弃的分支能直接删吗?
先 `git log feature/xxx` 看是否有未合并 commit。若有:
- 询问作者是否还需要
- 或导出 patch`git format-patch main..feature/xxx`
### Q10Reviewer 检查清单
审视 PR 是否值得合并:
- [ ] 分支命名规范,无人名
- [ ] 改动只做一件事,与 PR 标题一致
- [ ] 提交信息规范
- [ ] 测试覆盖关键路径
- [ ] CI 全绿
- [ ] 无调试代码(console.log、注释掉的代码)
- [ ] 无敏感信息(API Key、密码)
- [ ] 文档/注释 同步更新
- [ ] 跑过本地测试
---
## 十、配套工具与一键配置
### 全员 Git 配置(每台机器跑一次)
```bash
# pull 默认 rebase(避免无意义 merge commit
git config --global pull.rebase true
# push 自动追踪上游
git config --global push.autoSetupRemote true
# rebase 自动 stash
git config --global rebase.autoStash true
# 默认分支名
git config --global init.defaultBranch main
# 换行符处理
git config --global core.autocrlf input # Mac/Linux
git config --global core.autocrlf true # Windows
```
### 推荐工具
| 类型 | 工具 | 作用 |
|------|------|------|
| 提交检查 | husky + lint-staged | commit 前自动跑 lint/format |
| 提交规范 | commitlint | 强制 Conventional Commits |
| 密钥扫描 | gitleaks / git-secrets | 防止推送密钥 |
| 图形化客户端 | Fork / SourceTree / GitKraken | 新人友好 |
| CLI 增强 | gh / lazygit | 命令行高效操作 |
| CI/CD | GitHub Actions / GitLab CI | 自动测试和部署 |
| PR 预览 | Vercel / Netlify | 每 PR 一个独立环境 |
### 一键安装 husky + lint-staged + commitlintNode 项目)
```bash
bun add -D husky lint-staged @commitlint/cli @commitlint/config-conventional
# 初始化 husky
bunx husky init
# 配置 commitlint
echo "export default { extends: ['@commitlint/config-conventional'] }" > commitlint.config.mjs
# 添加 hooks
echo "bunx commitlint --edit \$1" > .husky/commit-msg
echo "bunx lint-staged" > .husky/pre-commit
```
`package.json` 添加:
```json
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"]
}
}
```
---
## 附录:决策速查表
### 我该开什么类型的分支?
| 我要做的事 | 分支前缀 | 例子 |
|----------|---------|------|
| 新功能 | `feature/` | `feature/评查详情页` |
| 修 bug | `fix/` | `fix/登录闪退` |
| 紧急修生产 | `hotfix/` | `hotfix/支付失败` |
| 重构 | `refactor/` | `refactor/数据层` |
| 写文档 | `docs/` | `docs/api文档` |
| 升级依赖 | `chore/` | `chore/升级react18` |
### 我该写什么 commit type
| 我做了什么 | type |
|----------|------|
| 写了新功能 | `feat` |
| 修 bug | `fix` |
| 重构(不改行为) | `refactor` |
| 性能优化 | `perf` |
| 改文档 | `docs` |
| 改格式(空格、缩进) | `style` |
| 加测试 | `test` |
| 杂项 | `chore` |
### 我该用哪个工作流?
```
1 人 → 方案 Amain + 偶尔 feature
2-5 人 → 方案 BGitHub Flowmain + feature + PR
6+ 人 / 多版本 → 方案 CGit Flow Litemain + develop + ...
```
---
## 附录:一页纸速查(打印贴墙)
```
┌─────────────────────────────────────────────────────────┐
│ 团队 Git 协作 · 一页纸速查 │
├─────────────────────────────────────────────────────────┤
│ 1. 永远从最新主干开任务分支 │
│ git checkout main && git pull --rebase │
│ git checkout -b feature/任务描述 │
│ │
│ 2. 分支命名 = 任务,不带人名! │
│ ✅ feature/评查详情页 │
│ ❌ feature/评查详情页-zhangsan │
│ │
│ 3. commit 写人话 │
│ ✅ feat(评查): 新增详情页骨架 │
│ ❌ update │
│ │
│ 4. 推送前先同步 │
│ git fetch && git rebase origin/main │
│ │
│ 5. 主分支 = PR 入口,禁止直推 │
│ │
│ 6. 共享资源 = 通知 → 操作 → 还原 → 告知 │
│ │
│ 7. 永远不对 main/develop --force │
│ │
│ 8. 合并后立刻删分支 │
└─────────────────────────────────────────────────────────┘
```
---